vuex4.0源码

github:https://github.com/vuejs/vuex 下载调试

基础

示例

// src/index.js
import {createApp} from 'vue'
import App from './App.vue'
import store from './store'

// vue.use(store)插件的用法,会默认调用store中的install方法
createApp(App).use(store, 'my').mount('#app') // 给store加标识,用于创建多个store


// store/index.js
import {createStore} from 'vuex'
export default createStore({
	state:{ count:0 },
	getter:{ // vuex4没有实现计算属性
		double(state){
			return state.count * 2
		}
	},
	mutations:{  // 可以更改状态,必须是同步更改的
		add(state, payload){
			state.count +=payload
		}
	},
	actions: { // 可以调用其他action,或者调用mutation
		asyncAdd({commit}, payload){
			setTimeout(()=>{
				commit('add', payload)
			}, 1000)
		}
	},
	modules: {
	}
})



// App.vue
import {computed} from 'vue'
import {useStore} from 'vuex'

export default{
	setup(){
		const store = useStore('my') 
		function add(){
			store.commit('add', 1)
		}
		function asyncAdd(){
			store.dispatch('asyncAdd', 1)
		}
	return{
		count: computed(()=> store.state.count),
		double: computed(()=> store.getters.double),
		add,
		asyncAdd
	}
	}
}

分析

  1. 创建一个Store类
  2. 把Store类实例挂载到全局上下文ctx和全局provide上
    createStore方法(new Store,把实例挂到全局上下文ctx)
    useStore(inject(storeKey),把实例挂载到全局provide上,供子组件通信使用)
    在这里插入图片描述
  3. 把各种方法挂载到Store实例上
    在这里插入图片描述
// index.js
import { inject, reactive } from 'vue'

// 对象的数据拼接完再给fn函数
function forEachValue(obj, fn){
    Object.keys(obj).forEach(key=>fn(obj[key], key))
}

// forEachValue({a:1, b:2}, function(value, key){
//     console.log(value, key)
// })

const storeKey = 'store'

class Store{
    constructor(options){
        const store = this

		// 拿到createStore的参数,分别挂载到当前组件的store上
        store._state = reactive({data: options.state})
        const _getters = options.getters 
        
        store.getters = {}

        forEachValue(_getters, function(fn, key){
            Object.defineProperty(store.getters, key, {
                get:()=> fn(store.state) 
            })
        })

        store._mutations = Object.create(null)
        store._actions = Object.create(null)
        const _mutations = options.mutations
        const _actions = options.actions
        
        // 遍历对象,把对象都挂载到store._mutations对象上
        // mutation相当于 store.commit('add', 1) commit执行mutations里的同步操作
        forEachValue(_mutations, function(mutation, key){
            store._mutations[key] = (payload)=>{ 
                mutation.call(store, store.state, payload)
            }
        })

        // action相当于 store.dispatch('asyncAdd', 1)  dispatch执行actions里的同步操作
        forEachValue(_actions, function(action, key){
            store._actions[key] = (payload)=>{ 
                action.call(store, store, payload)
            }
        })
    }
    get state(){
        return this._state.data
    }
    install (app, injectKey) {
        // 把store挂载到上下文的provide上,全局都可以访问
        app.provide(injectKey || storeKey, this)
        // 配置全局属性和方法,在页面可以直接使用$store.getters.xxx
        app.config.globalProperties.$store = this 
    }
    commit=(type, payload)=>{
        this._mutations[type](payload)
    }
    dispatch=(type, payload)=>{
        this._actions[type](payload)
    }
}

function createStore (options) {
    console.log(options)
    return new Store(options)
}

function useStore (key = null) {
    return inject(key !== null ? key : storeKey)
}

export {
    useStore,
    createStore
}

命名空间modules

示例

// store.js
import { createStore } from 'vuex'

const state = {
  count: 0
}

const mutations = {
  increment (state) {
    state.count++
  },
}

const actions = {
  increment: ({ commit }) => commit('increment'),
}

const getters = {
  evenOrOdd: state => state.count % 2 === 0 ? 'even' : 'odd'
}

const modules = {
  aCount: {
    namespaced: true,
    state:{count:0},
    mutations:{
      increment(state, payload){
        state.count +=payload
      }
    },
    actions:{
      increment: ({ commit }, payload) => commit('increment', payload),
    }
  },
  bCount: {
    namespaced: true,
    state:{count:0},
    mutations:{
      increment(state, payload){
        state.count +=payload
      }
    },
    actions:{
      increment: ({ commit }, payload) => commit('increment', payload),
    }
  }
}

export default createStore({
  state,
  getters,
  actions,
  mutations,
  modules
})

分析

在这里插入图片描述
1)从结构可以看出,_modules为ModuleCollection类的实例,这个实例的root为Module类的实例
2)_children上挂载的是每个命名空间对应的Module实例
3)分别把各个命名空间的mutation、action挂载到Store的_mutations、_actions上,且key拼接上对应的命名空间名称

