从源码视角来看Pinia!

一、Pinia 概览

Pinia 本质是:「基于 Vue3 响应式系统 + effectScope 的"全局可控副作用容器"」

Pinia 核心代码集中在:

Plain 复制代码
packages/pinia/src/
├── createPinia.ts
├── rootStore.ts
├── store.ts
├── subscriptions.ts
├── types.ts
Plain 复制代码
┌─────────────────────┐
│   用户 API 层        │  defineStore / storeToRefs
├─────────────────────┤
│   Store 实现层       │  setupStore / optionsStore
├─────────────────────┤
│   响应式 & 调度层     │  reactive / effectScope / watch
└─────────────────────┘

二、createPinia 全局容器

2.1 createPinia 核心

TypeScript 复制代码
export function createPinia() {
  const scope = effectScope(true)
  const state = scope.run(() => ref({}))!

  const pinia = markRaw({
    _e: scope,
    _s: new Map(), // store 注册
    state,
    install(app) {
      setActivePinia(pinia)
      app.provide(piniaSymbol, pinia)
    }
  })

  return pinia
}
① 全局 effectScope
TypeScript 复制代码
const scope = effectScope(true)

所有 store 的 effect / computed / watch,全部挂在这个全局 scope(作用域) 下

这意味着 pinia._e.stop() 就能一次性销毁所有 store 副作用

② 全局 state 容器
TypeScript 复制代码
const state = ref({})

Pinia 并不是每个 store 自己维护 root state。

而是 pinia 统一管理,然后由 storeId 区分 state 属于哪个 store:

TypeScript 复制代码
pinia.state.value[storeId] = storeState // storeId 就是我们通过 defineStore 创建 store 的第一个参数

三、defineStore 定义 store

TypeScript 复制代码
export function defineStore(id, setupOrOptions) {
  return function useStore() {
    const pinia = getActivePinia()
    if (!pinia._s.has(id)) {
      createStore(id, setupOrOptions, pinia)
    }
    return pinia._s.get(id)
  }
}

Pinia 内部其实有 ​两种 store​:

3.1 setupStore 核心流程

TypeScript 复制代码
function setupStore(id, setup, pinia) {
  const scope = effectScope()
  const store = reactive({})

  const setupResult = scope.run(() =>
    setup({ action })
  )

  for (const key in setupResult) {
    const prop = setupResult[key]
    store[key] = prop
  }

  pinia._s.set(id, store)
}
① 每个 store 自己也有一个 effectScope
TypeScript 复制代码
const scope = effectScope()

层级关系:

Plain 复制代码
Pinia Root Scope
 └── Store Scope
      ├── computed
      ├── watch
      └── effect

所以,store.$dispose() ⇒ stop 当前 store 的所有副作用,不影响其他 store

② store 是 reactive 包裹的对象
TypeScript 复制代码
const store = reactive({})

​注意:​ Pinia ​不包装 state,​ Pinia 包装的是整个 store

所以:

TypeScript 复制代码
store.count
store.double
store.increment

全部是同一个 reactive proxy。

如果通过 Setup Store 创建:

TypeScript 复制代码
const count = ref(0)
return { count }
TypeScript 复制代码
store.count === count // true // 本质上 Pinia 直接复用 Vue 原生响应式对象

如果是 options store :

TypeScript 复制代码
state: () => ({ count: 0 })
TypeScript 复制代码
pinia.state.value[id] = reactive(state())

然后:

TypeScript 复制代码
store.count -> toRef(pinia.state.value[id], 'count')

所以:

TypeScript 复制代码
store.count++ // 实际修改的是 pinia.state

四、getters 的底层原理(computed)

Options Store 的 getter:

TypeScript 复制代码
getters: {
  double: (state) => state.count * 2
}

源码实现本质:

TypeScript 复制代码
computed(() => {
  setActivePinia(pinia)
  return getter.call(store, store)
})

本质结论:

Pinia getter == Vue computed

  • 依赖收集由 Vue 完成
  • 脏检查、缓存完全交给 computed

五、actions

TypeScript 复制代码
actions: {
  increment() {
    this.count++
  }
}

源码:

TypeScript 复制代码
function wrapAction(name, action) {
  return function () {
    return action.apply(store, arguments)
  }
}

六、其他 API

6.1 $patch:为什么比直接改 state 更好?

TypeScript 复制代码
store.$patch({
  count: store.count + 1
})

源码:

TypeScript 复制代码
pauseTracking()
applyPatch() // 批量更新,暂停追踪依赖
resumeTracking()
triggerSubscriptions() // 统一触发

6.2 $subscribe / watch 的关系(副作用系统)

TypeScript 复制代码
store.$subscribe((mutation, state) => {})

源码:

TypeScript 复制代码
watch(
  () => pinia.state.value[id],
  (state) => callback(),
  { deep: true }
)

Pinia 的 subscribe 就是一个封装过的 watch

但多做了:mutation 类型、时间戳、devtools hook

6.3 storeToRefs:为什么不会丢响应式?

TypeScript 复制代码
const { count } = storeToRefs(store)

源码:

TypeScript 复制代码
if (isRef(value) || isReactive(value)) {
  refs[key] = toRef(store, key)
}

storeToRefs = 批量 toRef

七、Pinia vs Vuex(源码级简单对比)

相关推荐
QQ1__8115175158 小时前
Spring boot名城小区物业管理系统信息管理系统源码-SpringBoot后端+Vue前端+MySQL【可直接运行】
前端·vue.js·spring boot
钛态8 小时前
前端微前端架构:大项目的救命稻草还是自找麻烦?
前端·vue·react·web
一粒黑子8 小时前
【实战解析】阿里开源 PageAgent:纯前端 GUI Agent,一行JS让网页支持自然语言操控
前端·javascript·开源
独角鲸网络安全实验室8 小时前
2026微信小程序抓包全解析:从实操落地到合规风控,解锁前端调试新范式
前端·微信小程序·小程序·抓包·系统代理绕过·https证书严格校验·进程隔离
紫微AI8 小时前
前端文本测量成了卡死一切创新的最后瓶颈,pretext实现突破了
前端·人工智能·typescript
GISer_Jing8 小时前
AI前端(From豆包)
前端·aigc·ai编程
IT枫斗者8 小时前
前端部署后如何判断“页面是不是最新”?一套可落地的版本检测方案(适配 Vite/Vue/React/任意 SPA)
前端·javascript·vue.js·react.js·架构·bug
测试修炼手册8 小时前
[测试技术] 深入理解 JSON Web Token (JWT)
前端·json
AI老李8 小时前
2026 年 Web 前端开发的 8 个趋势!
前端
里欧跑得慢8 小时前
15. Web可访问性最佳实践:让每个用户都能平等访问
前端·css·flutter·web