首页 > 编程语言 >vuex源码分析

vuex源码分析

时间:2022-09-06 16:58:58浏览次数:76  
标签:分析 ... const module state 源码 ._ vuex store

   

什么是vuex

是一个专为Vue.js应用程序开发的状态管理模式。 什么是状态管理模式,vue 根据 data的变化会渲染模板,vuex则是把一些数据集中进行管理方便在vue 组件中使用。 官方文档例子:
  • state,驱动应用的数据源(相当于vue的data);
  • view,将 state 映射到视图(template);
  • actions,在 view 上的用户输入导致的状态变化(state)。
new Vue({
  // state
  data () {
    return {
      count: 0
    }
  },
  // view
  template: `
    <div>{{ count }}</div>
  `,
  // actions
  methods: {
    increment () {
      this.count++
    }
  }
})
  这个例子的数据流简图: vuex解决的痛点是大型单页应用中状态依赖,1.嵌套组件依赖同个状态;2.兄弟组件状态依赖。 比如嵌套组件依赖某个状态state,没有vuex我们就必须不断从父组件传参到子组件,这样就很繁琐也很容易出错;兄弟组件状态依赖如果还是依照老方法就很难解决了。 如果只是这么简单的单向数据流模式其实不需要用到vuex我们可以构建一个最简单的store.   一个最简单的store模式:
var store = {
  debug: true,
  state: {
    message: 'Hello!'
  },
  setMessageAction (newValue) {
    if (this.debug) console.log('setMessageAction triggered with', newValue)
    this.state.message = newValue
  },
  clearMessageAction () {
    if (this.debug) console.log('clearMessageAction triggered')
    this.state.message = ''
  }
}


//依赖store的组件,store中state的变化只能使用store.setMessageAction和store.clearMessageAction
var vmA = new Vue({
  data: {
    privateState: {},
    sharedState: store.state
  }
})

var vmB = new Vue({
  data: {
    privateState: {},
    sharedState: store.state
  }
})
但是如果遇到的是组件更多更复杂的大型应用,简单的store模式就不适用了。 2.一个最简单的vuex   vuex官方示意图:   既然要在vue组件中使用vuex,就必须初始化以及注入。

注入Vue

我们先看看如何注入Vue 注入vue实例代码:
import Vue from 'vue'
import Vuex from 'vuex'

// install Vuex框架
Vue.use(Vuex)


const store = new vuex({
xxx
})
// 创建并导出store对象。
export default store

new vue({
 store
})
要实现注入首先要在vuex实现install函数或方法,install首先要判断是否已经有vue,代码如下:

let Vue

function install(_vue) {
  if(Vue &&_vue === Vue) {
  return error
  }
  Vue = _vue
  
  applyMixin(Vue)
}


function applyMixin(Vue) {
  const version = Number(Vue.version.split('.')[0])
    if (version >= 2) {
        Vue.mixin({beforeCreate: vuexInit})
    }


    function vuexInit() {
        const options = this.$options
        if (options.store) {
            this.$store = typeof options.store === 'function' ? options.store() : options.store
        } else if(options.parent && options.parent.store) {
            this.$store = options.parent.store
        }
    }
}
let Vue 是全局变量,如果 Vue 变量为undefined,那么局部变量_vue赋值给全局变量Vue,然后通过applyMixinstore挂载在vue实例上。 ApplyMixin 函数在beforeCreate 周期检查new vue(option)的options是否有store,有就挂载在根组件上,并将其他子组件通过options.parent 建立一个根组件路径,一步步往上找到根组件store。这样就达成了vue实例只有一个store对象。 页面结构图: store流向图:  
const store = new Vuex({
state:{
xxx
},
getters:{
xxxxx
},
mutations:{
xxx
},
actions:{
xxxx
}
})
  vuex初始化,一般会传入下面的属性:
const store = new Vuex({
state:{
xxx
},
getters:{
xxxxx
},
mutations:{
xxx
},
actions:{
xxxx
},
modules:{
}
})
我们首先不考虑module 模块化,只单独考虑一个最简单的store。

一个最简单store

