vuex源码解析
一、vuex的使用回顾
//store/index.js
import Vuex from "vuex"
import Vue from "vue"
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
count: 0
}
getters: {
getCount: (state) => {return state.count}
},
mutations: {
changeCount(state, payload) {
state.count = payload
},
},
actions: {
addCount({commit},payload) {
commit("changeCount",payload)
}
}
})
export default store
//main.js
import store from "./store"
new Vue({
el: "#app",
store
})
二、逐句剖析vuex的使用
1. vue.use( )
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
解析: 上文中的 Vue.use(Vuex)会自动调用Vuex这个对象的install方法, 并在install中传入 Vue 实例,来保证内外的Vue实例一致。你可以把Vue.use方法理解为:Vue.use = ( object ) => { object.install( this )}
思考:故我们重构的 vuex 需要对外暴露一个install 方法
2. new Vuex.Store( )
const store = new Vuex.Store({
state: {...},
getters: {...},
mutations: {...},
actions: {...}
})
解析: 从new Vuex.Store()可以看出导入的Vuex包含着一个名为 Store的构造函数。
思考:综合以上两点,我们可以推断出在 vuex 文件的导出内容大致为:
export default {
install: (vue)=> {
//todo
},
Store: ({state,getters,actions,mutations})=> {
//vuex的核心代码
}
}
3. action
const store = new Vuex.Store({
actions: {
addCount({commit},payload) {
commit("changeCount",payload)
}
}
state:{...},
mutations: {...},
getters: {...}
}
解析: 这种是官方推荐的结构写法,你也可以这么写
addCount(context,payload) {
context.commit("changeCount",payload)
}
解析: 第二种写法种的参数context是指上下文环境即这个store实例。上文中的第一种写法,既然可以从这个context中解构出commit方法,不难得出这个store实例包含commit方法
三、刻画vuex 的大致结构
class Store {
constructor({state, getters, mutations, actions}) {
//todo
}
}
function install(vue){
//todo
}
export default {
Store,
install
}
四、完成 install 方法
思考: 在使用官方的vuex后, 我们在每个组件中都可以访问到this.$store, 可是我们在main.js中只是把store挂载到根实例中,按理说只有在根实例中才可以访问到store
//main.js
import store from "./store"
new Vue({
el: "#app",
store
})
结论: 要在根实例中把store 赋值给$store,并且利用vue组件加载的先父后子原则,从根实例的子组件开始,每个组件都从父组件拿到$store,并将从父组件拿到的$store 赋值给自己的$store属性,从而实现每个组件都拥有$store属性,并且都指向同一个store实例
let Vue
let install = (_Vue) => {
Vue = _Vue
// 通过混入beforeCreate生命周期的钩子函数,使每个vue组件都挂载上store
Vue.mixin({
beforeCreate(){
//this指向每个执行的 vue 组件
//先判断当前的this是不是根实例,因为第一次执行只有根实例上的$options有store实例
if(this.$options.store){
//根实例
this.$store = this.$options.store
} else{
//所以从根实例为一级组件开始,二级组件可以通过this.$parent
//拿到一级组件的store, 然后挂载到自己身上的$store
//如此反复 每个组件都挂载上 $store
this.$store = this.$parent && this.$parent.$store
}
}
})
}
export default install
五、完成 Store 中的 state
思考: store 中 state 的响应式是利用vue中data的响应式来实现的
import install from './intsall.js'
class Store {
constructor({state,getters,mutations,actions}){
this.vm = new Vue ({
data: {
state
}
})
}
}
export default {
install,
Store
}
六、完成 Store 中的 getters
提示: 为了方便读者理解,接下来的内容我们会先列出用法,再展示功能实现代码
getters: {
getCount: (state) => {return state.count}
},
提示: 功能实现代码如下:
class Store {
/***** state code *****/
constructor({state,getters,mutations,actions}){
this.vm = new Vue ({
data: {
state
}
})
}
/***** getters code ****/
this.getters = {}
for(let getterName in getters){
// 利用Object.deineProperty 对this.getter 进行访问劫持
Object.defineProperty(this.getters,getterName,{
get: ()=>{
//getter.getCount = (state) => {return state.count}
return getter[getterName](this.vm.state)
}
})
}
}
七、完成 Store 中的 mutations
提示: 原生使用语法如下:
mutations: {
changeCount(state, payload) {
state.count = payload
},
},
提示: 功能实现代码如下:
class Store {
/***** state code *****/
constructor({state,getters,mutations,actions}){
this.vm = new Vue ({
data: {
state
}
})
}
/***** mutations code ****/
this.mutations = {}
Object.keys(mutations).forEach( mutationName => {
this.mutations[mutationName] = (newValue)=> {
mutation[mutationName](this.vm.state,newValue)
}
})
}
八、完成 Store 中的 commit
提示: 原生使用语法如下:
addCount({commit},payload) {
commit("changeCount",payload)
}
提示: 功能实现代码如下:
class Store {
/***** state code *****/
constructor({state,getters,mutations,actions}){
this.vm = new Vue ({
data: {
state
}
})
}
/***** commit code ****/
this.commit = (mutationName,newValue)=> {
this.mutations[mutationName](newValue)
}
}
九、完成 Store 中的 actions
提示: 原生使用语法如下:
actions:{
addCount(context,payload) {
context.commit("changeCount",payload)
}
}
提示: 功能实现代码如下:
class Store {
/***** state code *****/
constructor({state,getters,mutations,actions}){
this.vm = new Vue ({
data: {
state
}
})
}
/***** actions code ****/
this.actions = {}
Object.keys(actions).forEach(actionName => {
this.actions[actionName] = (newValue)=> {
actions[actionName](this, newValue)
}
})
}