// src/module.js
import { forEachValue } from './utils'

class Module{
    constructor(rawModule){
        this._rawModule = rawModule,
        this.state = rawModule.state,
        this._children = {}
    }
    
    get namespaced () {
        return !!this._rawModule.namespaced
    }

    addChild(key, module){
        this._children[key] = module 
    }

    getChild(key){
        return this._children[key]
    }
  
    forEachChild (fn) {
        forEachValue(this._children, fn)
    }

    forEachGetter (fn) {
        if (this._rawModule.getters) {
            forEachValue(this._rawModule.getters, fn)
        }
    }

    forEachAction (fn) {
        if (this._rawModule.actions) {
            forEachValue(this._rawModule.actions, fn)
        }
    }

    forEachMutation (fn) { 
        console.log('_rawModule',this._rawModule )
        if (this._rawModule.mutations) {
            forEachValue(this._rawModule.mutations, fn)
        }
    }

}

export class ModuleCollection{
    constructor(rootModule){
        this.root = null
        // 把属性挂载到组件上
        this.register(rootModule, [])
    }
    register(rawModule, path){
        const newModule = new Module(rawModule)
        // 根模块
        if(path.length===0){
            this.root = newModule
        }else{ // [a] [b] [a,c] this.root._children.a._children.c
            const parent = path.slice(0,-1).reduce((module, current)=>{
                return module.getChild(current)
            }, this.root)
            parent.addChild(path[path.length-1], newModule)
        }

        if(rawModule.modules){
            forEachValue(rawModule.modules, (rawChildModule, key) =>{
                this.register(rawChildModule, path.concat(key))
            })
        }
    }
    getNamespace (path) {
        let module = this.root
        console.log('path', path)
        return path.reduce((namespace, key) => {
          module = module.getChild(key)
          console.log('module',  module)
          return namespace + (module.namespaced ? key + '/' : '')
        }, '')
    }
}
// src/store-utils.js
export function installModule(store, rootState, path, module){
    let isRoot = !path.length
    const namespace = store._modules.getNamespace(path)

    // 如果有命名空间
    if (module.namespaced) {
        store._modulesNamespaceMap[namespace] = module
    }

    if(!isRoot){
        let parentState = path.slice(0,-1).reduce((state,key)=>state[key], rootState)
        parentState[path[path.length-1]] = module.state
    }

    const local = module.context = makeLocalContext(store, namespace, path)

    module.forEachChild((child, key) => {
        installModule(store, rootState, path.concat(key), child)
    })

    /* 把_rawModule的mutations数据copy到组件实例的_mutations上
        _mutations:{
          // aCount/increment: [fn, fn] // 源码里是数组形式处理,这里只对应一个函数
          aCount/increment: fn
        }
    */
    module.forEachMutation((mutation, key) => {
        const namespacedType = namespace + key
        store._mutations[namespacedType] = (payload)=>{ 
          mutation.call(store, local.state, payload)
        }
    })
    
    module.forEachAction((action, key) => {
        const type = action.root ? key : namespace + key
        const handler = action.handler || action
        console.log('type', type)
        console.log('handler', handler)
        store._actions[type] = (payload)=>{ 
          handler.call(store, local, payload)
        }
    })
    
    module.forEachGetter((getter, key) => {
        const namespacedType = namespace + key
        store.getters = {}
        Object.defineProperty(store.getters, namespacedType, {
          get: namespace === ''
          ? () => getter(store.state) : () => getter(local.state)
        })
    })
}

function makeLocalContext (store, namespace, path) {
    const noNamespace = namespace === ''
  
    const local = {
      dispatch: noNamespace ? store.dispatch : (_type, _payload, _options) => {
        const args = unifyObjectStyle(_type, _payload, _options)
        const { payload, options } = args
        let { type } = args
  
        if (!options || !options.root) {
          type = namespace + type
        }
  
        return store.dispatch(type, payload)
      },
  
      commit: noNamespace ? store.commit : (_type, _payload, _options) => {
        const args = unifyObjectStyle(_type, _payload, _options)
        const { payload, options } = args
        let { type } = args
  
        if (!options || !options.root) {
          type = namespace + type
        }
  
        store.commit(type, payload, options)
      }
    }

    Object.defineProperties(local, {
      getters: {
        get: noNamespace
          ? () => store.getters
          : () => makeLocalGetters(store, namespace)
      },
      state: {
        get: () => getNestedState(store.state, path)
      }
    })
  
    return local
}

function getNestedState (state, path) {
  return path.reduce((state, key) => state[key], state)
}

function unifyObjectStyle (type, payload, options) {
    if (type.type) {
      options = payload
      payload = type
      type = type.type
    }
    return { type, payload, options }
  }
  

github源码参考

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值