一看就懂的vuex核心源码

Vuex是什么

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。说白了,Vuex就是一个全局单例模式管理的仓库,里面存储着响应式的数据,任何组件都可以从仓库里面取到数据。其可预测表现在:更改只能通过Vuex来集中更改。通过定义和隔离状态管理中的各种概念并通过强制规则维持视图和状态间的独立性,代码将会变得更结构化且易维护。

vuex的核心概念和使用方法点此直达官网:https://vuex.vuejs.org/zh/

核心概念

  • state 状态,数据
export default new Vuex.Store({
    
    
 state: {
    
     counter:0 },
})
  • mutations 更改状态的函数
export default new Vuex.Store({
    
    
 mutations: {
    
    
	 add(state) {
    
    
	 	state.counter++
	 }
   }
})
  • actions 异步操作
export default new Vuex.Store({
    
    
	 actions: {
    
    
		 add({
     
      commit }) {
    
    
			 setTimeout(() => {
    
    
			 	commit('add')
			 }, 1000);
		 }
	 }
})
  • getters 派生状态,类似计算属性
export default new Vuex.Store({
    
    
 	getters: {
    
    
 		doubleCounter(state) {
    
    
 			return state.counter * 2;
 		}
 	}
})
  • store 包含以上概念的容器

Vuex原理解析

根据上面的用法,以及我们使用时需要在main.js里面Vue.use(Vuex),分析Vuex实际上是干了这样几件事:

  1. 实现一个插件:声明Store类,挂载$store
  2. Store类
    • 创建 响应式state,保存mutations、actions和getters
    • 实现commit根据⽤户传⼊type执⾏对应mutation
    • 实现dispatch根据⽤户传⼊type执⾏对应action,同时传递上下⽂
    • 实现getters,按照getters定义对state做派⽣

第一步:实现一个插件,并创建响应式state。我们借助new Vue()去创建一个响应式的state。我们不希望用户传进来的state可以随意被修改,所以这里没有直接return options.state,而是声明一个不被代理的$$state。当用户访问state时,通过ES6中的get()和set()方法,对值进行访问和拦截。

let _Vue

class Store {
    
    
  constructor (options) {
    
    
    //   创建响应式数据
    this._vm = new _Vue({
    
    
      data () {
    
    
        // return options.state
        return {
    
    
          // 不希望被代理,保证是私有的不会被恣意篡改的
          $$state: options.state
        }
      }
    })
  }

  get state () {
    
    
    return this._vm._data.$$state
  }

  // 不希望用户直接修改state的值,所以这里进行错误警告并不做修改
  set state (v) {
    
    
    console.error('please use replaceState to reset state')
  }
}

function install (Vue) {
    
    
  _Vue = Vue
  Vue.mixin({
    
    
    beforeCreate () {
    
    
      if (this.$options.store) {
    
    
      	// 全局挂载$store
        Vue.prototype.$store = this.$options.store
      }
    }
  })
}

export default {
    
     Store, install }

第二步:我们同步修改state是通过commit(type, payload)方法来修改state的值的,其中type是在mutations中声明的对应的方法,并且该方法中有个参数是state:

const store = new Vuex.Store({
    
    
  state: {
    
    
    counter: 0
  },
  mutations: {
    
    
    add (state) {
    
    
      state.counter++
    }
  },
  getters: {
    
    
    doubleCounter (state) {
    
    
      return state.counter * 2
    }
  },
  actions: {
    
    
    add ({
     
      commit }) {
    
    
      setTimeout(() => {
    
    
        commit('add')
      }, 1000)
    }
  }
})
export default store

所以我们实现一个commit方法,根据⽤户传⼊type获取并执⾏对应mutation,并将state传进去:

class Store {
    
    
	 constructor(options = {
     
     }) {
    
    
		 // 保存⽤户配置的mutations选项
		 this._mutations = options.mutations || {
    
    }
	 }
	 commit(type, payload) {
    
    
		 // 获取type对应的mutation
		 const entry = this._mutations[type]
		 if (!entry) {
    
    
		 return console.error(`unknown mutation type: ${
      
      type}`);
	 }
		 // 传递state给mutation
		 entry(this.state, payload);
	 }
}

第三步:实现actions,根据⽤户传⼊type获取并执⾏对应action。与commit方法不同的是,因为用户操作可能是异步的promise啥的,所以我们需要return回去,并且绑定this的指向,否则会this指向错误。还有一点就是mutations里面声明的函数参数拿到的是state,而actions里面声明的函数参数拿到的是一个包含commit, getters等等的对象,这些其实就是Store本身。

class Store {
    
    
	 constructor(options = {
     
     }) {
    
    
		 // 保存⽤户编写的actions选项
		 this._actions = options.actions || {
    
    }
		 // 绑定commit上下⽂否则action中调⽤commit时可能出问题!!
		 // 同时也把action绑了,因为action可以互调
		 // 此步操作可简写为:this.commit = this.commit.bind(this); this.dispatch = this.dispatch.bind(this)
		 const store = this
		 const {
    
    commit, action} = store
		 this.commit = function boundCommit(type, payload) {
    
    
		 	commit.call(store, type, payload)
	 	 }
		 this.action = function boundAction(type, payload) {
    
    
		 	return action.call(store, type, payload)
		 }
	 }
	 
	 dispatch(type, payload) {
    
    
		 // 获取⽤户编写的type对应的action
		 const entry = this._actions[type]
		 if (!entry) {
    
    
		 	return console.error(`unknown action type: ${
      
      type}`)
		 }
		 // 异步结果处理常常需要返回Promise
		 return entry(this, payload);
	 }
}

第四步:实现getters,用户可以$store.getters.XXX访问getters,并且getter是响应式的。其实就是将getters里面定义的值存储到计算属性computed中,并且不允许直接修改

let _Vue

class Store {
    
    
  constructor (options) {
    
    
    this._warpperGetters = options.getters
    const computed = {
    
    }
    this.getters = {
    
    }
    
    Object.keys(this._warpperGetters).forEach(key => {
    
    
      // 将state传给getters
      computed[key] = () => this._warpperGetters[key](this.state)
      // 设置只读
      Object.defineProperty(this.getters, key, {
    
    
        get: () => this._vm[key]
      })
    })

    //   创建响应式数据
    this._vm = new _Vue({
    
    
      data () {
    
    
        return {
    
    
          // 不希望被代理,是私有的不会被恣意篡改的
          $$state: options.state
        }
      },
      computed
    })
  }

  get state () {
    
    
    return this._vm._data.$$state
  }

  set state (v) {
    
    
    console.error('please use replaceState to reset state')
  }

结合上面四步,就实现了一个简版的vuex。Vuex 可以帮助我们管理共享状态,并附带了更多的概念和框架。如果你的应用够简单,最好不要使用 Vuex。一个简单的 store 模式就足够所需了。但是,如果你需要构建一个中大型单页应用,你很可能会考虑如何更好地在组件外部管理状态,Vuex 将会成为自然而然的选择。

猜你喜欢

转载自blog.csdn.net/weixin_43867717/article/details/124969726