前端基础知识库vuejs系列三vuex核心原理

简介: 现在前端开发中vuejs是大部分前端工程师的首选,只要不是很小的项目都会引用vuex来进行状态管理。最近新开展的项目比较复杂,使用vuex的地方也比较多。在此过程中也遇到了不少问题。如今有时间正好研究下vuex框架源码,深入了解下他的底层实现。

前言


现在前端开发中vuejs是大部分前端工程师的首选,只要不是很小的项目都会引用vuex来进行状态管理。最近新开展的项目比较复杂,使用vuex的地方也比较多。在此过程中也遇到了不少问题。如今有时间正好研究下vuex框架源码,深入了解下他的底层实现。

为什么要使用vuex

在当前前端的spa模块化项目中不可避免的是某些变量需要在全局范围内引用,此时父子组件的传值,子父组件间的传值,兄弟组件间的传值成了我们需要解决的问题。虽然vue中提供了props(父传子)commit(子传父)兄弟间也可以用localstorage和sessionstorage。但是这种方式在项目开发中带来的问题比他解决的问题(难管理,难维护,代码复杂,安全性低)更多。vuex的诞生也是为了解决这些问题,从而大大提高我们vue项目的开发效率。(信不信由你=-=)我接触vue项目的这两年,虽然一直在用vuex(用的还算熟练),但是还没有深入的去了解过他的架构原理,最近正好有空,就来深入学习下。

vuex的整体架构主体

源码解析(工作原理)

  1. 根state的存储位置   当你在使用vuex时有没有想过你在代码中设置的state被vuex如何处理了呢
const store = new Vuex.Store({ 
state: { count: 0 }, 
mutations: { increment (state) { 
      state.count++ 
} } })

我们设置在跟state中的属性会被vuex存储在根元素中

this._modules = new ModuleCollection(options)//初始化
 const state = this._modules.root.state//获取定义的state

vuex初始化时先去获取定义在state属性中的值new ModuleCollection(options)进行模块收集(重新组装我们定义在store中的相关属性): 最终形成一棵module树

export default class ModuleCollection {
  constructor (rawRootModule) {
    // register root module (Vuex.Store options)
    this.register([], rawRootModule, false)
  }
  get (path) {
    return path.reduce((module, key) => {
      return module.getChild(key)
    }, this.root)
  }
  getNamespace (path) {
    let module = this.root
    return path.reduce((namespace, key) => {
      module = module.getChild(key)
      return namespace + (module.namespaced ? key + '/' : '')
    }, '')
  }
  update (rawRootModule) {
    update([], this.root, rawRootModule)
  }
  register (path, rawModule, runtime = true) {
    if (process.env.NODE_ENV !== 'production') {
      assertRawModule(path, rawModule)
    }
    const newModule = new Module(rawModule, runtime)
    if (path.length === 0) {
      this.root = newModule
    } else {
      const parent = this.get(path.slice(0, -1))
      parent.addChild(path[path.length - 1], newModule)
    }
    // register nested modules
    if (rawModule.modules) {
      forEachValue(rawModule.modules, (rawChildModule, key) => {
        this.register(path.concat(key), rawChildModule, runtime)
      })
    }
  }
  unregister (path) {
    const parent = this.get(path.slice(0, -1))
    const key = path[path.length - 1]
    if (!parent.getChild(key).runtime) return
    parent.removeChild(key)
  }
}
  1. commit(=>mutations)时做了什么

const {       dispatch,       commit     } = this//初始化先绑定commit

this.commit = function boundCommit(type, payload, options) {
      return commit.call(store, type, payload, options)
    }

绑定之后注册mutation(commit的属性)

function registerMutation(store, type, handler, local) {
  const entry = store._mutations[type] || (store._mutations[type] = [])
  entry.push(function wrappedMutationHandler(payload) {
    handler.call(store, local.state, payload)
  })
}
commit(_type, _payload, _options) {
    console.log('================commit=====================')
    console.log(_type)
    console.log(_payload)
    console.log(_options)
    // check object-style commit
    const {
      type,
      payload,
      options
    } = unifyObjectStyle(_type, _payload, _options)
    const mutation = {
      type,
      payload
    }
    //这是一个函数数组
    const entry = this._mutations[type]
    //判断当前设置的属性的值是否存在
    if (!entry) {
      if (process.env.NODE_ENV !== 'production') {
        console.error(`[vuex] unknown mutation type: ${type}`)
      }
      return
    }
    //在执行mutation的时候,会将_committing设置为true,执行完毕后重置,在开启strict模式时,会监听state的变化,当变化时_committing不为true时会给出警告
    this._withCommit(() => {
      //迭代传入的commit数组
      entry.forEach(function commitIterator(handler) {
        handler(payload)
      })
    })
    this._subscribers.forEach(sub => sub(mutation, this.state))
    //当开发环境是抛出警告(如果commit的属性不存在)
    if (
      process.env.NODE_ENV !== 'production' &&
      options && options.silent
    ) {
      console.warn(
        `[vuex] mutation type: ${type}. Silent option has been removed. ` +
        'Use the filter functionality in the vue-devtools'
      )
    }
  }

