【DeepSeek帮我准备前端面试100问】(十二)vuex的实现原理

123 阅读2分钟

Vuex 实现原理深度解析

Vuex 作为 Vue 的官方状态管理库,其内部实现非常精妙。下面我将从核心机制、响应式原理、模块系统等方面深入剖析 Vuex 的实现原理。

一、Vuex 核心架构

1. 整体架构图

┌───────────────────────────────────────────────────┐
│                    Vue Components                 │
└───────────────┬───────────────────┬───────────────┘
                │                   │
                ↓                   ↓
┌───────────────┴───┐      ┌────────┴─────────────┐
│     Actions       │      │       Mutations      │
└───────────────┬───┘      └────────┬─────────────┘
                │                   │
                └─────────┬─────────┘
                          ↓
┌───────────────────────────────────────────────────┐
│                      State                        │
└───────────────────────────────────────────────────┘

2. 核心实现机制

  1. Store 构造函数:创建全局唯一的 store 实例
  2. 响应式 state:利用 Vue 的响应式系统
  3. 提交/分发机制:实现 mutations 和 actions
  4. 模块收集系统:处理嵌套模块

二、核心源码解析

1. Store 构造函数

class Store {
  constructor(options = {}) {
    // 初始化核心属性
    this._committing = false
    this._actions = Object.create(null)
    this._mutations = Object.create(null)
    this._wrappedGetters = Object.create(null)
    this._modules = new ModuleCollection(options)
    
    // 绑定 commit 和 dispatch 的上下文
    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)
    }
    
    // 初始化根模块
    installModule(this, this.state, [], this._modules.root)
    
    // 使用 Vue 实例使 state 响应式
    resetStoreVM(this, this.state)
  }
}

2. 响应式 state 实现

Vuex 使用 Vue 实例来使 state 变为响应式:

function resetStoreVM(store, state) {
  // 旧的 Vue 实例
  const oldVm = store._vm
  
  // 创建新的 Vue 实例
  store._vm = new Vue({
    data: {
      $$state: state
    },
    computed: store.getters
  })
  
  // 销毁旧的 Vue 实例
  if (oldVm) {
    Vue.nextTick(() => oldVm.$destroy())
  }
}

这样做的巧妙之处在于:

  • 利用 Vue 已有的响应式系统
  • getters 作为 computed 属性实现缓存
  • 状态变化自动触发视图更新

3. commit 实现(同步修改 state)

commit(_type, _payload, _options) {
  // 参数标准化处理
  const { type, payload, options } = unifyObjectStyle(_type, _payload, _options)
  
  const mutation = { type, payload }
  const entry = this._mutations[type]
  
  // 执行所有同名的 mutation
  this._withCommit(() => {
    entry.forEach(function commitIterator(handler) {
      handler(payload)
    })
  })
  
  // 通知订阅者
  this._subscribers.forEach(sub => sub(mutation, this.state))
}

_withCommit(fn) {
  const committing = this._committing
  this._committing = true
  fn()
  this._committing = committing
}

关键点:

  • _withCommit 确保只有通过 mutation 才能修改 state
  • 严格模式下会检查 _committing 标志位

4. dispatch 实现(异步操作)

dispatch(_type, _payload) {
  // 参数标准化处理
  const { type, payload } = unifyObjectStyle(_type, _payload)
  
  const entry = this._actions[type]
  
  // 执行所有同名的 action
  return entry.length > 1
    ? Promise.all(entry.map(handler => handler(payload)))
    : entry[0](payload)
}

特点:

  • 返回 Promise 便于异步处理
  • 支持多个同名 action 并行执行

5. 模块系统实现

Vuex 使用 ModuleCollection 类管理模块:

class ModuleCollection {
  constructor(rawRootModule) {
    this.register([], rawRootModule)
  }
  
  register(path, rawModule) {
    const newModule = new Module(rawModule)
    if (path.length === 0) {
      this.root = newModule
    } else {
      const parent = this.get(path.slice(0, -1))
      parent.addChild(path[path.length - 1], newModule)
    }
    
    // 递归注册嵌套模块
    if (rawModule.modules) {
      Object.keys(rawModule.modules).forEach(key => {
        this.register(path.concat(key), rawModule.modules[key])
      })
    }
  }
}