在store的构造函数会先生成_actions,_mutations,_wrappedGetters这些对象用来存储传入的actions,mutations和getters。 构造函数代码:
class store {
   constructor() {
   
   // store internal statethis._committing = false // 是否在进行提交状态标识this._actions = Object.create(null) // acitons操作对象this._mutations = Object.create(null) // mutations操作对象this._wrappedGetters = Object.create(null) // 封装后的getters集合对象this._modules = new ModuleCollection(options) // Vuex支持store分模块传入,存储分析后的modulesthis._modulesNamespaceMap = Object.create(null) // 模块命名空间mapthis._subscribers = [] // 订阅函数集合,Vuex提供了subscribe功能this._watcherVM = new Vue() // Vue组件用于watch监视变化
   }
    const store = this
    const { dispatch, commit } = this
    this.dispatch = function boundDispatch (type, payload) {
      return dispatch.call(store, type, payload)
    }
    this.commit = function boundCommit (type, payload, options) {
      return commit.call(store, type, payload, options)
    }
}
  要修改state在store就只能通过commitdispatch,这里不具体讲commitdispatch 是怎么写得,来讲讲怎么做到只通过 commitdispatch来对 state操作,在vue中直接对state数据操作会报错。源码中constructor 函数中定义this._committing = false 这个_committing为修改state的标志,每次修改都会触发_committing 变化。 在commit 函数中,使用_withCommiting 包裹触发的函数:
commit(_type,_payload,_options) {
   ....
  this._withCommiting(() =>{
    entry.forEach( (handler) => handler(payload))
  })

}
_withCommiting函数代码:
_withCommiting(fn) {
        const commiting = this._commiting
        this._commiting = true

        fn()
        this._commiting  = commiting
    }
_withCommiting接受一个函数fn(实际就是commit调用的函数)为参数,每次commit ,_withCommiting中使用记录修改前状态,fn()运行完毕后,然后恢复之前的_commiting状态。 每次运行commit函数都会重复上面的步骤,必须通过 _withCommiting函数,_withCommiting 函数每次都会触发 committing 状态。 但是直接修改state就不会触发_commiting 那么如何监测有没有触发呢?这个问题先留着。 到这里store.commit 函数还未完成,回到constructor 代码可以看到commitdispatch 还要使用commit.call(store) 绑定到store 上,原因是什么?
...
...
function constructor(){
   ...
   ...
   ...
     const store = this
    const { dispatch, commit } = this
    this.dispatch = function boundDispatch (type, payload) {
      return dispatch.call(store, type, payload)
    }
    this.commit = function boundCommit (type, payload, options) {
      return commit.call(store, type, payload, options)
    }

}
看看commit 代码:
....
....
class store{
...
...
commit (_type, _payload, _options) {
   
    ....
    const mutation = { type, payload }
    const entry = this._mutations[type]
    
    this._withCommit(() => {
      entry.forEach(function commitIterator (handler) {
        handler(payload)
      })
    })
  }
}
  我们每次使用commit('xxx',xxx) 形式调用时,函数其实都是从this._mutations[type] 找到对应函数,也就是说需要用到this,我么知道js中的this很灵活也非常容易丢失,如果遇到this 丢失的情况,commit 就会报错。 我们看看todoMVC示例代码:
   .....
  addTodo ({ commit }, text) {
    commit('addTodo', {
      text,
      done: false
    })
  },
commit 函数是通过解构拿到的,此时的commit的就只是个函数,而不是store.commit 。 上面的解构效果相当于下面代码:
//{commit}
let commit =store.commit
  如果没有在constructor中强制绑定store那么addTodo 必然出错,无法执行下去。这也是为什么已经在class store 定义好了commitdispatch 但是还是要绑定的原因。  

模块收集

随着用到的状态越来越多vuex支持模块,来分割体积。每个模块都有state,gettersmutationsactions
const moduleA = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

const moduleB = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... }
}

