import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {count: 0},
mutations: {add(state, payload) {state.count += payload}
},
actions: {delayAdd(context, payload) {setTimeout(() => {context.commit("add", payload)
}, 1000)
}
},
getters: {doubleCount(state) {return state.count * 2}
}
})
new Vue({render: h => h(App),
store
}).$mount('#app')
this.$store.state.count
this.$store.getters.doubleCount
this.$store.commit('add', 1)
this.$store.dispatch('')
- 实现 install 办法
- 在 Vue 根实例化时,给 Vue 根实例挂载 store 对象
- store.state 和 getter 都是数据响应式,并且不能间接批改的 state 和 getter 的值,只能通过 commit 和 dispatch
- 实现 commit,同步批改 state 的值
- 实现 dispatch,反对异步批改 state 的值
let Vue
class Store {constructor(options) {
this._vm = null
this._mutations = options.mutations
this._actions = options.actions
this._wrappedGetters = options.getters
this.getters = {}
const storeComputed = {}
const self_store = this
Object.keys(this._wrappedGetters).forEach((key) => {
// 遍历取出 getters 的每个办法
const gettersFn = self_store._wrappedGetters[key]
// 转换为 computed,即返回一个无参数的高阶函数
storeComputed[key] = () => {return gettersFn(self_store.state)
}
// 定义 getter 为 只读属性, 并且可枚举
Object.defineProperty(self_store.getters, key, {get: () => self_store._vm[key],
enumerable: true
})
})
// new 一个新的 Vue 实例_vm,并在_vm 做数据响应式解决
this._vm = new Vue({
data: {
$$state: options.state // 加 $$ 示意不代理到 Vue 实例属性上
// Vue 初始化在对数据做响应式解决时,还会将数据代理到实例
},
computed: {...storeComputed}
})
console.log(this._vm)
}
get state () {return this._vm._data.$$state}
set state(v) {throw new Error(`use store.replaceState() to explicit replace store state.`)
}
// 这里须要应用箭头函数 避免 this 扭转
// 或者应用 this.commit = this.commit.bind(this) 绑定 this
commit = (type, payload) => {
// 依据 type 从 mutations 外面找对应的办法
const fn = this._mutations[type]
if (!fn) return
fn.call(this, this.state, payload)
}
dispatch = (type, payload) => {
// 依据 type 从 actions 外面找对应的办法
const fn = this._actions[type]
if (!fn) return
fn.call(this, {commit: this.commit, state: this.state}, payload)
}
}
function install(vue) {
/*
1、判断是否装置过插件
2、保留 vue 构造函数
3、通过混入的形式,在根实例创立时挂载 store 实例
*/
// 1、判断是否装置过插件
if (Store.installed) {return}
Store.installed = true
//2、保留 vue 构造函数
Vue = vue
// 通过混入的形式,在根实例创立时挂载 store 实例
Vue.mixin({beforeCreate() {if (this.$options.store) {Vue.prototype.$store = this.$options.store}
},
})
}
export default {Store, install}
- 执行 install 办法时,通过 Vue.mixin 混入的形式,在 Vue 实例化时,在 beforeCreate 生命周期里,给 Vue 原型挂载 store 对象 Vue.prototype.$store = store
- Store 构造函数里保留传入的配置选项,便于获取 state、mutations、actions、getters 等
- store.state 实现:通过 new 一个新的 Vue 实例_vm, 把 state 存于_vm.data,state 就变为响应式数据。重写 state 的 set 和 get,避免间接批改 state 的值,只能通过 commit 和 dispatch 批改 state 的值,get 则从_vm.data 获取
- commit 办法实现:commit 须要绑定 this 为 store 实例,避免 dispatch 调用是 this 指向扭转。通过传入的参数 type,从 mutations 里取出函数执行,并传入 state 参数
- dispatch 办法实现:与 commit 办法相似,通过传入的参数 type,从 actions 里取出函数执行,并将 commit 办法和 state 存于 context 作为参数传入
- getters 实现:遍历 getters 的属性,都转换为_vm 的 computed 外面,即返回无参的高阶函数,并应用 Object.defineProperty 为每个 key 从新定义 get 办法,无需定义 set, 避免间接批改 getter 的值