关于javascript:源码-vuex

32次阅读

共计 31632 个字符,预计需要花费 80 分钟才能阅读完成。

导航

[[深刻 01] 执行上下文 ](https://juejin.im/post/684490…)
[[深刻 02] 原型链 ](https://juejin.im/post/684490…)
[[深刻 03] 继承 ](https://juejin.im/post/684490…)
[[深刻 04] 事件循环 ](https://juejin.im/post/684490…)
[[深刻 05] 柯里化 偏函数 函数记忆 ](https://juejin.im/post/684490…)
[[深刻 06] 隐式转换 和 运算符 ](https://juejin.im/post/684490…)
[[深刻 07] 浏览器缓存机制(http 缓存机制)](https://juejin.im/post/684490…)
[[深刻 08] 前端平安 ](https://juejin.im/post/684490…)
[[深刻 09] 深浅拷贝 ](https://juejin.im/post/684490…)
[[深刻 10] Debounce Throttle](https://juejin.im/post/684490…)
[[深刻 11] 前端路由 ](https://juejin.im/post/684490…)
[[深刻 12] 前端模块化 ](https://juejin.im/post/684490…)
[[深刻 13] 观察者模式 公布订阅模式 双向数据绑定 ](https://juejin.im/post/684490…)
[[深刻 14] canvas](https://juejin.im/post/684490…)
[[深刻 15] webSocket](https://juejin.im/post/684490…)
[[深刻 16] webpack](https://juejin.im/post/684490…)
[[深刻 17] http 和 https](https://juejin.im/post/684490…)
[[深刻 18] CSS-interview](https://juejin.im/post/684490…)
[[深刻 19] 手写 Promise](https://juejin.im/post/684490…)
[[深刻 20] 手写函数 ](https://juejin.im/post/684490…)

[[react] Hooks](https://juejin.im/post/684490…)

[[部署 01] Nginx](https://juejin.im/post/684490…)
[[部署 02] Docker 部署 vue 我的项目 ](https://juejin.im/post/684490…)
[[部署 03] gitlab-CI](https://juejin.im/post/684490…)

[[源码 -webpack01- 前置常识] AST 形象语法树 ](https://juejin.im/post/684490…)
[[源码 -webpack02- 前置常识] Tapable](https://juejin.im/post/684490…)
[[源码 -webpack03] 手写 webpack – compiler 简略编译流程 ](https://juejin.im/post/684490…)
[[源码] Redux React-Redux01](https://juejin.im/post/684490…)
[[源码] axios ](https://juejin.im/post/684490…)
[[源码] vuex ](https://juejin.im/post/684490…)
[[源码 -vue01] data 响应式 和 初始化渲染 ](https://juejin.im/post/684490…)
[[源码 -vue02] computed 响应式 – 初始化,拜访,更新过程 ](https://juejin.im/post/684490…)
[[源码 -vue03] watch 侦听属性 – 初始化和更新 ](https://juejin.im/post/684490…)
[[源码 -vue04] Vue.set 和 vm.$set ](https://juejin.im/post/684490…)
[[源码 -vue05] Vue.extend ](https://juejin.im/post/684490…)

[[源码 -vue06] Vue.nextTick 和 vm.$nextTick ](https://juejin.im/post/684790…)

前置常识

一些单词

Mutation:变异
raw:未加工
assert: 断言

internal:外部的
(store internal state : store 外部的 state)

duplicate:反复,赋值,正本
localized:部分的

unify:对立
(unifyObjectStyle 对立对象类型)

Valid: 无效的 

vue 如何编写一个插件

  • 插件通常来为 vue 增加 (全局性能)

    • 全局办法,全局资源,全局混入,vue 实例办法 等
    • Vue 结构器静态方法和属性 Vue.prototype 实例办法和属性 mixindirective
  • 如果应用插件

    • 通过组件模式:

        1. Vue.use(plugin, [options])
        1. new Vue(组件选项)
    • 通过 script 形式引入

      • 比方 vue-router 插件

        • 会在 index.js 外部判断 如果是 (浏览器环境并且 window.Vue 存在),就主动调用 window.Vue.use(VueRouter)
  • 留神点:

    • <font color=red> 每个插件都应该裸露一个 install 办法,并且 install 办法的第一个参数必须是 Vue 结构器,第二个参数是一个可选的参数对象 </font>

      • 因为 install 办法的第一个参数是 Vue 结构器,所以能够获取 Vue 上的 directive, mixin, 等等
    • 前面会晓得如果没有裸露 install 办法,那插件自身就必须是一个函数,该函数就会被当做 install 办法
  • 代码

     插件:const firstPlugin = {install(Vue, options) {console.log(options, 'options')
    
          // 全局办法 firstGlobalMethod
          Vue.firstGlobalMethod = function () {console.log('插件 - 全局办法 - 即 Vue 结构器的静态方法')
          }
    
          // 增加全局资源 全局指令
          Vue.directive('first-directive', {bind(el, binding, vnode, oldVnode) {console.log('只调用一次,指令第一次绑定到元素时调用。在这里能够进行一次性的初始化设置')
                  console.log(el, 'el 指令所绑定的元素,能够用来间接操作 DOM')
                  console.log(binding, 'binding')
                  console.log(vnode, 'Vue 编译生成的虚构节点')
                  console.log(oldVnode, '上一个虚构节点,仅在 update 和 componentUpdated 钩子中可用')
              },
              inserted() {console.log('被绑定元素插入父节点时调用 ( 仅保障父节点存在,但不肯定已被插入文档中)')
              }
          })
    
          // 全局混合 注入组件选项
          Vue.mixin({created: function () {console.log('插件 - 全局混合 - 在所有组件中混入 created() 生命周期')
              }
          })
    
          // 实例办法
          // 个别规定在 Vue.prototype 上挂载的属性和办法都要以 $ 结尾
          Vue.prototype.$firstPluginMethod = function () {console.log('插件 - 实例办法 - 挂载在 vue 实例原型对象上的办法,记得该属性和办法以 $ 结尾')
          }
      }
    }
    
    export default firstPlugin
     注册插件:import Vue from 'vue'
    import App from './App.vue'
    import router from './router'
    import store from './store'
    import axios from 'axios'
    import firstFlugin from './plugin/first-plugin'
    
    Vue.config.productionTip = false
    
    Vue.prototype.$axios = axios
    
    Vue.use(firstFlugin) // -------------------------------------------------------------- 注册插件
    
    new Vue({
    router,
    store,
    render: h => h(App)
    }).$mount('#app')
    

Vue.use()

  • Vue.use(plugin)
  • 参数:object 或者 function
  • 作用:

    • 装置 Vue 插件
    • 插件是对象:需提供 install 办法
    • 插件是函数:本身会作为 install 办法
    • install 办法:会将 Vue 作为参数,在 Vue.use() 调用时主动执行
  • 留神:

    • <font color=red>Vue.use() 须要在 new Vue() 之前被调用 </font>
    • 当 install 办法被同一个插件屡次调用,插件将只会被装置一次
  • Vue.use() 源码

    • 次要做了以下事件

      1. 如果插件注册过

        • 间接返回 (返回 this 次要为了能链式调用)
      2. 没注册过

        • 判断该插件是对象还是函数

          • <font color=red> 对象:调用 install 办法 </font>
          • <font color=red> 函数:把该插件作为 install 办法,间接调用该函数 </font>
        • 把该插件增加进插件数组,下次判断是否注册过期,就注册过了
        • 返回 this 不便链式调用
      3. 总结:

        • Vue.use() 最次要就是调用插件的 install 办法
    // Vue.use() 源码
    // 文件门路:core/global-api/use.js
    
    
    import {toArray} from '../util/index'
    
    export function initUse(Vue: GlobalAPI) {Vue.use = function (plugin: Function | Object) {
      // Vue.use 在 Vue 结构器上挂载 use 办法
    
      const installedPlugins = (this._installedPlugins || (this._installedPlugins = []))
      // installedPlugins
      // 如果 Vue._installedPlugins 存在就间接赋值
      // 如果 Vue._installedPlugins 不存在赋值为空数组
    
      if (installedPlugins.indexOf(plugin) > -1) {
        // 该插件在插件数组 installedPlugins 中存在就间接返回
        // 这里返回 this,就能够实现链式调用,这里的 this 就是 Vue
        return this
      }
    
      const args = toArray(arguments, 1)
      // additional parameters
      // 将除去 Vue 以外的参数收集成数组
    
      args.unshift(this)
      // 将 Vue 增加到数组的最后面
      // 因为 args 要作为插件 install 办法的参数,而 install 办法规定第一个参数必须是 Vue 结构器 
    
      if (typeof plugin.install === 'function') {
        // 插件是对象,就把插件对象的 install 办法的 this 绑定到该 plugin 上,传入参数执行
        plugin.install.apply(plugin, args)
      } else if (typeof plugin === 'function') {
        // 插件是函数,传参执行
        plugin.apply(null, args)
      }
    
      installedPlugins.push(plugin)
      // 如果该插件在 installedPlugins 不存在,首先在下面赋值为空数组,执行 isntall 后,把该插件增加进 installedPlugins 示意存在
    
      return this
      // return this 是为了链式调用,this 指代 Vue
    }
    }
    
    
    
    
    // ------------------------------------------------------
    // toArray(list, start)
    // 比方:// list = [1,2,3,4] 
    // start = 1
    export function toArray(list: any, start?: number): Array<any> {
    start = start || 0
    let i = list.length - start
    const ret: Array<any> = new Array(i)
    while (i--) {ret[i] = list[i + start]
      // i-- 先赋值即 while(3)
      // 而后减去 1 即 i =2
      // ret[2] = list[3]
      // ret[1] = list[2]
      // ret[0] = list[1]
      // ret = [2,3,4]
    }
    return ret
    }
    

vuex 源码

(1) vuex 如何装置和应用

  • 装置

    • npm install vuex -S
  • 应用

    main.js 中
    ----
    
    import Vuex from 'vuex'
    Vue.use(Vuex) // -------------------------------- 会调用 Vuex 上的 install 办法
    const store = new Vuex.Store({// --------------- new Store(options)
    state: {},
    mutations: {},
    actions: {},
    getters: {},
    modules: {}})
    const app = new Vue({
    router,
    store, // -------------------------------------- 注入 store
    render: h => h(App)
    }).$mount('#app')
    

(2) Vue.use(Vuex)

  • Vue.use(Vuex) 会调用 Vuex 中的 Vuex.install 办法

    • <font color=red>Vuex.install</font> 办法会调用 <font color=red>applyMixin(Vue)</font>

      • applyMixin(Vue)

        • 次要就是把 store 实例子注入每个组件中
        • 具体就是在组件的的 beforeCreate 生命周期中 mixin 混入 <font color=red>vuexInit</font> 办法,从而把 store 实例注入每个组件

          • mixin 须要留神:

              1. 同名钩子函数将合并为一个数组,因而都将被调用
              1. 混入对象的钩子将在组件本身钩子之前调用
  • 总结:

    • Vue.use(Vuex) => Vuex.install() => applyMixin(Vue) => Vue.mixin({beforeCreate: vuexInit})
    • 通过 Vue.use(Vuex) 的最终成果就是将 store 实例注入到每个组件中,且是共用同一个 store 实例
    • 所以:能够在每个组件中通过 this.$store 拜访到 strore 实例
    Vuex 中的 install 办法
    ----
    
    let Vue // bind on install
    export function install (_Vue) {if (Vue && _Vue === Vue) {if (__DEV__) { 
        console.error('[vuex] already installed. Vue.use(Vuex) should be called only once.'
        )
      }
      return
      // Vue 存在,并且和传入的_Vue 相等,并且是生产环境,返回
    }
    Vue = _Vue
    // Vue 不存在,就赋值传入的参数_Vue
    applyMixin(Vue)
    // 调用 applyMixin 办法
    }
applyMixin(Vue)
---
 
export default function (Vue) {const version = Number(Vue.version.split('.')[0])

  if (version >= 2) {Vue.mixin({ beforeCreate: vuexInit})
    // 版本大于等于 2,混入 beforeCreate 生命周期钩子 vuexInit 办法
  } else {// 版本 1 是为了兼容,不思考}


  function vuexInit () {
    const options = this.$options
    // 这里的 this 代表每一个组件,具备 beforeCreate 钩子
  
    // store injection
    if (options.store) {
      // this.$options.store 存在
        // 1. 阐明是根组件,即通过 const vue = new Vue({store: store}) 生成的实例 vue,即根组件
  
      this.$store = typeof options.store === 'function'
        ? options.store()
        : options.store
      // 在根组件上改在 $store 属性
        // 1. store 是个函数,间接调用函数,返回值赋值
        // 2. store 是个对象,就间接赋值
    } else if (options.parent && options.parent.$store) {
      this.$store = options.parent.$store
      // 如果不是根组件,并且父组件存在
        // 1. 因为:(父组件的 beforeCreate) 会早于 (子组件的 beforeCreate) 先执行,而除了根组件,其余组件的 $store 都是应用父组件的 $store
        // 2. 所以:导致所有组件的 $store 都是应用的 根组件的 $store,根组件只用本身的 $store,即一层层传递
        // 3. 最终:所有组件都应用了 根组件的 $store,即都应用了传入的 store 实例
    }
  }
}

(3) Store 类的 constructor

<font color=blue size=5>this._modules = new ModuleCollection(options)</font>

  • ModuleCollection 类

    • 次要作用

        1. 赋值 this.root 为根模块
        1. 如果模块中存在 modules 属性,就给父模块的 _children 属性对象中增加该模块的对应关系
    • 实例属性

      • root

        • 即 new Module 实例
    • 原型属性

      • getNamespace:如果 module 的 namespaced 属性存在,就递归的拼接 path
    • 在构造函数中调用 this.register([], rawRootModule, false)
    • <font color=blue>this.register([], rawRootModule, false)</font>

      • 次要作用就是:
      • <font color=blue>new Module(rawModule, runtime)</font>

        • 实例属性

          • 实例上挂载 <font color=blue>runtime, _children, _rawModule, state</font>
          • _children:默认是一个空对象

            • 用来寄存子 module
          • _rawModule:就是传入的 module

            • 要么是根 module 即传入 Vuex 的 options
            • 要么是 modules 中的各个 module
          • state

            • 是一个函数就调用该函数返回 state 对象
            • 是一个对象间接赋值
        • 原型属性

          • 原型上具备 <font color=blue>getChild,addChild,namespaced,update,forEachGetter …</font>
          • getChild:返回_children 对象中的某个 module
          • addChild:向_children 对象中增加莫格 module
          • namespaced:该 module 是否具备 namespaced 属性,一个布尔值
          • forEachGetter:循环 getters 对象,将 value 和 key 传给参数函数
      • <font color=blue>parent.addChild(path[path.length – 1], newModule)</font>

        • 向父 module 中的_children 对象中增加子模块映射
      • <font color=blue>array.prototype.reduce 办法须要留神 </font>

        • [].reduce((a,b) => {...}, params) 当空数组执行 reduce 时,不论第一个参数函数执行了任何逻辑,都会间接返回第二个参数 params
        • 在源码中有大量的空数组执行 reduce 的逻辑,比方注册 module 和注册 namespce
      • <font color=blue>array.prototype.slice 办法须要留神 </font>

        • [].slice(0, -1) 返回的是一个空数组

    <font color=red size=5>installModule(this, state, [], this._modules.root)</font>

  • 次要作用

    1. 将 module 的拼接后的 (path) 和 (moudle) 作为 key-value 映射到 (store._modulesNamespaceMap) 对象中
    2. state

      • 将所有 modules 中的 module 中的 state,合并到 rootModule 的 state 中
      • key => moduleName
      • value => moduleState
      • 须要留神的是批改 state 都须要通过 (store._withCommit) 包装
    3. local

      • 结构 module.context 对象,赋值给 local 对象,将 local 对象作为参数传入 registerMutation,registerAction,registerGetter
      • local 对象上有 dispatch, commit, getters, state 等属性
    4. mutations

      • 将所有的 module 中的 (mutations 中的函数) 都增加到 store 的 (this._mutations) 对象中

        • key:

          • 该 module 的 namespace + mutations 中的某个函数名
          • 比方 cart/incrementItemQuantitymodule 名 + mutation 函数的名字
        • value

          • 一个数组
          • 成员是具体的 mutation 函数的包装函数,wrappedMutationHandler (payload)
      • 留神:

        • <table><tr><td bgcolor=orange>mutations hander</td></tr></table>

          • 参数

            • 第一个参数 state

              • 该 state 是以后部分模块的 state,而不是总的 state
            • 第二个参数 payload
        • <table><tr><td bgcolor=yellow>commit</td></tr></table>

          • store.commit
          • 批改 store 中 state 的惟一形式是 store.commit(mutations hander)
          • store.commit('increment', 10)
          • store.commit('increment', {amount: 10})
          • store.commit({type: 'increment',amount: 10})
    5. actions

      • 将所有的 module 中的 (actions 中的函数) 都增加到 store 的 (this._actions) 对象中
      • key:

        • action.root ? key : namespace + key
        • 下面的 key 示意的是 actions 中的 action 函数名
        • 下面的 namespace 示意的 moudle 名 +/
        • 比方:cart/addProductToCartmodule 名 + action 函数的名字
      • value:

        • 一个数组
        • 成员是具体的 action 函数的包装函数,wrappedActionHandler(payload)
      • 留神:

        • <table><tr><td bgcolor=orange>action 函数 </td></tr></table>

          • 参数

            • 第一个参数

              • 是一个 context 对象,context 和 store 实例具备雷同办法和属性
              • context 对象具备 dispatch commit getters state rootGetters rootState 这些属性
            • 第二个参数

              • payload
        • <table><tr><td bgcolor=yellow>dispatch</td></tr></table>

          • store.dispatch
          • action 函数是通过 store.dispatch 来触发的
          • store.dispatch('increment')
          • store.dispatch('incrementAsync', {amount: 10})
          • store.dispatch({type: 'incrementAsync', amount: 10})
    6. getters

      • 将所有 module 中的 getters 都映射到 (store._wrappedGetters) 对象上

        • key:namespace + key module 名 /getter 函数
        • value: 一个函数,即 wrappedGetter (store)
      • <table><tr><td bgcolor=orange>getter 函数 </td></tr></table>

        • 参数

          • 第一个参数:部分 state 对象
          • 第二个参数:部分 getters 对象
          • 第三个参数:根 state
          • 第四个参数:根 getters
          • (state, getters, rootState, rootGetters) => {...}
          • 留神 getter 函数的参数是有程序的,而 action 函数是第一个参数对像没有程序
    7. 循环 moudle 中的 (_children) 对象,顺次执行 (installModule) 办法
  • <font color=red>store._modules.getNamespace(path)</font>

    • 次要作用:拼接 module 的 path,而后赋值给 namespace
  • <font color=red>store._withCommit(fn)</font>

    • 包装传入的 mutation 函数

      • 在 mutation 函数执行时将 this._committing 设置 true
      • 在 mutation 函数执行后将 this._committing 设置 false
      • 这样能够保障批改 state 只能通过 mutation 函数,间接批改的话没有设置 this._committing 为 true,则证实不是通过 mutation 在批改
  • <font color=red>Vue.set(parentState, moduleName, module.state)</font>

    • 将 moudles 中的 module 中的 state 合并到父 state 上
  • <font color=red>makeLocalContext(store, namespace, path)</font>

    • 生成 local 对象,具备 dispatch, commit, getters, state 等属性
  • <font color=red>module.forEachMutation(fn)</font>

    • 循环遍历以后 module 中的 mutations 对象,将 value 和 key 传入 fn(mutation, key)
    • 拼接 namespace 和 key
    • 调用 registerMutation(store, namespacedType, mutation, local)
  • <font color=red>registerMutation(store, namespacedType, mutation, local)</font>

    • 向 (store._mutations[type] ) 数组中 push 一个 (mutation 包装函数)

      1. store._mutations[type]

        • typenamespace/getter 函数的函数名 ,比方像这样 cart/incrementItemQuantity
        • valuewrappedMutationHandler (payload) 这样的函数
      2. 包装函数就是给 mutation 函数增加一层壳,传入 payload,再外面调用 mutation handle 函数
      3. 留神:

        • store._mutations 是一个对象
        • 每个 store._mutations[type] 是一个数组
  • <font color=red>module.forEachAction(fn)</font>

    • module.forEachMutation(fn) 相似
    • 循环遍历以后 module 中的 actions 对象

      • 用 (action.root) 来失去不同的 type
      • 用 (action.handle) 是否存在来赋值 handle
      • 调用 registerAction(store, type, handler, local) 函数
  • <font color=red>registerAction(store, type, handler, local)</font>

    • 向 (store._actions[type] ) 数组中 push 一个 (action 包装函数 wrappedActionHandler)
  • <font color=red>wrappedActionHandler(payload)</font>

    • 会在外部调用 (action 或者 action.handler) 函数命名为 handler 函数
    • 如果 handler 函数的返回值不是 promise,就将返回值转换成 promise 对象,并 resolve,而后返回 fulfilled 状态的 promise 对象
    • <font color=red>handler()</font>

      • 绑定 this 到 store
      • 参数:

        • 第一个参数是一个对象,具备以下属性

          • dispatch
          • commit
          • getters
          • state
          • rootGetters
          • rootState
        • 第二个参数是 payload
  • <font color=red>module.forEachGetter(fn)</font>

    • 循环遍历 module 中的 getters 中的所有 getter
    • 将 value 和 key 传给 fn(value, key)
  • <font color=red>registerGetter(store, namespacedType, getter, local)</font>

    • 给 (store._wrappedGetters) 增加属性办法

      • key => namespace + key
      • value => wrappedGetter
    • <font color=red>wrappedGetter</font>

      • getter 的包装函数,(store) => getter(localState, localGetters, rootState, rootGetters)
      • 参数:部分 state getters,根 state getter

<font color=DarkOrchid>commit (_type, _payload, _options) </font>

  • 次要做了以下事件:

    1. 如果第一个参数是对象,就革新参数

      • (一) store.commit(‘increment’, {amount: 10})
      • (二) store.commit({type: ‘increment’,amount: 10})
      • 将第二种革新成第一种
    2. 寻找该 mutaation,即在 this._mutations 中通过 key 寻找 mutation 对应的 [mutation]

      • 没找到报错
      • 找到循环数组中寄存的 mutation 包装函数,外面会调用真正的 mutation handler 函数
    3. 浅拷贝 this._subscribers 而后遍历该数组,调用外面的 subscribe 函数 => 即更改 state 后须要响应视图等
  • subscribe

    • subscribe(handler: Function): Function

      • store.subscribe((mutation, state) => {...})
      • 订阅 store 的 mutation
      • handler 会在每个 mutation 实现后调用,接管 mutation 和通过 mutation 后的状态作为参数
      • 要进行订阅,调用此办法返回的函数即可进行订阅

<font color=DarkOrchid>dispatch (_type, _payload) </font>

  • 次要做了以下事件

    1. 革新参数
    2. 执行 this._actionSubscribers 中的 before 钩子的 action 监听函数

      • before 示意订阅处理函数的被调用机会应该在一个 action 散发之前调用
    3. 执行 action 函数

      • this._actions[type] 数组长度大于 1,就 promise.all 包装,保障 result 是所有 promise 都 resolve
      • this._actions[type] 数组长度小于等于 1,间接调用
    4. 当所有 promise 都 resolve 后,即所有异步都返回后果后,执行_actionSubscribers 中 after 钩子的 action 监听函数

      • after 示意订阅处理函数的被调用机会应该在一个 action 散发之后调用
    5. 并将最终的后果 resolve 进来,返回 promise 对象
  • subscribeAction

    • subscribeAction(handler: Function): Function

      • store.subscribeAction((action, state) => {...})
      • 订阅 store 的 action
      • handler 会在每个 action 散发的时候调用并接管 action 形容和以后的 store 的 state 这两个参数
      • 留神:

        • 从 3.1.0 起,subscribeAction 也能够指定订阅处理函数的被调用机会应该在一个 action 散发之前还是之后 (默认行为是之前)
        • 即能够指定 store.subscribeAction({before: (action, state) => {},after: (action, state) => {}})

源码代码

(1) this._modules = new ModuleCollection(options)

  • 次要作用

    • 赋值 this.root 为根模块
    • 如果模块中存在 modules 属性,就给父模块的 _children 属性对象中增加该模块的对应关系
// 文件目录 src/module/module-collection.js

export default class ModuleCollection {constructor (rawRootModule) {// register root module (Vuex.Store options)
    this.register([], rawRootModule, false)
  }
}

---

register (path, rawModule, runtime = true) {if (__DEV__) {assertRawModule(path, rawModule)
      // dev 环境,就断言传入的 options 中的各个属性对象的每个 key 对于应的 value 的类型,如果不是应该有的类型,就抛错
    }

    // dev 环境并且传入的 options 合乎断言要求 或者 prod 环境
    // 则进入上面代码

    const newModule = new Module(rawModule, runtime) // ---------------------------------------- 剖析 1
    // 创立一个 module 实例
    // module
      // 实例属性 runtime, _children, _rawModule, state
      // 原型属性  getChild,addChild,namespaced,update,forEachGetter ... 等

    if (path.length === 0) {
      this.root = newModule
      // 数组长度是 0,就 new Module 实例赋值给 root 属性
    } else {
      // 问题:什么时候 path.length !== 0
      // 答案:上面会判断是否有 modules 属性,当存在 modules 属性时,会再次调用 register,此时长度不为 0

      const parent = this.get(path.slice(0, -1)) // -------------------------------------------- 剖析 2
      // parent:获取父 module

      parent.addChild(path[path.length - 1], newModule) // ------------------------------------- 剖析 3
      // 给父 module._children 对象中增加 key 和 value,即父 modlue 的子 module
    }

    if (rawModule.modules) {
      // 如果该 module 中存在 modules 对象,就遍历 modules
      forEachValue(rawModule.modules, (rawChildModule, key) => { // --------------------------- 剖析 4
        this.register(path.concat(key), rawChildModule, runtime)
        // path.concat(key) => 相似 [module1],[module2],留神 concat 不会扭转 path
        // rawChildModule   => module1 module2
      })
    }
  }
  • 剖析 1

    // 文件目录 src/module/module.js
    
    export default class Module {constructor (rawModule, runtime) {
      this.runtime = runtime
      // Store some children item
      this._children = Object.create(null)
      // _children 对象
        // 用来寄存 modules 属性中的所有子 moudle
        // key => moudle 的名字
        // value => module 对象,外面可能有 state,mutations,actions,getters 等
    
      // Store the origin module object which passed by programmer
      this._rawModule = rawModule
      // 以后模块
    
      const rawState = rawModule.state
    
      // Store the origin module's state
      this.state = (typeof rawState === 'function' ? rawState() : rawState) || {}
      // state
        // 函数:就调用,返回 stated 对象
        // 对象:间接赋值
    }
    
     addChild (key, module) {this._children[key] = module
    }
    
    getChild (key) {return this._children[key]
    }
    }
  • 剖析 2

    get (path) {// 当 path 是空数组时,[].reducer((a,b) => a.getChild(key), this.root) 返回 root
      return path.reduce((module, key) => {return module.getChild(key)
      }, this.root)
    }
  • 剖析 3

    addChild (key, module) {this._children[key] = module
      // 给 module 实例的_children 属性对象中增加映射
      // key => moudle 名
      // value => module 对象
    }
  • 剖析 4

    export function forEachValue (obj, fn) {Object.keys(obj).forEach(key => fn(obj[key], key))
    // 遍历 obj
      // fn(value, key)
      // 将 obj 中的 key 作为第二个参数
      // 将 obj 中的 value 作为第一个参数
    }

(2) Store 类的 constructor

Store 类的构造函数 - src/stroe.js
---

export class Store {constructor (options = {}) {
    // Auto install if it is not done yet and `window` has `Vue`.
    // To allow users to avoid auto-installation in some cases,
    // this code should be placed here. See #731
    if (!Vue && typeof window !== 'undefined' && window.Vue) {install(window.Vue)
      // 如果 Vue 不存在 并且 window 存在 并且 window.Vue 存在,就主动装置
    }

    if (__DEV__) {assert(Vue, `must call Vue.use(Vuex) before creating a store instance.`)
      assert(typeof Promise !== 'undefined', `vuex requires a Promise polyfill in this browser.`)
      assert(this instanceof Store, `store must be called with the new operator.`)
      // 断言
        // Vue.use(Vuex) 肯定要在 new Vuex.store() 前调用
        // 是否存在 promise,因为 vuex 依赖 promise
        // Store 必须通过 new 命令调用
    }

    const {plugins = [], // 解构赋值 plugins 和 strict,并赋默认值
      strict = false
    } = options

    // store internal state
    this._committing = false  
    // _committing
      // 标记位,默认是 false
      // 批改 state 都会用 _withCommit 函数包装,在执行批改 state 函数中,this._committing=true, 批改后置为 false
      // 用来判断是否通过 mutation 函数批改 state,只有通过 mutation 批改 state 才是非法的
      // 比方在合并 state 的操作中会用到
  
    this._actions = Object.create(null)
    // _actions
      // 寄存所有 moudle 的 action
      // {//   cart/addProductToCart: [ƒ], f 指的是 wrappedActionHandler (payload) 
      //   cart/checkout: [ƒ],
      //   products/getAllProducts: [],
      // }
  
    this._actionSubscribers = []
    // _actionSubscribers
      // 收集 action 监听函数
      // 留神辨别:// this._subscribers = [] mutation 监听函数
  

    this._mutations = Object.create(null)
    // _mutations
      // 寄存所有 moudle 的 mutation
      // {//   cart/incrementItemQuantity: [ƒ], f 指的是 wrappedMutationHandler
      //   cart/pushProductToCart: [ƒ],
      //   cart/setCartItems: [ƒ],
      //   cart/setCheckoutStatus: [ƒ],
      //   products/decrementProductInventory: [ƒ],
      //   products/setProducts: [ƒ],
      // }
  
    this._wrappedGetters = Object.create(null)
    // _wrappedGetters
      // 寄存所有 module 中的 getter
      // {//   cart/cartProducts: ƒ wrappedGetter(store)
      //   cart/cartTotalPrice: ƒ wrappedGetter(store)
      // }
      // 留神:!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!// 1. this._wrappedGetters 在 resetStoreVM(this, state) 会用到 
        // 2. 留神辨别 (store.getters) 和 (store._wrappedGetters)

    this._modules = new ModuleCollection(options)
    // _modules
      // ModuleCollection 类次要就是收集所有的 moudle
      // {
      //   root: {
      //     runtime: false,
      //     state: {}, // 留神此时还没有合并 state
      //     _children: { // 把 moudules 中的 module 放入父模块的 _children 属性中
      //       cart: Module,
      //       products: Module,
      //     },
      //     _rawModule: 根 module
      //   }
      // }

    this._modulesNamespaceMap = Object.create(null)
    // _modulesNamespaceMap
      // namespace 和 mdule 的一一映射对象
      // {
      //   cart/: Module 
      //   products/: Module
      // }

    this._subscribers = []
    // _subscribers
      // mutation 监听函数
      // 留神辨别:// this._actionSubscribers = [] action 监听函数
  
    this._watcherVM = new Vue()
    this._makeLocalGettersCache = Object.create(null)

    // bind commit and dispatch to self
    const store = this
    const {dispatch, commit} = this
    this.dispatch = function boundDispatch (type, payload) {return dispatch.call(store, type, payload)
      // 绑定 dispatch 函数的 this 到 store 实例上
    }
    this.commit = function boundCommit (type, payload, options) {return commit.call(store, type, payload, options)
       // 绑定 commit 函数的 this 到 store 实例上
    }

    // strict mode
    this.strict = strict
    // 严格模式
      // 默认是 flase
      // 严格模式下,只能通过 mutation 批改 state
      // 在生产环境中倡议敞开,因为严格模式会深度监测状态树来检测不合规的状态变更,有性能损耗

    const state = this._modules.root.state
    // 根 state

    // init root module.
    // this also recursively registers all sub-modules
    // and collects all module getters inside this._wrappedGetters
    installModule(this, state, [], this._modules.root)  // --------------------------------- 上面会剖析

    // initialize the store vm, which is responsible for the reactivity
    // (also registers _wrappedGetters as computed properties)
    resetStoreVM(this, state) // ----------------------------------------------------------- 上面会剖析

    // apply plugins
    plugins.forEach(plugin => plugin(this))
    // 循环遍历插件,传入 stroe

    const useDevtools = options.devtools !== undefined ? options.devtools : Vue.config.devtools
    // 是否存在:传入 new Vuex.stroe(options) 中的 options 中存在 devtools
    // 存在:options.devtools
    // 不存在:Vue.config.devtools

    if (useDevtools) {devtoolPlugin(this)
    }
  }
}

(3) installModule(this, state, [], this._modules.root)

function installModule (store, rootState, path, module, hot) {
  const isRoot = !path.length
  // 当 path 数组长度是 0,则是根 module

  const namespace = store._modules.getNamespace(path)
  // 获取 namespace => module 名 + /
  //                => 或者 ''

  // register in namespace map
  if (module.namespaced) {
    // module.namespaced
      // 每个 module 能够有 namespaced 属性,是一个布尔值
      // 示意开启部分 module 命名
      // 命名空间官网介绍 (https://vuex.vuejs.org/zh/guide/modules.html)

    if (store._modulesNamespaceMap[namespace] && __DEV__) {console.error(`[vuex] duplicate namespace ${namespace} for the namespaced module ${path.join('/')}`)
      // 反复了
    }
    store._modulesNamespaceMap[namespace] = module
    // _modulesNamespaceMap
      // 建设 module 和 nameSpace 的映射关系
      // key : namespace
      // vlaue: module
        // {
        //   cart/: Module 
        //   products/: Module
        // }
  }

  // set state
  if (!isRoot && !hot) {
    // 不是根模块 并且 hot 为 flase,才会进入判断
  
    const parentState = getNestedState(rootState, path.slice(0, -1))
    // parentState
    // 获取该模块的
    const moduleName = path[path.length - 1]
    store._withCommit(() => {if (__DEV__) {if (moduleName in parentState) {
          console.warn(`[vuex] state field "${moduleName}" was overridden by a module with the same name at "${path.join('.')}"`
          )
        }
      }
      Vue.set(parentState, moduleName, module.state)
      // 合并所有 modules 中的 state 到 rootState
    })
  }

  const local = module.context = makeLocalContext(store, namespace, path)
  // 申明 module.context 并赋值
  // local
    // dispatch
    // commit
    // getters
    // state

  module.forEachMutation((mutation, key) => {
    const namespacedType = namespace + key
    registerMutation(store, namespacedType, mutation, local)
    // 把所有 modules 中的每一个 mutations 中的的 mutation 函数增加到 _mutations 对象上
    // _mutations 对象如下的格局
    // {//   cart/incrementItemQuantity: [ƒ], f 指的是 wrappedMutationHandler
    //   cart/pushProductToCart: [ƒ],
    //   cart/setCartItems: [ƒ],
    //   cart/setCheckoutStatus: [ƒ],
    //   products/setProducts: [f],
    // }
  })

  module.forEachAction((action, key) => {
    const type = action.root ? key : namespace + key
    const handler = action.handler || action
    registerAction(store, type, handler, local)
    // 把所有 module 中的每一个 actions 中的的 action 函数增加到 _actions 对象上
    // _actions 对象如下的格局
    // {//   cart/addProductToCart: [ƒ], f 指的是 wrappedActionHandler (payload) 
    //   cart/checkout: [ƒ]
    //   products/getAllProducts: []
    // }
  })

  module.forEachGetter((getter, key) => {
    const namespacedType = namespace + key
    registerGetter(store, namespacedType, getter, local)
    // 把所有 modul 中的每一个 getter 函数增加到 _wrappedGetters 对象上
    // 寄存所有 module 中的 getter
    // {//   cart/cartProducts: ƒ wrappedGetter(store)
    //   cart/cartTotalPrice: ƒ wrappedGetter(store)
    // }
  })

  module.forEachChild((child, key) => {installModule(store, rootState, path.concat(key), child, hot)
    // 循环遍历 module._children 对象,并在每次循环中执行 installModule 办法
  })
}

(4) resetStoreVM(this, this.state, hot)

function resetStoreVM (store, state, hot) {
  // resetStoreVM
    // 参数
      // store
      // state
      // hot

  const oldVm = store._vm
  // oldVm 缓存旧的 store._vm

  // bind store public getters
  store.getters = {}
  // 在 store 实例上增加 getters 属性对象,初始值是一个空对象
  // 留神:// 1. 辨别 store._wrappedGetters 和 store.getters

  // reset local getters cache
  store._makeLocalGettersCache = Object.create(null)

  const wrappedGetters = store._wrappedGetters
  // wrappedGetters
    // 缓存 store._wrappedGetters

  const computed = {}
  // 申明 computed 变量

  forEachValue(wrappedGetters, (fn, key) => {
    // 循环 wrappedGetters,将 value 和 key 作为参数传入 forEachValue 的第二个参数函数

    computed[key] = partial(fn, store)
    // 1. partial 是这样一个函数
      // function partial (fn, arg) {//   return function () {//     return fn(arg)
      //   }
      // }
    // 2. 即 computed[key] = () => fn(store) 
      // fn 就是具体的 getter 函数

    Object.defineProperty(store.getters, key, {get: () => store._vm[key],
      enumerable: true // 可枚举
    })
    // 1. 给 store.getters 对象增加 key 属性
    // 2. 拜访 stroe.getters[key] = store._vm[key]
      // 即拜访 store.getter.aaaa 相当于拜访 store._vm.aaaa
  })

  // use a Vue instance to store the state tree
  // suppress warnings just in case the user has added
  // some funky global mixins
  const silent = Vue.config.silent
  // 缓存 Vue.config.silent

  Vue.config.silent = true
  //  开启勾销正告
  // 勾销 Vue 所有的日志与正告,即在 new Vue() 时不会有正告

  store._vm = new Vue({
    data: {?state: state // 11. 将传入的 state 赋值给 data 中的 ?state 属性},
    computed // 22. 将 computed 变狼赋值给 Vue 中的 computed 属性 (computed[key] = () => fn(store))
  })
  // store._vm = new Vue(...)
    // 通过下面 1122 使得 state 和 () => fn(store) 具备响应式
  
  Vue.config.silent = silent
  // 敞开勾销正告

  // enable strict mode for new vm
  if (store.strict) {enableStrictMode(store)
    // 使能严格模式,保障批改 store 只能通过 mutation
  }

  if (oldVm) {if (hot) {
      // dispatch changes in all subscribed watchers
      // to force getter re-evaluation for hot reloading.
      store._withCommit(() => {
        oldVm._data.?state = null
        // 解除援用
      })
    }
    Vue.nextTick(() => oldVm.$destroy())
    // dom 更新后,捣毁 vue 实例
      // const oldVm = store._vm
      // store._vm = new Vue()}
}

(5) commit

commit (_type, _payload, _options) {
    // check object-style commit
    const {
      type,
      payload,
      options
    } = unifyObjectStyle(_type, _payload, _options)
    // 结构 commit 须要的参数类型
    // 1. store.commit('increment', 10)
    // 2. store.commit('increment', {amount: 10}) 
    // 3. store.commit({type: 'increment',amount: 10})
    // 就是将第 3 种状况结构成第 2 种的状况

    const mutation = {type, payload}

    const entry = this._mutations[type]
    // entry 找到须要提交的 mutation 函数组成的数组,是一个数组,数组种就是包装过后的 mutation handle 函数

    if (!entry) {
      // 没找到该 mutation
      if (__DEV__) {console.error(`[vuex] unknown mutation type: ${type}`)
      }
      return
    }

    this._withCommit(() => {
      // this._withCommit 保障批改 state 是非法伎俩,即 this._committing 在批改是是 true
      entry.forEach(function commitIterator (handler) {handler(payload)
        // 传入参数执行 mutation handle 函数
      })
    })

    this._subscribers
      .slice() // shallow copy to prevent iterator invalidation if subscriber synchronously calls unsubscribe
      .forEach(sub => sub(mutation, this.state))
      // 浅拷贝 this._subscribers 而后遍历该数组,调用外面的 subscribe 函数 
      // 即更改 state 后须要响应视图等

    if (
      __DEV__ &&
      options && options.silent
    ) {
      console.warn(`[vuex] mutation type: ${type}. Silent option has been removed. ` +
        'Use the filter functionality in the vue-devtools'
      )
    }
  }

(6) dispatch

dispatch (_type, _payload) {
    // check object-style dispatch
    const {
      type,
      payload
    } = unifyObjectStyle(_type, _payload)
    // 结构 commit 须要的参数类型
    // 1. store.dispatch('increment')
    // 2. store.dispatch('incrementAsync', {amount: 10})
    // 3. store.dispatch({type: 'incrementAsync', amount: 10})
    // 就是将第 3 种状况结构成第 2 种的状况

    const action = {type, payload}
    const entry = this._actions[type]
    if (!entry) {
      // 没找到该 action
      if (__DEV__) {console.error(`[vuex] unknown action type: ${type}`)
      }
      return
    }

    try {
      this._actionSubscribers
        .slice() // shallow copy to prevent iterator invalidation if subscriber synchronously calls unsubscribe
        .filter(sub => sub.before) 
        .forEach(sub => sub.before(action, this.state))
        // before 钩子 action 监听函数
        // before 示意订阅处理函数的被调用机会应该在一个 action 散发之前调用
    } catch (e) {if (__DEV__) {console.warn(`[vuex] error in before action subscribers: `)
        console.error(e)
      }
    }

    const result = entry.length > 1
      ? Promise.all(entry.map(handler => handler(payload)))
      : entry[0](payload)
    // 长度大于 1,promise.all() 保障 result 所有 resolve
    // 长度小于等于 1,间接调用

    return new Promise((resolve, reject) => {
      result.then(res => {
        try {
          this._actionSubscribers
            .filter(sub => sub.after)
            .forEach(sub => sub.after(action, this.state))
          // after 示意订阅处理函数的被调用机会应该在一个 action 散发之后调用
        } catch (e) {if (__DEV__) {console.warn(`[vuex] error in after action subscribers: `)
            console.error(e)
          }
        }
        resolve(res)
        // resolve 最终后果
      }, error => {
        try {
          this._actionSubscribers
            .filter(sub => sub.error)
            .forEach(sub => sub.error(action, this.state, error))
        } catch (e) {if (__DEV__) {console.warn(`[vuex] error in error action subscribers: `)
            console.error(e)
          }
        }
        reject(error)
      })
    })
  }

(7) mapstate

  • 首先是 mapstate 如何应用

     官网的例子
    
    computed: {
    ...mapState('some/nested/module', {
      a: state => state.a,
      b: state => state.b
    })
    }
    
    当映射的计算属性的名称与 state 的子节点名称雷同时,咱们也能够给 mapState 传一个字符串数组
    computed: mapState([
    // 映射 this.count 为 store.state.count
    'count'
    ])
  • 源码

    • 把 state 结构成 computed 对象返回
    • 依据 namespace 把 (部分的 state 和 getter 作为参数) 传给传入的参数对象的 (属性函数)
    export const mapState = normalizeNamespace((namespace, states) => {
    // normalizeNamespace
      // 返回改装参数后的,f(namespace, states)
      // 改成上面的参数模式
        // ...mapState('some/nested/module', {
        //   a: state => state.a,
        //   b: state => state.b
        // })
    const res = {}
    if (__DEV__ && !isValidMap(states)) {
      // 如果是 dev 环境 并且 states 不是是一个对象或者一个数组
      // 报错
    
      // function isValidMap (map) {//   return Array.isArray(map) || isObject(map)
      // }
      console.error('[vuex] mapState: mapper parameter must be either an Array or an Object')
    }
    
    normalizeMap(states).forEach(({key, val}) => {// 1. 如果 states 是数组,返回一个数组,每个成员是一个对象 ({ key: key, val: key})
    // 2. 如果 states 是对象,返回一个数组,每个成员是一个对象 ({key:key, val: map[key] })
      res[key] = function mappedState () {
        let state = this.$store.state
        let getters = this.$store.getters
        if (namespace) {const module = getModuleByNamespace(this.$store, 'mapState', namespace)
          // module
            // 在 (store._modulesNamespaceMap) 对象中找到 (参数 namespace) 对应的 (module)
    
          if (!module) {return}
    
          state = module.context.state // 获取该 module 种的部分 state
          getters = module.context.getters // 获取该 module 种的部分 getters
        }
        return typeof val === 'function'
          ? val.call(this, state, getters)
          : state[val]
        // val 是一个函数,就调用函数 val.call(this, state, getters) 返回
        // val 不是函数,就间接返回 state[val]
      }
      // mark vuex getter for devtools
      res[key].vuex = true
    })
    return res
    // 最初返回 res 对象
    // res 对象会作为组件的 computed
    })
    
------

function normalizeNamespace (fn) {return (namespace, map) => {if (typeof namespace !== 'string') {
      // 如果 namespace 不是一个字符串
        // 阐明传入只传入了一个参数
        // 就把 namespace='',把第一个不是字符串的参数赋值给第二个参数
      map = namespace
      namespace = ''} else if (namespace.charAt(namespace.length - 1) !=='/') {
      namespace += '/'
      // 没有 / 则增加
    }
    return fn(namespace, map)
    // 返回转换参数过后的 fn
  }
}
------

function getModuleByNamespace (store, helper, namespace) {// 1. getModuleByNamespace(this.$store, 'mapState', namespace)

  const module = store._modulesNamespaceMap[namespace]
  // 找到 namespace 对应的 module
  if (__DEV__ && !module) {console.error(`[vuex] module namespace not found in ${helper}(): ${namespace}`)
  }
  return module
  // 返回 module
}
------

function normalizeMap (map) {if (!isValidMap(map)) {return []
    // 不是数组或者对象,默认返回一个数组
  }
  return Array.isArray(map)
    ? map.map(key => ({ key, val: key}))
    : Object.keys(map).map(key => ({ key, val: map[key] }))
  // 1. 如果是数组,返回一个数组,每个成员是一个对象 ({key: key, val: key})
  // 2. 如果是对象,返回一个数组,每个成员是一个对象 ({key:key, val: map[key] })
}

应用中的留神点

mapState – (带 namespace 和不带 namespace)

mapGetters

mapMutations

mapActions

  • ui 组件中

    <template>
    <div class="vuex">
      <div>
        <div style="font-size: 30px">vuex</div>
        <div>dispatch 一个 action => store.dispatch({type: 'actionName', payload: ''})</div>
        <div>commit 一个 mutation => store.dispatch({type: 'actionName', payload: ''})</div>
       
        <div>------</div>
        <button @click="changeCount"> 点击批改 vuexModule 中的 count+1 </button>
        <div>{{moduleState.count}}</div>
    
        <div>------</div>
        <div><button @click="getName"> 点击,发动申请,获取 name 数据,利用 Vuex actions - 不传参数 </button></div>
        <div><button @click="getName2"> 点击,发动申请,获取 name 数据,利用 Vuex actions - 传参 </button></div>
        <div>{{moduleState.name}}</div>
      </div>
    </div>
    </template>
    
    <script>
    import {mapState, mapGetters, mapMutations, mapActions} from "vuex";
    
    export default {
    name: "vuex",
    data() {return {};
    },
    computed: {
      ...mapState({
        rootState: state => {   // --------------- 命名为 rootState
          return state; // ----------------------- 这里的 state 是 rootMoudule 的 state
        }
      }),
      ...mapState("vuexModule", { // ------------ namespace
        moduleState: state => { // -------------- 命名为 moduleState
          return state; // ---------------------- 这里的 state 是 moudles 中 vuexModule 的 state
        }
      }),
      ...mapGetters("vuexModule", { // ---------- 第二个参数是对象,即能够批改 getter 的名字
        changeGetterName: "square"
      }),
      ...mapGetters("vuexModule", ['square']), // 第二个参数是数组
    },
    methods: {
      ...mapMutations('vuexModule', {addCountChangeName: 'AddCount' // ------- 对象形式,能够批改 mutation 的名字}),
      ...mapActions('vuexModule', ['getData', 'getData2']), // mapActions
      changeCount() {this.addCountChangeName(1) // ----------- 参数将作为 mutation(state, payload) 的 payload
      },
      getName() {this.getData() // ----------------------- 不传参给 action 函数,解决异步
      },
      getName2() {
        this.getData2({ // ----------------------- 传参给 action 函数,解决异步
          url: "/home",
          method: "get"
        })
      }
    },
    mounted() {
      console.log(
        this.rootState.vuexModule.count,
        "没有 namespace 的 mapState 获取的 state - 拜访 coount"
      );
      console.log(
        this.moduleState.count,
        "具备 namespace 的 mapState 获取的 state - 拜访 count"
      );
      console.log(this.changeGetterName, 'mapGetters 第二个参数是对象');
      console.log(this.square, 'mapGetters 第二个参数是数组');
    }
    };
    </script>
    
  • store/vuexModule

    import {getName} from '../../api/home'
    
    const vuex = {
    namespaced: true,
    state() {
      return {
        count: 2,
        name: 'init name'
      }
    },
    getters: {square(state, getters, rootState, rootGetters) {console.log(state, 'state')
        console.log(getters, 'getters')
        console.log(rootState, 'rootState')
        console.log(rootGetters, 'rootGetters')
        return (state.count * rootState.rootCount)
      }
    },
    mutations: {AddCount(state, payload) {state.count = state.count + payload},
      getNameData(state, payload) {state.name = payload}
    },
    actions: {async getData(context) { // dispatch 不穿参给 action 函数
        const {commit} = context
        const res = await getName({
          url: "/home",
          method: "get"
        });
        if (res && res.data && res.data.name) {console.log(res.data.name, '2222')
          commit('getNameData', res.data.name)
        }
      },
      async getData2(context, payload) { 
        // dispatch 穿参给 action 函数
        // action(context, payload)
          // 第一个参数:context 和 store 具备雷同的 api
          // 第二个参数:payload 是 dispatch 一个 action 穿过来的参数
        const {commit} = context
        const res = await getName(payload);
        // const res = await getName({
        //   url: "/home",
        //   method: "get"
        // });
        if (res && res.data && res.data.name) {console.log(res.data.name, '2222')
          commit('getNameData', res.data.name)
        }
      },
    }
    };
    
    export default vuex

材料

vuex 官网文档 https://vuex.vuejs.org/zh/

川神 较全面 https://juejin.im/post/684490…
2.3.1 版本 Vuex, 过程具体:https://juejin.im/post/684490…
yck https://juejin.im/post/684490…

正文完
 0