请简述一下vuex实现原理[通俗易懂]

Vue (58) 2023-07-27 19:12

Hi,大家好,我是编程小6,很荣幸遇见你,我把这些年在开发过程中遇到的问题或想法写出来,今天说一说请简述一下vuex实现原理[通俗易懂],希望能够帮助你!!!。

请简述一下vuex实现原理

请简述一下vuex实现原理[通俗易懂]_https://bianchenghao6.com/blog_Vue_第1张

vuex实现原理我们简单过一遍源码 地址

首先我们例出几个问题进行思考

  • store是怎么注册的?
  • mutation,commit 是怎么实现的?
  • 辅助函数是怎么实现的?

store是怎么注册的

看了下面的源码就很清楚了, 我们看到vuex在vue 的生命周期中的初始化钩子前插入一段 Vuex 初始化代码。给 Vue 的实例注入一个 $store 的属性,这也就是为什么我们在 Vue 的组件中可以通过 this.$store.xxx 访问到 Vuex 的各种数据和状态

// 源码位置 https://github.com/vuejs/vuex/blob/665455f8daf8512e7adbf63c2842bc0b1e39efdb/src/mixin.js
export default function (Vue) {
 const version = Number(Vue.version.split('.')[0])
 if (version >= 2) {
 Vue.mixin({ beforeCreate: vuexInit })
 } else {
 // override init and inject vuex init procedure
 // for 1.x backwards compatibility.
 const _init = Vue.prototype._init
 Vue.prototype._init = function (options = {}) {
 options.init = options.init
 ? [vuexInit].concat(options.init)
 : vuexInit
 _init.call(this, options)
 }
 }
 /**
 * Vuex init hook, injected into each instances init hooks list.
 */
 function vuexInit () {
 const options = this.$options
 // store injection
 if (options.store) {
 this.$store = typeof options.store === 'function'
 ? options.store()
 : options.store
 } else if (options.parent && options.parent.$store) {
 this.$store = options.parent.$store
 }
 }
}

mutations,commit 是怎么实现的

// 源码位置 https://github.com/vuejs/vuex/blob/665455f8daf8512e7adbf63c2842bc0b1e39efdb/src/store.js#L417
function registerMutation (store, type, handler, path = []) {
 const entry = store._mutations[type] || (store._mutations[type] = [])
 entry.push(function wrappedMutationHandler (payload) {
 handler(getNestedState(store.state, path), payload)
 })
}

registerMutation 是对 store 的 mutation 的初始化,它接受 4 个参数,store为当前 Store 实例,type为 mutation 的 key,handler 为 mutation 执行的回调函数,path 为当前模块的路径。mutation 的作用就是同步修改当前模块的 state ,函数首先通过 type 拿到对应的 mutation 对象数组, 然后把一个 mutation 的包装函数 push 到这个数组中,这个函数接收一个参数 payload,这个就是我们在定义 mutation 的时候接收的额外参数。这个函数执行的时候会调用 mutation 的回调函数,并通过 getNestedState(store.state, path) 方法得到当前模块的 state,和 playload 一起作为回调函数的参数

我们知道mutation是通过commit来触发的,这里我们也来看一下commit的定义

// 源码位置 https://github.com/vuejs/vuex/blob/665455f8daf8512e7adbf63c2842bc0b1e39efdb/src/store.js#L82
 commit (_type, _payload, _options) {
 // check object-style commit
 const {
 type,
 payload,
 options
 } = unifyObjectStyle(_type, _payload, _options)
 const mutation = { type, payload }
 const entry = this._mutations[type]
 if (!entry) {
 if (process.env.NODE_ENV !== 'production') {
 console.error(`[vuex] unknown mutation type: ${type}`)
 }
 return
 }
 this._withCommit(() => {
 entry.forEach(function commitIterator (handler) {
 handler(payload)
 })
 })
 this._subscribers.forEach(sub => sub(mutation, this.state))
 if (
 process.env.NODE_ENV !== 'production' &&
 options && options.silent
 ) {
 console.warn(
 `[vuex] mutation type: ${type}. Silent option has been removed. ` +
 'Use the filter functionality in the vue-devtools'
 )
 }
 }

commit 支持 3 个参数,type 表示 mutation 的类型,payload 表示额外的参数,根据 type 去查找对应的 mutation,如果找不到,则输出一条错误信息,否则遍历这个 type 对应的 mutation 对象数组,执行 handler(payload) 方法,这个方法就是之前定义的 wrappedMutationHandler(handler),执行它就相当于执行了 registerMutation 注册的回调函数

辅助函数

辅助函数的实现都差不太多,这里只讲解mapState

	// 源码地址 https://github.com/vuejs/vuex/blob/665455f8daf8512e7adbf63c2842bc0b1e39efdb/src/helpers.js#L7
export const mapState = normalizeNamespace((namespace, states) => {
 const res = {}
 normalizeMap(states).forEach(({ key, val }) => {
 res[key] = function mappedState () {
 let state = this.$store.state
 let getters = this.$store.getters
 if (namespace) {
 const module = getModuleByNamespace(this.$store, 'mapState', namespace)
 if (!module) {
 return
 }
 state = module.context.state
 getters = module.context.getters
 }
 return typeof val === 'function'
 ? val.call(this, state, getters)
 : state[val]
 }
 // mark vuex getter for devtools
 res[key].vuex = true
 })
 return res
})

mapState在调用了 normalizeMap 函数后,把传入的 states 转换成由 {key, val} 对象构成的数组,接着调用 forEach 方法遍历这个数组,构造一个新的对象,这个新对象每个元素都返回一个新的函数 mappedState,函数对 val 的类型判断,如果 val 是一个函数,则直接调用这个 val 函数,把当前 store 上的 state 和 getters 作为参数,返回值作为 mappedState 的返回值;否则直接把 this.$store.state[val] 作为 mappedState 的返回值

为了更直观的理解,我们看下最终mapState的效果

	computed: mapState({
 name: state => state.name,
 })
 
 // 等同于
 
 computed: {
 name: this.$store.state.name
 }

发表回复