Pinia 核心源码深度解析 一

228 阅读2分钟

Pinia 核心源码深度解析

一、核心模块与功能概览

Pinia 的 Store 核心实现模块,主要包含以下功能:

  1. Store 创建:处理 Options API 和 Setup API 两种定义方式
  2. 响应式状态管理:深度合并状态、处理 Ref/Reactive 对象
  3. Action 与 Getter 处理:实现 action 上下文绑定和 getter 缓存
  4. 插件系统:支持通过 pinia.use() 扩展功能
  5. 热更新(HMR) :支持开发环境下的模块热替换
  6. 副作用管理:通过 effectScope 实现依赖自动清理

二、核心函数解析

1. mergeReactiveObjects - 深度合并响应式对象
TypeScript
function mergeReactiveObjects(target, patchToApply) {
  // 处理 Map/Set 类型
  if (target instanceof Map && patchToApply instanceof Map) {
    patchToApply.forEach((value, key) => target.set(key, value))
  } 
  // 处理普通对象
  else {
    for (const key in patchToApply) {
      const subPatch = patchToApply[key]
      // 递归合并嵌套对象
      if (isPlainObject(targetValue) && isPlainObject(subPatch)) {
        target[key] = mergeReactiveObjects(targetValue, subPatch)
      } else {
        target[key] = subPatch
      }
    }
  }
}

核心作用:实现 $patch 的深度状态合并,支持 Map/Set 等复杂类型,确保响应式更新正确触发。

2. createOptionsStore - 选项式 Store 适配器
TypeScript
function createOptionsStore(id, options, pinia) {
  const { state, actions, getters } = options

  // 转换为 Composition API 形式
  function setup() {
    const localState = toRefs(pinia.state.value[id])
    return assign(localState, actions, computedGetters)
  }

  // 最终调用 Setup API 的创建方法
  return createSetupStore(id, setup, options, pinia, true)
}

实现要点

  • 将 Options 形式转换为 Composition API 形式
  • 使用 toRefs 保持响应式
  • 通过 computed 包装 getters 实现缓存
3. createSetupStore - Composition API 核心实现
TypeScript
function createSetupStore($id, setup, options, pinia) {
  // 创建副作用作用域
  const scope = effectScope()
  const setupStore = scope.run(() => setup({ action }))!

  // 处理响应式状态
  for (const key in setupStore) {
    const prop = setupStore[key]
    if (isRef(prop) || isReactive(prop)) {
      // 同步状态到全局 pinia.state
      pinia.state.value[$id][key] = prop
    } else if (typeof prop === 'function') {
      // 包装 Action
      setupStore[key] = action(prop, key)
    }
  }

  // 应用插件
  pinia._p.forEach(extender => {
    assign(store, extender({ store, pinia }))
  })
}

核心逻辑

  1. 副作用隔离:通过 effectScope 管理整个 Store 的副作用
  2. 状态同步:自动将 setup 返回的 ref/reactive 同步到全局状态树
  3. Action 增强:通过 action 包装器实现 $onAction 订阅

三、关键机制详解

1. Action 处理机制
TypeScript
function action<Fn extends _Method>(fn: Fn, name: string): Fn {
  const wrappedAction = function (this: any) {
    // 触发 action 订阅回调
    triggerSubscriptions(actionSubscriptions, {
      args,
      name,
      store,
      after,
      onError
    })

    try {
      const ret = fn.apply(store, args)
      // 处理 Promise 链
      if (ret instanceof Promise) {
        return ret.then(value => {
          triggerSubscriptions(afterCallbackList, value)
          return value
        })
      }
    } catch (error) {
      triggerSubscriptions(onErrorCallbackList, error)
    }
  }
  return wrappedAction
}

核心功能

  • 自动添加 $onAction 订阅支持
  • 统一处理同步/异步错误
  • 提供 after 和 onError 生命周期钩子
2. Getter 缓存机制
TypeScript
// 处理 Options API 的 getters
const computedGetters = Object.keys(getters).reduce((acc, name) => {
  acc[name] = computed(() => {
    setActivePinia(pinia)
    return getters[name].call(store, store)
  })
  return acc
}, {})

实现原理

  • 使用 Vue 的 computed 实现缓存
  • 自动绑定 Store 上下文(this
  • 在 getter 访问时自动激活当前 Pinia 实例
3. 响应式状态同步
TypeScript
// 将 setup 返回的状态同步到全局状态树
for (const key in setupStore) {
  if (isRef(prop) || isReactive(prop)) {
    if (!isOptionsStore) {
      pinia.state.value[$id][key] = prop
    }
  }
}

设计要点

  • 自动检测 ref/reactive 对象
  • 通过 toRef/toRefs 保持响应式链接
  • 支持服务端渲染(SSR)状态同步

四、高级特性实现

1. 热更新(HMR)支持
TypeScript
store._hotUpdate = markRaw((newStore) => {
  // 合并新旧状态
  newStore._hmrPayload.state.forEach(stateKey => {
    if (stateKey in store.$state) {
      const newStateTarget = newStore.$state[stateKey]
      if (isPlainObject(newStateTarget)) {
        patchObject(newStateTarget, oldStateSource)
      }
    }
  })
  // 更新 getters/actions
  for (const actionName in newStore._hmrPayload.actions) {
    store[actionName] = action(newStore[actionName], actionName)
  }
})

实现机制

  • 通过 _hmrPayload 记录状态元数据
  • 使用 patchObject 深度合并状态对象
  • 动态替换 actions/getters 保持响应性
2. 插件系统集成
TypeScript
pinia._p.forEach(extender => {
  const extensions = scope.run(() => extender({
    store,
    app: pinia._a,
    pinia,
    options: optionsForPlugin
  }))
  assign(store, extensions)
})

扩展能力

  • 可访问完整的 Store 上下文
  • 支持添加新状态/方法
  • 可拦截 action 执行过程

五、性能优化策略

  1. 响应式优化

    • 使用 markRaw 标记不需要响应化的对象
    • 通过 shallowRef 优化大型数据集
    • 批量更新通过 $patch 减少触发次数
  2. 内存管理

    TypeScript
    function $dispose() {
      scope.stop()
      pinia._s.delete($id)
    }
    
    • 通过 effectScope.stop() 自动清理所有副作用
    • 支持手动销毁 Store 释放内存
  3. 类型系统优化

    • 使用条件类型处理 Options/Setup API 类型推断
    TypeScript
    type StoreState<SS> = SS extends Store<infer S> ? UnwrapRef<S> : ...
    
    • 精确推导 Action/Getter 类型

总结:Pinia 架构设计亮点

  1. 分层架构

    Text
    ┌─────────────┐
    │  User Code  │
    └──────┬──────┘
           │
    ┌──────▼──────┐
    │   Store     │
    └──────┬──────┘
           │
    ┌──────▼──────┐
    │  Pinia Core │
    └──────┬──────┘
           │
    ┌──────▼──────┐
    │ Vue Reactivity │
    └───────────────┘
    
  2. 响应式集成

    • 深度集成 Vue3 响应式系统
    • 利用 effectScope 实现精准的依赖追踪
  3. 扩展性设计

    • 插件系统与 Vue 生态无缝集成
    • 支持多种 Store 定义方式
  4. 类型安全

    • 全流程 TypeScript 类型支持
    • 精确推导 Options/Setup 类型