手写vuex4源码(五)模块实现原理

一、前文梳理

流程:

  • vuex暴露两个方法createStoreuseStore
  • 调用createStore传递过来所有的state、getters、mutations
  • createStore中调用 new Store方法,产生store实例,传递用户定义的所有的数据
  • new Store中的install方法会把store实例放在全局上,确保每个组件可以通过$store访问,同时通过app.provide(injectKey || storeKey, this)把store放到app上
  • new Store调用new ModuleCollection继续传递数据,ModuleCollectionregister方法把数据变成一个root树
  • new StoreinstallModule方法把state、getters、mutations等按照模块划分好
  • useStore把当前store注入每个组件:组件中会const store = useStore();

目前状态:
假如每个模块都有同名的add方法,调用时,每一个都会触发

二、实现逻辑

当用户调用方法时,想要实现调用哪个就触发对应的方法,使用发布订阅模式

三、收集getters、mutations、actions

把所有的getters、mutations、actions收集到store

1、在store中创建收集容器

constructor(options) {
        const store = this
        // options是一个对象,对象中有state、modules等
        // 发布订阅模式,用户触发哪个就去调用哪个
        store._modules = new ModuleCollection(options)
        store._wrappedGetters = Object.create(null);
        store._mutations = Object.create(null);
        store._actions = Object.create(null);
}

2、定义辅助方法

在modules.js中定义方法

forEachGetter(fn){
        if(this._raw.getters){
            forEachValue(this._raw.getters,fn)
        }
    }
    forEachMutation(fn){
        if(this._raw.mutations){
            forEachValue(this._raw.mutations,fn)
        }
    }
    forEachAction(fn){
        if(this._raw.actions){
            forEachValue(this._raw.actions,fn)
        }
    }

3、实现收集

在安装时,把所有的对象放到store对应定义的变量上

(1)getter

getter第一个参数为state,此处用store.state因为后续我们会将store.state 用reactive包裹,把数据变成响应式

 // 安装的时候 把所有的对象放到store对应定义的变量上,遍历当前模块上所有的getter,
    // getters  module._raw.getters
    module.forEachGetter((getter, key) => { // {double:function(state){}}
        // 第一个参数是getter的内容,key为getter的属性名,再去调用原来的方法,
        // store._wrappedGetters[key] = getter   这样写没有办法传递参数
        store._wrappedGetters[key] = () => {
            // 通过这个方法去获取最新的state
            // 第一个参数为state,写module.state问题:模块上的状态是自己维护的,不具备响应式的功能
            return getter(getNestedState(store.state, path)); 
        }
    })
function getNestedState(state, path) { // 根据路径 获取store.上面的最新状态
    return path.reduce((state, key) => state[key], state)
}

(2)mutation

// mutation   {add:[mutation]}
  module.forEachMutation((mutation, key) => {
    const entry = store._mutations[key] || (store._mutations[key] = []);
    // 把当前的mutation放到entry里面
    // 用户store.commit('add',payload),调用entry里面的方法,payload作为参数传递进来
    entry.push((payload)=>{ 
        // call:改变this的指向同时让函数mutation立即执行,第一个参数为最新的state,第二个为用户传递的参数
        mutation.call(store, getNestedState(store.state, path), payload)
    })
  });

(3)action

  • mutation和action的一个区别, action执行后返回一个是promise,需要判断用户是否写了.then方法
  • action既可以调用commit又可以调用dispatch,所以区别于mutation第一个参数传递store
//   // store.dispatch('LOGIN',payload).then(()=>{})
  module.forEachAction((action, key) => {
    const entry = store._actions[key] || (store._actions[key] = []);
    // 用户store.dispatch('add',payload),调用entry里面的方法,payload作为参数传递进来
    entry.push((payload)=>{ 
        // action既可以调用commit又可以调用dispatch,所以第一个参数传递store
        let res = action.call(store, store, payload)
        // res 是不是一个promise
        if (!isPromise(res)) {
            return Promise.resolve(res);
        }
        return res;
    })
  });

utils中扩展工具类,判断返回值是不是一个promise

export function isPromise(val) {
  return val && typeof val.then === "function";
}

手写vuex4源码(五)模块实现原理_第1张图片

四、下文功能

目前store中没有state,state是单独的,需要把state、getter等定义在store上

你可能感兴趣的:(手写vuex4.x源码,javascript,前端,开发语言)