const store = new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
})
vuex源码中构造函数会把传入的options 先传入到moduleCollection(option) 生成嵌套module,没有modules key值就是根模块,有的就是子模块。
class store {
   constructor(options={}) {
   this._modules = new ModuleCollection(options)
   } 
}
moduleCollection基本逻辑: 1.首先在constructor调用register迭代注册完模块 2.使用get方法获取父模块
/**
* path:Array
* rawModule:root / modules
*/
class ModuleCollection {
    constructor(options = {}){
        ...
        this.register([],options)
    }
 register(path,rawModule) {
     const newModule =  new Module(rawModule)
     if(path.length === 0) {
         this.root = rawModule
     } else{
        const parent =this.get(path.slice(0,-1))
        parent.addChild(path[path.length-1],newModule)
     }
    
     if(rawModule.modules) {
            forEachValue(rawModule.modules,(rawChildModule,key)=>{
                this.register(path.concat(key),rawChildModule)
            })
        }
}



    get (path) {
      return path.reduce((module,key) =>{
          return module.getChild(key)
      },this.root)
    }
    
}
这里很巧妙的使用递归方式模块化,逻辑上:1.判断是否是根模块,是的话立即生成模块2.不是根模块,那么就通过空数组 path 不断向 path 添加模块名,这样在添加子模块时方便找到父模块,建立正确关系图。 1.首先path是用来保存module name的数组 2.在register 函数判断是否是子模块, 3.不是就 通过方法this.register(path.concat(key),rawChildModule) 一边添加path数组一边注册子模块 4.当模块化完成我们得到一个模块化的store和一个可用来寻找模块的path数组(存有所有除根模块的模块名) 然后在注册子模块通过get来获取上一级模块也就是父模块,可以看看get是怎么实现通过path.reduce函数,将根模块(this.root)传入,迭代查询。
register() {
  //....
 // ...else{
        const parent =this.get(path.slice(0,-1))
        parent.addChild(path[path.length-1],newModule)
     }
}
 
 get (path) {
      return path.reduce((module,key) =>{
          return module.getChild(key)
      },this.root)
    }
  现在模块化完成我们该将所有模块,以及模块getters,actions,mutations全部注册。什么意思呢就是把所有的函数都塞进下面的存储对象,方便我们调用。 还有一个问题就是模块化之后像下面这样代码,参数state 指向的就是moduleA中的state,但是之前的getter,action,mutation 都是指向全局的state,现在我们应该怎么办呢?
const moduleA = {state: () => ({
    count: 0}),
  mutations: {increment (state) {// `state` is the local module state
      state.count++}},
  getters: {doubleCount (state) {return state.count * 2}}
}
所以现在我们要完成两件事1:注册好模块及其函数;2.传入参数state应该指向局部

注册模块

在vuex源码里 installModule 做完了这两件事。 代码:
function installModule (store, rootState, path, module, hot) {
  const isRoot = !path.length
  const namespace = store._modules.getNamespace(path)

  // register in namespace map
  if (module.namespaced) {
    if (store._modulesNamespaceMap[namespace] && __DEV__) {
      console.error(`[vuex] duplicate namespace ${namespace} for the namespaced module ${path.join('/')}`)
    }
    store._modulesNamespaceMap[namespace] = module
  }

  // set state
  if (!isRoot && !hot) {
    const parentState = getNestedState(rootState, path.slice(0, -1))
    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)
    })
  }
 
 //------分割线----------
 const local = module.context = makeLocalContext(store, namespace, path)
 module.forEachMutation((mutation, key) => {
    ...
    registerMutation(store, namespacedType, mutation, local)
  })

  module.forEachAction((action, key) => {
   ...
    registerAction(store, type, handler, local)
  })

  module.forEachGetter((getter, key) => {
  ....
    registerGetter(store, namespacedType, getter, local)
  })
  module.forEachChild((child, key) => {
    installModule(store, rootState, path.concat(key), child, hot)
  })
}
上面的代码我们以分割线为界分割线上是Vue.set(parentState, moduleName, module.state) 用这个方法将store中所有state转成一个state树。 这里有一个值得注意的地方 const local = module.context = makeLocalContext(store, namespace, path) 这行代码是什么意思?从字面看是创建局部context。 makeLocalContext 代码:

