前言
需要对原先分享过的有个初步了解
- vue数据响应式的原理 Object.defineProperty
- 非父子组件之间的通讯 bus
Vuex的实现实际上就是相当于上述两者之间的结合。
Vuex的特点
Vuex在全局拥有一个State存放数据,通过Mutation和Action更改数据,这实际上是Vuex内部实现了一个bus。
Vuex根据vue数据响应式的原理,实现使内部vue-bus的数据监听。
安装
使用过Vuex的朋友一定知道,Vuex的安装十分简单,只需要提供一个store,然后执行下面两句代码即完成的Vuex的引入。
Vue.use(Vuex);
new Vue({
el: '#app',
store
});
其中Vuex是vue的插件,具体如何开发一个vue插件,请看vue的官方文档 链接。
Vuex通过全局 mixin 方法添加一些组件选项,在Vue的beforeCreacte钩子中将Vuex的Store实例挂载到组件的$store属性上。
这样可以在每一个组件中通过this.$store访问全局的Store实例了
var Vuex = {}
Vuex.install = function (Vue, options) {
Vue.mixin({
beforeCreate: function () {
// 老一点的版本没有$root
if(this.$root.$options.store){
this.$store = this.$root.$options.store
return
}
// 下面是源码
/*
const options = this.$options
if (options.store) {
this.$store = options.store
} else if (options.parent && options.parent.$store) {
this.$store = options.parent.$store
}
*/
})
}
Vue.use(Vuex)
const store = new Vue.Store({
state: {
count: 0
}
})
window.vm = new Vue({
el: '#app',
store
});
// vm.$store === store true
Store
在知道了Store是如何安装的之后,就要看下Store中的大致原理
因为vue本身是数据响应式的,所以才会有通过new Vue创建一个vue-bus来实现组件之间的通信。
同理,Vuex是一个专门为Vue.js框架设计的、用于对Vue.js应用程序进行状态管理的库,Vuex在Store内部实现了一个vue-bus,就可以满足一个状态管理的功能。
在Store内部实现vue-bus之后,还有些细节需要完善
每一个组件中都要能够访问到Store实例,这才才可以在每个组件中都管理状态。通过上面的安装原理就已经实现了。
实现对bus的数据监听
下面有一个比较有趣的例子
var globalData = {
d: 'hello world'
};
new Vue({
data () {
return {
$$state: {
globalData
}
}
}
});
Vue.prototype.globalData = globalData;
// 在任意模板中<div>{{globalData.d}}</div> globalData.d进行修改,我们发现模板中的globalData.d发生了变化
模板中{{globalData.d}}实际是this.globalData.d
这时候再来看下源码
/* 通过vm重设store,新建Vue对象使用Vue内部的响应式实现注册state以及computed */
function resetStoreVM (store, state, hot) {
/* 存放之前的vm对象 */
const oldVm = store._vm
// bind store public getters
store.getters = {}
const wrappedGetters = store._wrappedGetters
const computed = {}
/* 通过Object.defineProperty为每一个getter方法设置get方法,比如获取this.$store.getters.test的时候获取的是store._vm.test,也就是Vue对象的computed属性 */
forEachValue(wrappedGetters, (fn, key) => {
// use computed to leverage its lazy-caching mechanism
computed[key] = () => fn(store)
Object.defineProperty(store.getters, key, {
get: () => store._vm[key],
enumerable: true // for local getters
})
})
// use a Vue instance to store the state tree
// suppress warnings just in case the user has added
// some funky global mixins
const silent = Vue.config.silent
/* Vue.config.silent暂时设置为true的目的是在new一个Vue实例的过程中不会报出一切警告 */
Vue.config.silent = true
/* 这里new了一个Vue对象,运用Vue内部的响应式实现注册state以及computed*/
store._vm = new Vue({
data: {
$$state: state
},
computed
})
Vue.config.silent = silent
// enable strict mode for new vm
/* 使能严格模式,保证修改store只能通过mutation */
if (store.strict) {
enableStrictMode(store)
}
if (oldVm) {
/* 解除旧vm的state的引用,以及销毁旧的Vue对象 */
if (hot) {
// dispatch changes in all subscribed watchers
// to force getter re-evaluation for hot reloading.
store._withCommit(() => {
oldVm._data.$$state = null
})
}
Vue.nextTick(() => oldVm.$destroy())
}
}
通过源码可以看到 在store._vm上创建了一个实例
vm.$store.state === vm.$store._vm.$data.$$state // true
// 简化下设计思想
const sore = new Vuex.Store()
// Vuex.Store 构造函数
class Store {
constructor (options = {}) {
const {state} = options
resetStoreVM(this, state)
}
get state () {
return this._vm._data.$$state
}
}
// resetStoreVM
function resetStoreVM (store, state, hot) {
store._vm = new Vue({
data: {
$$state: state
}
})
}
// new Store
const store = new Store({
state: {}
})
// store._vm
// store.state 返回 store._vm._data.$$state
// Vue.use
Vue.mixin({
beforeCreate: function () {
this.$store = store
}
})
// 使用
this.$store.state...
{{$store.state}}