背景
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。Vuex 是专门为 Vue.js 设计的状态管理库,以利用 Vue.js 的细粒度数据响应机制来进行高效的状态更新。如果你已经灵活运用,但是依然好奇它底层实现逻辑,不妨一探究竟。
Vue 组件开发
我们知道开发 Vue 插件,安装的时候需要执行 Vue.use(Vuex)
import Vue from 'vue'
import Vuex from '../vuex'
Vue.use(Vuex)
通过查看 Vue API Vue-use 开发文档,我们知道安装 Vue.js 插件。如果插件是一个对象,必须提供
install
方法。如果插件是一个函数,它会被作为 install 方法。install 方法调用时,会将 Vue 作为参数传入。该方法需要在调用new Vue()
之前被调用。当 install 方法被同一个插件多次调用,插件将只会被安装一次。
为了更好了的去理解源码意思,这里写了一个简单的测试实例。
测试实例代码
import Vue from 'vue'
import Vuex from '../vuex'
Vue.use(Vuex)
export default new Vuex.Store({
plugins: [],
state: {
time: 1,
userInfo: {
avatar: '',
account_name: '',
name: ''
},
},
getters: {
getTime (state) {
console.log('1212',state)
return state.time
}
},
mutations: {
updateTime(state, payload){
state.time = payload
}
},
actions: {
operateGrou({ commit }) {
// commit('updateTime', 100)
return Promise.resolve().then(()=>{
return {
rows: [1,2,3]
}
})
}
},
modules: {
report: {
namespaced: true,
state: {
title: '',
},
getters: {
getTitle (state) {
return state.title
}
},
mutations: {
updateTitle(state, payload){
state.title = payload
}
},
actions: {
operateGrou({ commit }) {
commit('updateTitle', 100)
return Promise.resolve().then(()=>{
return {
rows: [1,2,2,3]
}
})
}
},
modules: {
reportChild: {
namespaced: true,
state: {
titleChild: '',
},
mutations: {
updateTitle(state, payload){
state.title = payload
}
},
actions: {
operateGrou({ commit }) {
commit('updateTitle', 100)
return Promise.resolve().then(()=>{
return {
rows: [1,2,2,3]
}
})
}
},
}
}
},
part: {
namespaced: true,
state: {
title: '',
},
mutations: {
updateTitle(state, payload){
state.title = payload
},
updateTitle1(state, payload){
state.title = payload
}
},
actions: {
operateGrou({ commit }) {
commit('updateTitle', 100)
return Promise.resolve().then(()=>{
return {
rows: [1,2,2,3]
}
})
}
},
modules: {
partChild: {
namespaced: true,
state: {
titleChild: '',
},
getters: {
getTitleChild (state) {
return state.titleChild
}
},
mutations: {
updateTitle(state, payload){
state.titleChild = payload
}
},
actions: {
operateGrou({ commit }) {
commit('updateTitle', 1000)
return Promise.resolve().then(()=>{
return {
rows: [1,2,2,3]
}
})
}
},
modules: {
partChildChild: {
namespaced: true,
state: {
titleChild: '',
},
getters: {
getTitleChild (state) {
return state.titleChild
}
},
mutations: {
updateTitle(state, payload){
state.titleChild = payload
}
},
actions: {
operateGrou({ commit }) {
commit('updateTitle', 1000)
return Promise.resolve().then(()=>{
return {
rows: [1,2,2,3]
}
})
}
},
}
}
}
}
}
}
})
Graphviz 父子结点关系图
用 Graphviz 图来表示一下父子节点的关系,方便理解
组件开发第一步 install & mixin
在调用 Vuex 的时候会找其 install 方法,并把组件实例传递到 install 方法的参数中。
let Vue;
class Store {
}
const install = _Vue => {
Vue = _Vue;
Vue.mixin({
beforeCreate(){
console.log(this.$options.name);
}
})
};
export default {
Store,
install
}
到这里说一下 Vuex 实现的思想,在 Vuex 的 install 方法中,可以获取到 Vue 实例。
我们在每个 Vue 实例上添加 $store 属性,可以让每个属性访问到 Vuex 数据信息;
我们在每个 Vue 实例的 data 属性上添加上 state,这样 state 就是响应式的;
收集我们传入 new Vuex.Store(options) 即 options 中所有的 mutaions、actions、getters;
接着当我们 dispatch 的时候去匹配到 Store 类中存放的 actions 方法,然后去执行;
当我们 commit 的时候去匹配到 Store 类中存放的 mutations 方法,然后去执行;
这其实就是一个发布订阅模式,先存起来,后边用到再取再执行。好了解这些,我们开始真正的源码分析;参考vue实战视频讲解:进入学习
Vue 实例注入 $store
为了更好理解,我们打印出 Vue 实例,可以看到注入了 $store,见下图。
具体实现关键点
const install = (_Vue) => {
Vue = _Vue
Vue.mixin({
beforeCreate(){
// 我们可以看下面 main.js 默认只有我们的根实例上有 store,故 this.$options.store 有值是根结点
if(this.$options.store) {
this.$store = this.$options.store // 根结点赋值
} else {
this.$store = this.$parent && this.$parent.$store // 每个实例都会有父亲。故一层层给实例赋值
}
}
})
}
- main.js
import Vue from 'vue'
import App from './App.vue'
import store from './store'
Vue.config.productionTip = false
new Vue({
store,
render: h => h(App)
}).$mount('#app')
$store.state 响应式
响应式核心就是挂载到实例 data 上,让 Vue 内部运用 Object.defineProperty 实现响应式。
class Store{
constructor (options) {
// 我们知道 options 是用户传入 new Vuex.Store(options) 参数
this.vm = new Vue({
data: {
state: options.state
}
})
}
}
$store.mutations & commit
来看下 用户传入的 mutations 变成了什么,数据采用 最上面的测试实例代码。
我们可以看到 mutations 是一个对象,里面放了函数名,值是数组,将相同函数名对应的函数存放到数组中。
mutations
- 实际上就是收集用户传入的 mutations, 放到一个对象中。
const setMoutations = (data, path = []) => {
const mutations = data.mutations
Object.keys(mutations).map(item => {
this.mutations[item] = this.mutations[item] || [] // 之前的旧值
this.mutations[item].push(mutations[item]) // 存起来
})
const otherModules = data.modules || {
} // 有子 modules 则递归
if (Object.keys(otherModules).length > 0){
Object.keys(otherModules).map