模块安装过程:

function installModule(store, rootState, path, module) {
  // 设置模块 state
  if (path.length > 0) {
    const parentState = getNestedState(rootState, path.slice(0, -1))
    Vue.set(parentState, path[path.length - 1], module.state)
  }
  
  // 注册 mutation
  module.forEachMutation((mutation, key) => {
    const namespacedType = namespace + key
    registerMutation(store, namespacedType, mutation, local)
  })
  
  // 注册 action
  module.forEachAction((action, key) => {
    const namespacedType = namespace + key
    registerAction(store, namespacedType, action, local)
  })
  
  // 注册 getter
  module.forEachGetter((getter, key) => {
    const namespacedType = namespace + key
    registerGetter(store, namespacedType, getter, local)
  })
  
  // 递归安装子模块
  module.forEachChild((child, key) => {
    installModule(store, rootState, path.concat(key), child)
  })
}

三、响应式原理详解

1. State 响应式实现

Vuex 通过将 state 作为 Vue 实例的 data 属性实现响应式:

store._vm = new Vue({
  data: {
    $$state: state
  },
  computed: getters
})
  • $$state 会被 Vue 转换为响应式属性
  • 访问 store.state 实际上是访问 store._vm._data.$$state
  • 修改 state 必须通过 mutation 才能触发响应式更新

2. Getters 实现机制

Getters 作为 Vue 的 computed 属性实现:

function resetStoreVM(store, state) {
  const wrappedGetters = store._wrappedGetters
  const computed = {}
  
  // 将 getter 转换为 computed 属性
  Object.keys(wrappedGetters).forEach(key => {
    computed[key] = () => wrappedGetters[key](store)
    Object.defineProperty(store.getters, key, {
      get: () => store._vm[key],
      enumerable: true
    })
  })
  
  store._vm = new Vue({
    data: { $$state: state },
    computed  // getters 作为 computed
  })
}

优势:

  • 自动缓存计算结果
  • 只有当依赖的 state 变化时才重新计算

四、插件系统原理

Vuex 的插件是一个函数,接收 store 作为参数:

const myPlugin = store => {
  // 订阅 store 的 mutation
  store.subscribe((mutation, state) => {
    console.log(mutation.type)
  })
}

const store = new Vuex.Store({
  // ...
  plugins: [myPlugin]
})

内部实现:

class Store {
  constructor() {
    this._subscribers = []
    // 应用插件
    plugins.forEach(plugin => plugin(this))
  }
  
  subscribe(fn) {
    this._subscribers.push(fn)
  }
}

五、严格模式实现

严格模式下会深度 watch state,检查是否通过 mutation 修改:

function enableStrictMode(store) {
  store._vm.$watch(function() { 
    return this._data.$$state 
  }, () => {
    assert(store._committing, 
      `Do not mutate vuex store state outside mutation handlers.`)
  }, { deep: true, sync: true })
}

六、辅助函数实现

以 mapState 为例:

function mapState(states) {
  const res = {}
  normalizeMap(states).forEach(({ key, val }) => {
    res[key] = function mappedState() {
      return typeof val === 'function'
        ? val.call(this, this.$store.state, this.$store.getters)
        : this.$store.state[val]
    }
  })
  return res
}

七、总结:Vuex 设计精妙之处

  1. 巧用 Vue 响应式系统

    • 复用 Vue 的响应式机制
    • getters 作为 computed 属性实现缓存
  2. 严格的状态变更控制

    • 只能通过 commit 修改 state
    • 严格模式防止意外修改
  3. 灵活的模块系统

    • 支持嵌套模块
    • 命名空间隔离
  4. 可扩展的插件机制

    • 日志、持久化等插件
    • 订阅 mutation 变化
  5. 统一的状态管理

    • 单一状态树
    • 可预测的状态变化

理解 Vuex 的实现原理有助于:

  • 更高效地使用 Vuex
  • 能够开发 Vuex 插件
  • 在遇到问题时能够快速定位
  • 为学习其他状态管理库打下基础

对于现代 Vue 3 项目,虽然 Pinia 是更推荐的选择,但理解 Vuex 的设计思想仍然非常有价值。