持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第4天,点击查看活动详情
前言
前端以前是没有状态管理的,直到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 time
const state = {
count: 0
}
const Counter = {
// Convert state into reactive object
data () {
return state
},
render (h) {
// Proxy the object
return 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实例中返回属性具有响应式的特点
- 待补充