导航

[[深刻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 = falseVue.prototype.$axios = axiosVue.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.jsimport { 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 = 1export function toArray(list: any, start?: number): Array<any> {start = start || 0let i = list.length - startconst 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, // -------------------------------------- 注入storerender: 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 installexport 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不存在,就赋值传入的参数_VueapplyMixin(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.jsexport 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.jsexport 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...