3.dispatch(=>actions)做了啥(初始化同commit)

//dispatch定义位置
    this.dispatch = function boundDispatch(type, payload) {
      return dispatch.call(store, type, payload)
    }

绑定dispatch

function registerAction(store, type, handler, local) {
  const entry = store._actions[type] || (store._actions[type] = [])
  entry.push(function wrappedActionHandler(payload, cb) {
    let res = handler.call(store, {
      dispatch: local.dispatch,
      commit: local.commit,
      getters: local.getters,
      state: local.state,
      rootGetters: store.getters,
      rootState: store.state
    }, payload, cb)
    //将res函数转为异步 promise
    if (!isPromise(res)) {
      res = Promise.resolve(res)
    }
    if (store._devtoolHook) {
      return res.catch(err => {
        store._devtoolHook.emit('vuex:error', err)
        throw err
      })
    } else {
      return res
    }
  })
}

注册actions

dispatch(_type, _payload) {
    // check object-style dispatch
    const {
      type,
      payload
    } = unifyObjectStyle(_type, _payload)
    const action = {
      type,
      payload
    }
    const entry = this._actions[type]
    if (!entry) {
      if (process.env.NODE_ENV !== 'production') {
        console.error(`[vuex] unknown action type: ${type}`)
      }
      return
    }
    try {
      this._actionSubscribers
        .filter(sub => sub.before)
        .forEach(sub => sub.before(action, this.state))
    } catch (e) {
      if (process.env.NODE_ENV !== 'production') {
        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)
    return result.then(res => {
      try {
        this._actionSubscribers
          .filter(sub => sub.after)
          .forEach(sub => sub.after(action, this.state))
      } catch (e) {
        if (process.env.NODE_ENV !== 'production') {
          console.warn(`[vuex] error in after action subscribers: `)
          console.error(e)
        }
      }
      return res
    })
  }

其他

vuex和全局变量的区别:(借用博客)

1,【响应式】vuex的状态存储是响应式的,当Vue组件从store中读取状态的时候,若store中的状态发生变化,那么相应的组件也会得到高效更新。

2,【不能直接改变store】不能直接改变store的变化,改变store中状态的唯一途径是commit mutation。方便于跟踪每一个状态的变化。


相关文章
|
19天前
|
Web App开发 前端开发 JavaScript
前端应用实现 image lazy loading 的原理介绍
前端应用实现 image lazy loading 的原理介绍
29 0
|
1月前
|
JavaScript API
Vue3的响应式原理
Vue 3 中的响应式原理是通过使用 ES6 的 `Proxy 对象`来实现的**。在 Vue 3 中,每个组件都有一个响应式代理对象,当组件中的数据发生变化时,代理对象会立即响应并更新视图。
|
3天前
|
存储 JavaScript
Vue的状态管理:Vuex的使用和最佳实践
【4月更文挑战第24天】Vue状态管理库Vuex用于集中管理组件状态,包括State(全局状态)、Getters(计算属性)、Mutations(同步状态变更)和Actions(异步操作)。Vuex还支持Modules,用于拆分大型状态树。使用Vuex时,需安装并创建Store,定义状态、getter、mutation和action,然后在Vue实例中注入Store。遵循最佳实践,如保持状态树简洁、使用常量定义Mutation类型、避免直接修改状态、在Actions中处理异步操作、合理划分Modules,以及利用Vuex提供的插件和工具,能提升Vue应用的稳定性和可维护性。
|
3天前
|
JavaScript 前端开发 开发者
Vue的响应式原理:深入探索Vue的响应式系统与依赖追踪
【4月更文挑战第24天】Vue的响应式原理通过JavaScript getter/setter实现,当数据变化时自动更新视图。它创建Watcher对象收集依赖,并通过依赖追踪机制精确通知更新。当属性改变,setter触发更新相关Watcher,重新执行操作以反映数据最新状态。Vue的响应式系统结合依赖追踪,有效提高性能,简化复杂应用的开发,但对某些复杂数据结构需额外处理。
|
19天前
|
JavaScript 前端开发 API
vue中的ref/reactive区别及原理
vue中的ref/reactive区别及原理
18 0
|
19天前
|
存储 JavaScript
vue3 vuex
vue3 vuex
19 0
vue3 vuex
|
1月前
|
JavaScript 前端开发 算法
深入探讨前端框架Vue.js中的虚拟DOM机制
本文将深入探讨前端框架Vue.js中的虚拟DOM机制,分析其原理、优势以及在实际开发中的应用场景,帮助读者更好地理解Vue.js框架的核心特性。
|
1月前
|
前端开发 JavaScript API
|
1月前
|
JavaScript
vue双向数据绑定的原理?
vue双向数据绑定的原理?
14 1
|
1月前
|
JSON JavaScript 算法
Vue之v-for(包含key内部原理讲解)
Vue之v-for(包含key内部原理讲解)