function installModule (store, rootState, path, module, hot) {
       ...
       ...
const local = module.context=makeLocalContext(store,namespace,path)
      ...
      ...
}
function  makeLocalContext(store,namespace,path) {
    const noNamespace =namespace === ''
    const local = {
        dispatch: noNamespace ? store.dispatch : (_type,_payload,_options) =>{
            let { type, payload ,options} = unifyObjectStyle(_type, _payload,_options)
            if (!options || !options.root) {
                type = namespace+type
            }
            return store.dispatch(type,payload)
        },
        commit: noNamespace ? store.commit : (_type,_payload,_options) =>{
            let { type, payload ,options} = unifyObjectStyle(_type, _payload,_options)
            if (!options || !options.root) {
                type = namespace+type
            }
            return store.commit(type,payload,options)
        },
       
    }
    
    Object.defineProperties(local, {
        getters:{
            get:noNamespace ? () =>store.getters :() => makeLocalGetter(store,namespace) 
        },
        state: {
          get: () => getNestedState(store.state, path)
        }
      })

    return local
}
可以看出makeLocalContext 通过传入的namespace 来判断全局还是局部,创建了类似store的context,这个context有着局部模块所有的一切state,getters,mutations,actions。 回到 installModule 代码,分割线下就是注册模块和模块内的getter,action,mutation 。 每个registerxxx 注册函数都将生成的local context传入其中。这样做就确保模块内的getter,action,mutation state都指向模块内的state。
module.forEachMutation((mutation, key) => {
    ...
    registerMutation(store, namespacedType, mutation, local)
  })

  module.forEachAction((action, key) => {
   ...
    registerAction(store, type, handler, local)
  })

  module.forEachGetter((getter, key) => {
  ....
    registerGetter(store, namespacedType, getter, local)
  })
  module.forEachChild((child, key) => {
    installModule(store, rootState, path.concat(key), child, hot)
  })
