1.从Vuex的用法去分析
vuex的基本使用:
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export default new Vuex.Store({
state: {},
getters: {},
mutations: {},
actions: {},
},
});
观察基本使用得出结论:
- Vuex是一个插件,因为它是通过Vue.use来加载。所以Vuex或者Vuex.install是一个function。
- Vuex.Store是一个构造函数,可以被实例化。
- 由于Vuex有Store这个属性,所以Vuex不是一个function,那么它的install属性就是一个function。
Vuex的结构:
const Vuex = {
install,
Store
}
2.实现Vuex.install
const install = function(Vue) {
Vue.mixin({
beforeCreate() {
if(this.$options && this.$options.store) { // 判断是否是根组件
this.$store = this.$options.store
} else { // 子组件
this.$store = this.$parent && this.$parent.$store
}
}
})
}
思路:使用Vue.mixin来进行全局混入,在每一个组件上挂载$store这个属性。
问:为什么要在beforeCreate这个生命钩子进行挂载?
答:因为在created这个生命钩子的所有的数据都初始化完成了,是最早可以使用data的。所以我们要在created之前挂载数据,在beforeCreate是最好的。
问:为什么要区别根组件和子组件?
答:因为在根组件上挂载了store,只能通过this.$option.store去获取实例化的Vuex.Store。获取到之后挂载到根组件的this.$store上。而子组件想要挂载自己的this.$store,只能通过this.$parent.$store的方式获取,这样一层层的传store下来,让每一个组件都可以访问到$store。
4.实现Vuex.Store
Vuex.Store接收一个对象参选,有state、getters、mutations、actions这几个常用的属性。
import Vue from 'vue'
class Store{
constructor(options) {
// 使用Vue的data来使state具备响应式
this.vm = new Vue({
data: {
state: options.state
}
})
// 可以把下面的get state()写成这种形式
// this.state = this.vm.data
// 使用Object.defineProperty来实现getters
this.getters = {}
Object.keys(options.getters).forEach(key =>{
Object.defineProperty(this.getters, key, {
get:() => {
return options.getters[key](this.state)
}
})
})
//实现actions
this.actions = {}
Object.keys(options.actions).forEach(key => {
this.actions[key] = (args) => {
// action的第一个参数:具备commit和state属性,我们直接把this作为第一个参数
options.actions[key](this,args)
}
})
// 实现mutations
this.mutations = {}
Object.keys(options.mutations).forEach(key => {
this.mutations[key] = (args) =>{
// options的第一个参数使state
options.mutations[key](this.state, args)
}
})
}
get state() {
return this.vm.state
}
// dispatch方法
dispatch = (actionsName, args) =>{
this.actions[actionsName](args)
}
// commit方法
commit = (mutationName, args) => {
this.mutations[mutationName](args)
}
}
问:为什么将dispatch和commit方法写成箭头函数?
答:因为commit方法是在action里面调用的,会导致它的this指向有问题,所以将它写成箭头函数。dispatch也写成箭头函数,是为了方便记忆,就全部都写成箭头函数。
VueX的完整源码:
import Vue from 'vue'
class Store{
constructor(options) {
// 使用Vue的data来使state具备响应式
this.vm = new Vue({
data: {
state: options.state
}
})
// 可以把下面的get state()写成这种形式
// this.state = this.vm.data
// 使用Object.defineProperty来实现getters
this.getters = {}
Object.keys(options.getters).forEach(key =>{
Object.defineProperty(this.getters, key, {
get: ()=> {
return options.getters[key](this.state)
}
})
})
//实现actions
this.actions = {}
Object.keys(options.actions).forEach(key => {
this.actions[key] = (args) => {
// action的第一个参数:具备commit和state属性,我们直接把this作为第一个参数
options.actions[key](this,args)
}
})
// 实现mutations
this.mutations = {}
Object.keys(options.mutations).forEach(key => {
this.mutations[key] = (args) =>{
// options的第一个参数使state
options.mutations[key](this.state, args)
}
})
}
get state() {
return this.vm.state
}
// dispatch方法
dispatch = (actionsName, args) =>{
this.actions[actionsName](args)
}
// commit方法
commit = (mutationName, args) => {
this.mutations[mutationName](args)
}
}
const install = function(Vue) {
Vue.mixin({
beforeCreate() {
if(this.$options && this.$options.store) { // 判断是否是根组件
this.$store = this.$options.store
} else { // 子组件
this.$store = this.$parent && this.$parent.$store
}
}
})
}
const Vuex = {
install,
Store
}
export default Vuex