前言
前端以前是没有状态管理的,直到Fackbook提出一个叫Flux的概念,才有了状态管理。
以前前端是通过MVC模式管理代码,但后来我们使用例如Vue、React、Angular这类通过声明式开发的框架,发现状态很难管理,容易出现状态被任意修改。当应用越来越大,这种不确定性会导致系统不稳定,而且定位bug也变得困难。
单向数据流
Flux,VueX都是一种关于单向数据流的思想开发的状态管理模式,这个模式下包含以下几个部分:
- 状态,驱动应用的数据源;
- 视图,以声明方式将状态映射到视图;
- 操作,响应在视图上的用户输入导致的状态变化。
以下是一个表示“单向数据流”理念的简单示意:
在这个单向数据流模型中,使用一个上传数据流和一个下传数据流进行双向数据通信,两个数据流之间相互独立。单向数据流指只能从一个方向来修改状态,比如当State发生改变时,会推动View显示改变;而View改变时,会推动事件Action从而使得State也得到更新。
VueX
VueX是一个基于单向数据流、专门为Vue.js设计的一个全局状态管理库,如下图模型一样每个部分的职能明确,
- State:负责存放数据,供Vue组件获取。
- Mutations:负责生成状态快照,修改State中的属性。
- Action:负责异步事件,处理完毕后递交给Mutations生成状态快照。
实现一个VueX
实现一个案例:显示一个按钮以及一个数字,记录并显示点击按钮的次数。
对象共享
<script src="../node_modules/vue/dist/vue.js"></script><div id="app"><counter></counter><counter></counter><counter></counter><button @click="inc">increment</button>
</div><script>
// create a counter component (that doesn't take any props)
// all instances of it should share the same count state
// and a button that increments all counters at the same timeconst state = {count: 0
}const Counter = {// Convert state into reactive objectdata () {return state},render (h) {// Proxy the objectreturn h('div', this.count)}
}new Vue({el: '#app',components: {Counter},methods: {inc () {state.count++}}
})
</script>
如上代码中,
- 我们创建了一个State对象,在子组件Counter以及跟组件app中都引入并使用了它。
- 我们在子组件中通过data函数返回这个State,使得Vue会调用观察对象转换它使它得到响应性能力。
- 我们在子组件中都返回的是一个对象,State。这虽然有驳子组件中data应用函数返回一个新的对象的理论,但在这里我们需要的是公用一个对象。
实例共享
可以看出来以上我们通过共用同一个对象达到了VueX部分的效果,但这并不是VueX实现的方式,接下来我们将通过实例分享的方式完成这部分同样的功能。
<script src="../node_modules/vue/dist/vue.js"></script><div id="app"><counter></counter><counter></counter><counter></counter><button @click="inc">increment</button>
</div><script>
// copy and modify the first exercise to use a Vue instance as
// a shared store instead.
const state = new Vue({data: {count: 0},methods: {inc () {this.count++}}
})const Counter = {render: h => h('div', state.count)
}new Vue({el: '#app',components: {Counter},methods: {inc () {state.inc()}}
})
</script>
如上代码中,
- 我们通过创建一个实例化Vue对象并将他赋名为State。
- 这个State由于是一个Vue实例化对象,他所具备的data里的属性也都具备了响应式功能。
- 这个State具有一个方法,来改变State中的值,这就类似于我们的Mutations。
- 同样在子组件和父组件中都引用了这个State对象,并运用了其中的属性。
通过实例共享,我们将改变State中属性的方法也写在了State对象中,这使得它看上去更接近VueX了。
最终实现
<script src="../node_modules/vue/dist/vue.js"></script><div id="app"><counter></counter><counter></counter><counter></counter><button @click="inc">increment</button>
</div><script>
function createStore ({ state, mutations }) {return new Vue({data: {state},methods: {commit (mutation) {if (!mutations.hasOwnProperty(mutation)) {throw new Error('Unknown mutation')}mutations[mutation](state)}}})
}const store = createStore({state: { count: 0 },mutations: {inc (state) {state.count++}}
})const Counter = {render (h) {return h('div', store.state.count)}
}new Vue({el: '#app',components: { Counter },methods: {inc () {store.commit('inc')}}
})
</script>
如上代码中,
- 我们创建了一个类似VueX中的createStore 方法
- 接受一个对象,属性为我们所需用的State和Mutations
- 返回值为一个Vue对象实例,提供了state和commit俩个属性供使用
- 我们调用了这个createStore方法,并创建了我们所需的State以及Mutations
- 引入各组件完成了状态管理。
总结
- VueX的本质是运用了Vue实例中返回属性具有响应式的特点
- 待补充