Vuex 文档示例代码:
const store = createStore({
  modules: {
    account: {
      namespaced: true,// 局部模块
      assetsstate: () => ({ ... }), 
      getters: {isAdmin () { ... } // -> getters['account/isAdmin']},
      actions: {login () { ... } // -> dispatch('account/login')},
      mutations: {login () { ... } // -> commit('account/login')},// nested mod
      
      modules: {
      // namespace继承父模块名
        myPage: {state: () => ({ ... }),
          getters: {profile () { ... } // -> getters['account/profile']}
          
          },}
          
})
account 模块 namespaced:true 那么模块下的getter等在调用上实际为 getters['account/isAdmin']},只不过vuex源码把细节实现了不需要开发者手动调用。  

辅助函数

mapStatemapGetter等系列函数,为组件创建的语法糖, 比如在组件可能需要将store的多个状态转成计算属性,mapState 帮助我们生成计算属性
import { mapState } from 'vuex'

export default {
  // ...
  computed: mapState({
    // 箭头函数可使代码更简练
    count: state => state.count,

    // 传字符串参数 'count' 等同于 `state => state.count`
    countAlias: 'count',

    // 为了能够使用 `this` 获取局部状态,必须使用常规函数
    countPlusLocalState (state) {
      return state.count + this.localCount
    }
  })
}
也可以使用对象展开运算符将mapState返回的对象混入到外部对象中。
computed: {
  localComputed () { /* ... */ },
  // 使用对象展开运算符将此对象混入到外部对象中
  ...mapState({
    // ...
  })
}
mapState为例, mapState(namespace?: string, map: Array<string> | Object<string | function>): Object 第一个参数是可选的,可以是一个命名空间字符串也可以是个函数。   用法一:
computed: {
  ...mapState({
    a: state => state.some.nested.module.a,
    b: state => state.some.nested.module.b
  })
},
methods: {
  ...mapActions([
    'some/nested/module/foo', // -> this['some/nested/module/foo']()
    'some/nested/module/bar' // -> this['some/nested/module/bar']()
  ])
}
用法二:
computed: {
  ...mapState('some/nested/module', {
    a: state => state.a,
    b: state => state.b
  })
},
methods: {
  ...mapActions('some/nested/module', [
    'foo', // -> this.foo()
    'bar' // -> this.bar()
  ])
}··

state和getter转化

store初始化后,其中定义state也得是个可响应的对象(reactive object),否则就没法影响组件的渲染。但是我们通过vuex初始化后的state 只是个普通对象。到目前为止无法做到state 数据改变时使用该state相应组件也会跟着变化。因此state必须要想办法把它转成reactive 对象,才能在vue组件中使用。 因为vue中的data属性是个reactive object,所以我们只要把vuexstate传入vuedata属性,不需要在vuex另写一套把state转成可响应的代码。 vuex中的getters也是同样道理,getters是依赖state的computed,那么我们也可以把getters做好处理后传入computed属性中。   vuex源码中resetStoreVM 就是在vuex 中生成一个vue实例_vm ,然后将stategetters 属性转换。   伪代码:


class Store {
constrcutor() {
    
}
const store = this    
//存储getters

this._wrapperGetters = Object.create(null)

function buildVM(store){

const wrapperGetters = store.wrapperGetters

 forEachValue(wrapperGetters,(fn,key) =>{
         /**将wrapperGetters转变成
         computed:{
             zzz(){
             return xxxx
             }
        */  }
        computed[key] = partial(fn,store)
        //把computed的getters通过get挂载在store.getters上
        Object.defineProperty(store.getters, key, {
            get: ()=>store._vm[key],
            enumerable: true
        })
    })
 store._vm= new vue({
    data:{
      $$state:store.state
    },
    computed
    })
}
enableStrictMode(store)
 get state() {
        return this._vm._data.$$state 
    }
    
    //将getters注册到this._wrapperGetters
    registerGetters() {
         ...
   }
}


 function forEachValue(obj,fn) {
    Object.keys(obj).forEach(key => fn(obj[key],key))
}
在 buildVM函数内使用new vue 创建一个vue新实例_vm ,将_vm 挂载在store上面store._vm= new vue(...)state 传入到 _vm 中的 data 属性中,这样就完成了store.state 的可响应化。 getters是依赖statecomputed property,是不是和vue中·computed属性一致呢,所以按照相同逻辑,将wrapperGetters 转成vue实例中computed 属性函数。 现在我们解决之前的问题就是只能通过commit 修改state,直接修改就会报错。
function enableStrictMode (store) {
  store._vm.$watch(function () { return this._data.$$state }, () => {
    if (__DEV__) {
      assert(store._committing, `do not mutate vuex store state outside mutation handlers.`)
    }
  }, { deep: true, sync: true })
}
之前说每次commit 都会改变_committing 值,所以在store._vm 创建后,使用$watch 来监测this._data.$$state 数据变化,如果$$state 数据变化了, 但是 _committing 值为false,那么就是直接修改state,就会报错了。  

插件和热重载

  vuex支持插件plugin 系统,plugin 系统的基本原理其实很简单,就是传入一个实例化的store 这样就可以调用store 里的各种参数和方法了。

plugin

代码:
const myPlugin = store => {
  // 当 store 初始化后调用
  store.subscribe((mutation, state) => {
    // 每次 mutation 之后调用
    // mutation 的格式为 { type, payload }
  })
}
在插件中是也不能直接修改状态,只能通过mutationaction修改。通过提交 mutation,插件可以用来同步数据源到 store。
/index.js
const plugin = createWebSocketPlugin(socket)

const store = new Vuex.Store({
  state,
  mutations,
  plugins: [plugin]
})
plugin函数中可以使用store.subscribestore.subscribeAction用来订阅mutationsactions 。 可以看看subscribe官方文档解释:
订阅 store 的 mutation。handler 会在每个 mutation 完成后调用,接收 mutation 和经过 mutation 。
  1.subscribe 以subscribe为例,首先把handle保存在store实例中,subscribe系列函数返回一个取消订阅函数。
function subscribe(handle) {
  this.subscriber.push(handle)
  
  return ()=>{
  this.subscriber.splice(handle,0)
  }
}
2.调用方法
store.subscribe((mutation, state) => {
  console.log(mutation.type)
  console.log(mutation.payload)
})
handler 会在每个 mutation 完成后调用,接收 mutation 和经过 mutation 后的状态作为参数,因此handler调用是和commit息息相关的,mutation和handler顺序不能反,否则handler无法接受到mutation和经过 mutation 后的状态为参数。
commit() {
  this.withCommiting(()=>{
    this.mutation[xxx]()
    this.store.subscribers.forEach((sub)=>sub())
  })
}
  3.取消订阅 subscribe 方法会返回一个 unsubscribe 函数,当不再需要订阅时应该调用该函数。例如,你可能会订阅一个 Vuex 模块,当你取消注册该模块时取消订阅。或者你可能从一个 Vue 组件内部调用 subscribe,然后不久就会销毁该组件。在这些情况下,你应该记得手动取消订阅。
const unscribe= store.subscribe(()=>{xxxx})
//取消订阅
unscribe()

hotreload

hotreload不是vuex本身具备的功能而是webpack提供。 1.通过module.hot来判断是否支持热重载 2.然后通过module.hot.accept(xx)jianggetters,mutations,actions当成模块载入, 然后提取需要热重载的模块 3.store.hotUpdate加载新模块
// store.js
import Vue from 'vue'
import Vuex from 'vuex'
import mutations from './mutations'
import moduleA from './modules/a'

Vue.use(Vuex)

const state = { ... }

const store = new Vuex.Store({
  state,
  mutations,
  modules: {
    a: moduleA
  }
})

if (module.hot) {
  // 使 action 和 mutation 成为可热重载模块
  module.hot.accept(['./mutations', './modules/a'], () => {
    // 获取更新后的模块
    // 因为 babel 6 的模块编译格式问题,这里需要加上 `.default`
    const newMutations = require('./mutations').default
    const newModuleA = require('./modules/a').default
    // 加载新模块
    store.hotUpdate({
      mutations: newMutations,
      modules: {
        a: newModuleA
      }
    })
  })
}
到这里可以总结一下vuex的源码流程 1.注入vue 2.模块化 3.注册模块下的actions,mutations,getters 4.初始化_vm,将store的state转成reactive和getters转成computed 属性 5.初始化插件
constructor (options = {}) {
   //1注入vue
    if (!Vue && typeof window !== 'undefined' && window.Vue) {
      install(window.Vue)
    }

   ...
   //创建存储actions,getters,mutations等对象,并模块初始化
    this._committing = false
    this._actions = Object.create(null)
    this._actionSubscribers = []
    this._mutations = Object.create(null)
    this._wrappedGetters = Object.create(null)
    this._modules = new ModuleCollection(options)
    this._modulesNamespaceMap = Object.create(null)
    this._subscribers = []
    this._watcherVM = new Vue()
    this._makeLocalGettersCache = Object.create(null)
   ....

   //注册模块下的actions,mutations,getters
    installModule(this, state, [], this._modules.root)

    //初始化_vm,将store的state转成reactive和getters转成computed 属性
    resetStoreVM(this, state)

    // 初始化插件
    plugins.forEach(plugin => plugin(this))

   
  }
   

tips

require.context动态加载模块,返回一个webpackContext,这个context有三个属性resolve, keys, id。 这个webpackContext其实内部大概是
var map = {
        "./A.js": "./src/components/test/components/A.js",
        "./B.js": "./src/components/test/components/B.js",
        "./C.js": "./src/components/test/components/C.js",
        "./D.js": "./src/components/test/components/D.js"
};
只不过map是模块内部变量,无法直接访问,所以通过提供的keys方法访问。 然后将内部的map通过keys()迭代获取key值,然后通过key获取value,最后包装成 {key:value}外部对象。
const context = require.context("./modules", false, /([a-z_]+)\.js$/i)

  const modules = context
    .keys()
    .map((key) => ({ key, name: key.match(/([a-z_]+)\.js$/i)[1] }))
    .reduce(
      (modules, { key, name }) => ({
        ...modules,
        [name]: context(key).default
      }),
     zhe {}
    )
利用require,context动态导入模块
const importAll = context => {
  const map = {}

  for (const key of context.keys()) {
    const keyArr = key.split('/')
    keyArr.shift() // 移除.
    map[keyArr.join('.').replace(/\.js$/g, '')] = context(key)
  }

  return map
}

export default importAll



import importAll from '$common/importAll'
export default importAll(require.context('./', true, /\.js$/))
  利用require,context热重载还需要用到context返回的id
  1. keys: 返回匹配成功模块的名字组成的数组
  1. resolve: 接受一个参数request,request为test文件夹下面匹配文件的相对路径,返回这个匹配文件相对于整个工程的相对路径
  1. id: 执行环境的id,返回的是一个字符串,主要用在module.hot.accept,热加载
// store.js
import Vue from 'vue'
import Vuex from 'vuex'

// 加载所有模块。
function loadModules() {
  const context = require.context("./modules", false, /([a-z_]+)\.js$/i)

  const modules = context
    .keys()
    .map((key) => ({ key, name: key.match(/([a-z_]+)\.js$/i)[1] }))
    .reduce(
      (modules, { key, name }) => ({
        ...modules,
        [name]: context(key).default
      }),
      {}
    )

  return { context, modules }
}

const { context, modules } = loadModules()

Vue.use(Vuex)

const store = new Vuex.Store({
  modules
})

if (module.hot) {
  // 在任何模块发生改变时进行热重载。
  module.hot.accept(context.id, () => {
    const { modules } = loadModules()

    store.hotUpdate({
      modules
    })
  })
}
 

标签:分析,...,const,module,state,源码,._,vuex,store
From: https://www.cnblogs.com/aliceKurapika/p/16662404.html

相关文章

  • 直播平台搭建源码,把bitmap插入到相册和相机图库
    直播平台搭建源码,把bitmap插入到相册和相机图库插入到相册:       Filefile=newFile(filePath);      try{        Medi......
  • GPU 利用率低常见原因分析及优化
    一、GPU利用率的定义本文的GPU利用率主要指GPU在时间片上的利用率,即通过nvidia-smi显示的GPU-util这个指标。统计方式为:在采样周期内,GPU上面有kernel执行的......
  • Python源码解析-dict的底层实现(PyDictObject)
    目录简介PyDictObject对象类型创建dict缓存池本文基于Python3.10.4。简介元素与元素之间通常可能会存在某种联系,这个联系将两个元素关联在一起。为了刻画这种关联关系,编......
  • 汽车电子电路技术完整分析
    汽车电子电路技术完整分析参考文献链接https://mp.weixin.qq.com/s/-Wt6HfM3Pq-qpbbblvUTFwhttps://mp.weixin.qq.com/s/KRNmE4aaCW4EWpp8M0V-cAhttps://mp.weixin.qq.......
  • 国内RPA厂商综合分析之【来也科技】
    由于工作的缘故,小爬这段时间对国内主流RPA产品进行了一番细致的调研和产品试用,对这些产品和厂商也算是打过几次交道,对他们的产品特性和优缺点算是有一些了解。今天我们......
  • Day06页面结构分析
    页面结构分析header:标记头部区域的内容(用于页面或页面中的一块区域)footer:标记脚步区域的内容(用于整个页面或页面的一块区域)section:Web页面中的一块独立区域article:独立......
  • pandas_pandas_网站log分析
    #读取整个文件夹的log,合并到一个dataframe\1.遍历文件夹读取log\2.然后通过concat函数将这些log合并一个大的dataframe-----------------------------------------......
  • 股票分析-市盈率
    PE市盈率=股价/每股收益=总市值/净利润租售比:房子的价格/每年租金投资某一项回本的年限(PE)1*市盈率(PE)在12到14倍之间是符合(回本期限)动态:当总市值/今年(估算今年的净利润......
  • php8.0源码编译安装mysqli拓展
    将开发环境web项目上传到CentOS8.0云服务器上后,调用PHP文件报500错误,经排查php环境中不存在mysqli拓展,导致无法使用mysqli_connect()函数;查看phpinfo()页面证实猜想;原因可......
  • 源码(chan,map,GMP,mutex,context)
    目录1、chan原理1.1chan底层数据结构1.2创建channel原理1.3写入channel原理1.4读channel原理1.5关闭channel原理1.6总结2、map原理2.1存储结构2.2初始化原理2.3写入......