使用 Vue Observable 作为状态存储
原文链接:https://austincooper.dev/2019/08/09/vue-observable-state-store/
Vue.js 2.6.0 版本新增了 ` Vue.Observablereactive()` 函数。该函数返回给定对象的响应式实例。在 Vue 中,对象并非默认具有响应式特性。这意味着,如果我们想在对象属性发生变化时做出响应,则需要进行一些额外的工作。`reactive()` 函数让这一切变得非常简单。点击此处Vue.Observable了解更多关于响应式的信息。
非反应性
const obj = {x: 0, y: 0};
export default { // Vue component
data() { return {}; },
method() {
updateObj() {
obj.x = 1;
obj.y = 2;
}
}
}
在这个例子中,调用updateObj不会触发计算值的重新计算,也不会重新渲染视图。幸运的是,Vue 组件提供了该data函数。该函数返回的对象data 是响应式的!
通过 data() 实现响应式
export default { // Vue component
data() {
return {
obj: {x: 0, y: 0}
};
},
method() {
updateObj() {
this.obj.x = 1;
this.obj.y = 2;
}
}
}
由于结果data是响应式的,调用updateObj将导致依赖于的计算值obj重新计算,并在需要时更新视图。
从组件中突破状态
在基础组件/应用程序中,所有可变数据都存储在函数返回的对象中data。将应用程序的所有数据存储在data每个组件的函数中很快就会出现问题。尤其是在同级组件之间需要传递数据时,这个问题会更加突出。
包含数据的组件是否通过事件将数据传递给父组件,然后父组件再通过 props 将数据传递给兄弟组件?即使是简单的场景,这种做法也明显是一种代码异味,会严重影响开发体验。它会导致组件耦合、难以测试、容易出现 bug、难以维护,而且令人困惑。
这时,国营商店就派上用场了。
国家管理
Vuex 是 Vue.js 的首选状态存储插件。以下是vuejs.org对 Vuex 的描述:
Vuex 是一个用于 Vue.js 应用程序的状态管理模式和库。它为
应用程序中的所有组件提供一个集中式的状态存储,并通过规则确保状态只能
以可预测的方式进行修改。
这很棒,但 Vuex 的使用并非易事。首先,它需要作为插件添加到你的 Vue 应用中。其次,它功能非常强大,因此入门可能比较困难。最后,许多应用足够简单,并不需要 Vuex 及其所有状态管理功能。
那么,Vuex 的替代方案是什么呢?当然,答案正是本文的主题Vue.Observable:
Vue.Observable 作为状态存储
最后,所有背景知识都已介绍完毕,下面介绍如何将其用作Vue.Observable状态存储。
store.js
import Vue from 'vue';
import axios from 'axios';
const state = Vue.Observable({ // this is the magic
radius: 0,
color: 'red'
});
export const getters {
radius: () => state.radius,
color: () => state.color
}
export const mutations {
setRadius: (val) => state.radius = val,
setColor: (val) => state.color = val
}
export const actions {
fetchRadiusFromApi() {
return axios
.get('http://localhost:5001/api/radius')
.then((res) => {
mutations.setRadius(res.data);
});
},
fetchColorFromApi() {
return axios
.get('http://localhost:5001/api/color')
.then((res) => {
mutations.setColor(res.data);
});
}
}
第 4 行,也就是我们声明的state地方,是关键所在。Getter 和 mutations 用于读取和更新状态。Actions 用于异步调用,也就是 API 请求。Actions 会提交 mutations,这些 mutations 可能基于 API 请求的结果。
component.vue
<template>
<div>
<div>Radius: {{ radius }}</div>
<div>Color: {{ color }}</div>
<button @:click="setRadius(0)">Reset radius</button>
<button @:click="fetchColorFromApi">Fetch color</button>
</div>
</template>
<script>
import { getters, mutations, actions } from 'store.js';
export default {
data() { return {}; },
computed() {
...getters // radius(), color()
},
created() {
this.fetchRadiusFromApi(); // fetching data right away
this.fetchColorFromApi().then(() => {
console.log('You can chain then after actions, if you return the request');
});
}
methods() {
...mutations, // setRadius(val), setColor(val)
...actions // fetchRadiusFromApi(), fetchColorFromApi()
}
}
</script>
总结
就是这样!任何组件都可以直接导入store.js并共享同一个状态。无需使用 props/events 来传递数据。
对于不需要所有 getter 方法或只需要计算值的组件,这里有一个额外提示:
component.js
computed() {
// ...getters <- instead of this, do this:
radius() {
return getters.radius;
},
diameter() {
return getters.radius * 2;
}
// this component doesn't need color
}