基于 vuejs/pinia v2 源码
2026-03-16 | 技术深度解读
第一部分:基础概念
第二部分:核心实现
第三部分:核心功能
第四部分:高级特性
Pinia 是 Vue 3 的官方状态管理库,名字来源于西班牙语的"菠萝"
核心特点
与 Vuex 对比
┌─────────────────────────────────────────┐
│ Vue Application │
│ ┌─────────────────────────────────┐ │
│ │ app.use(pinia) │ │
│ └─────────────────────────────────┘ │
└─────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ Pinia Instance │
│ ┌───────────┐ ┌──────────────────┐ │
│ │ state │ │ _s (stores) │ │
│ │ (全局状态) │ │ Map<id, store>│ │
│ └───────────┘ └──────────────────┘ │
│ ┌───────────────────────────────────┐ │
│ │ _p (plugins) │ │
│ └───────────────────────────────────┘ │
└─────────────────────────────────────────┘
│
┌───────────┼───────────┐
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Store A │ │ Store B │ │ Store C │
└─────────┘ └─────────┘ └─────────┘
| 版本 | 时间 | 主要变化 |
|---|---|---|
| Vuex 3.x | 2017 | Vue 2 官方状态管理,mutations + actions |
| Vuex 4.x | 2020 | Vue 3 兼容版本 |
| Pinia 0.x | 2019 | 实验性项目,探索新 API |
| Pinia 1.x | 2021 | 稳定版本,Vue 3 默认推荐 |
| Pinia 2.x | 2022 | 完整 Vue 2/3 支持,Setup Store |
💡 设计理念:Keep it Simple, Stupid (KISS)
export function createPinia(): Pinia {
const scope = effectScope(true)
// 创建全局状态(响应式)
const state = scope.run<Ref<Record<string, StateTree>>>(() =>
ref<Record<string, StateTree>>({})
)!
let _p: Pinia['_p'] = [] // 插件列表
let toBeInstalled: PiniaPlugin[] = []
const pinia: Pinia = markRaw({
install(app: App) {
setActivePinia(pinia)
pinia._a = app
app.provide(piniaSymbol, pinia)
app.config.globalProperties.$pinia = pinia
// 安装待安装的插件
toBeInstalled.forEach((plugin) => _p.push(plugin))
toBeInstalled = []
},
use(plugin) { ... },
_p, // 插件列表
_a: null, // Vue app 实例
_e: scope, // effectScope
_s: new Map<string, StoreGeneric>(), // store 缓存
state, // 全局状态
})
return pinia
}
interface Pinia {
// 插件列表
_p: PiniaPlugin[]
// Vue app 实例
_a: App | null
// effectScope 用于管理副作用作用域
_e: EffectScope
// Store 缓存 Map
_s: Map<string, StoreGeneric>
// 全局状态(响应式 ref)
state: Ref<Record<string, StateTree>>
// 安装方法
install(app: App): void
// 使用插件
use(plugin: PiniaPlugin): Pinia
}
💡 关键点:所有 Store 共享同一个 state 对象,便于 SSR 序列化
三种调用方式:
// 1. Options Store(传统写法)
export const useCounterStore = defineStore('counter', {
state: () => ({ count: 0 }),
getters: {
double: (state) => state.count * 2,
},
actions: {
increment() { this.count++ },
},
})
// 2. Setup Store(推荐)
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const double = computed(() => count.value * 2)
function increment() { count.value++ }
return { count, double, increment }
})
// 3. 仅 ID + Options
export const useCounterStore = defineStore({
id: 'counter',
state: () => ({ count: 0 }),
})
function createOptionsStore<Id, S, G, A>(
id: Id,
options: DefineStoreOptions<Id, S, G, A>,
pinia: Pinia,
hot?: boolean
): Store<Id, S, G, A> {
const { state, actions, getters } = options
function setup() {
// 初始化状态
if (!initialState) {
pinia.state.value[id] = state ? state() : {}
}
const localState = toRefs(pinia.state.value[id])
// 合并 state、actions、getters
return assign(
localState,
actions,
Object.keys(getters || {}).reduce((computedGetters, name) => {
computedGetters[name] = markRaw(
computed(() => {
setActivePinia(pinia)
const store = pinia._s.get(id)!
return getters![name].call(store, store)
})
)
return computedGetters
}, {})
)
}
return createSetupStore(id, setup, options, pinia, hot, true)
}
优势
类型推断
// 自动推断类型
const useStore = defineStore(
'main',
() => {
const count = ref(0)
// count: Ref<number>
const double = computed(
() => count.value * 2
)
// double: ComputedRef<number>
return { count, double }
}
)
function createSetupStore($id, setup, options, pinia, hot, isOptionsStore) {
let scope: EffectScope
// 订阅回调列表
let subscriptions: SubscriptionCallback<S>[] = []
let actionSubscriptions: StoreOnActionListener[] = []
// 部分 Store 对象(核心方法)
const partialStore = {
_p: pinia,
$id,
$onAction: addSubscription.bind(null, actionSubscriptions),
$patch,
$reset,
$subscribe(callback, options = {}) {
const removeSubscription = addSubscription(
subscriptions, callback, options.detached,
() => stopWatcher()
)
const stopWatcher = scope.run(() =>
watch(() => pinia.state.value[$id], callback, options)
)
return removeSubscription
},
$dispose,
}
// 创建响应式 store
const store = reactive(partialStore)
// 执行 setup 函数
const setupStore = pinia._e.run(() =>
(scope = effectScope()).run(() => setup({ action }))
)!
// 合并 setupStore 到 store
assign(store, setupStore)
return store
}
sequenceDiagram
participant U as User Code
participant D as defineStore
participant C as createSetupStore
participant P as Pinia
participant S as Store
U->>D: defineStore('id', setup)
D->>D: 返回 useStore 函数
U->>D: useStore()
D->>P: 检查 Store 缓存 _s
alt Store 不存在
D->>C: createSetupStore()
C->>C: 创建 effectScope
C->>C: 执行 setup()
C->>C: 遍历 setup 返回值
loop 每个属性
alt 是 ref/reactive
C->>P: 添加到 pinia.state
else 是 function
C->>C: 包装为 action
else 是 computed
C->>C: 标记为 getter
end
end
C->>P: 缓存到 _s
end
D->>S: 返回 Store 实例
两种调用方式:
// 1. 对象式 patch
store.$patch({
count: store.count + 1,
name: 'New Name'
})
// 2. 函数式 patch(推荐用于复杂更新)
store.$patch((state) => {
state.items.push('new item')
state.count++
state.changedAt = Date.now()
})
💡 性能优化:多次修改会合并为一次更新,减少 watcher 触发
function mergeReactiveObjects<T>(
target: T,
patchToApply: _DeepPartial<T>
): T {
// 处理 Map 类型
if (target instanceof Map && patchToApply instanceof Map) {
patchToApply.forEach((value, key) => target.set(key, value))
}
// 处理 Set 类型
if (target instanceof Set && patchToApply instanceof Set) {
patchToApply.forEach(target.add, target)
}
// 递归合并普通对象
for (const key in patchToApply) {
const subPatch = patchToApply[key]
const targetValue = target[key]
if (
isPlainObject(targetValue) &&
isPlainObject(subPatch) &&
!isRef(subPatch) &&
!isReactive(subPatch)
) {
target[key] = mergeReactiveObjects(targetValue, subPatch)
} else {
target[key] = subPatch
}
}
return target
}
// 订阅状态变化
const unsubscribe = store.$subscribe(
(mutation, state) => {
console.log('Type:', mutation.type)
console.log('Store ID:', mutation.storeId)
console.log('New State:', state)
// mutation.type 可能是:
// - 'direct': 直接修改 store.count = 1
// - 'patch object': store.$patch({ count: 1 })
// - 'patch function': store.$patch(state => state.count++)
},
{ detached: true, deep: true, flush: 'sync' }
)
// 取消订阅
unsubscribe()
💡 detached: 即使组件卸载,订阅仍然有效
// 监听 action 调用
const unsubscribe = store.$onAction(
({ name, args, after, onError, store }) => {
console.log(`Action ${name} called with:`, args)
// action 成功完成后执行
after((result) => {
console.log(`Action ${name} finished:`, result)
})
// action 失败时执行
onError((error) => {
console.error(`Action ${name} failed:`, error)
})
}
)
// 异步 action 支持
await store.fetchData() // after 回调会等待 Promise resolve
const action = <Fn extends _Method>(fn: Fn, name: string): Fn => {
const wrappedAction = function (this: any) {
setActivePinia(pinia)
const args = Array.from(arguments)
const afterCallbackList: Array<(resolvedReturn: any) => any> = []
const onErrorCallbackList: Array<(error: unknown) => unknown> = []
// 触发 $onAction 订阅
triggerSubscriptions(actionSubscriptions, {
args,
name: wrappedAction[ACTION_NAME],
store,
after: (cb) => afterCallbackList.push(cb),
onError: (cb) => onErrorCallbackList.push(cb),
})
try {
let ret = fn.apply(store, args)
// 处理异步 action
if (ret instanceof Promise) {
return ret
.then((value) => {
triggerSubscriptions(afterCallbackList, value)
return value
})
.catch((error) => {
triggerSubscriptions(onErrorCallbackList, error)
return Promise.reject(error)
})
}
triggerSubscriptions(afterCallbackList, ret)
return ret
} catch (error) {
triggerSubscriptions(onErrorCallbackList, error)
throw error
}
} as MarkedAction<Fn>
return wrappedAction
}
// Options Store 才支持 $reset
const $reset = isOptionsStore
? function $reset() {
const { state } = options
const newState = state ? state() : {}
// 使用 $patch 统一更新,触发订阅
this.$patch(($state) => {
assign($state, newState)
})
}
: __DEV__
? () => {
throw new Error(
`🍍: Store "${$id}" is built using the setup syntax ` +
`and does not implement $reset().`
)
}
: noop
// 使用示例
store.$reset() // 重置到初始状态
💡 Setup Store 不支持 $reset:因为无法确定初始状态
function $dispose() {
// 停止 effectScope,清理所有副作用
scope.stop()
// 清空订阅
subscriptions = []
actionSubscriptions = []
// 从 Pinia 缓存中移除
pinia._s.delete($id)
}
// 使用场景
// 1. 测试中清理
afterEach(() => {
store.$dispose()
})
// 2. 动态创建的 store
const dynamicStore = createDynamicStore()
dynamicStore.$dispose()
⚠️ 注意:$dispose 不会删除 pinia.state.value 中的状态
// Store 使用 reactive 包装
const store = reactive({
$id,
$patch,
$subscribe,
// ... 其他属性
})
// 状态使用 ref
const state = ref<Record<string, StateTree>>({})
// Setup Store 的 ref 会被收集到 pinia.state
if (isRef(prop) && !isComputed(prop)) {
pinia.state.value[$id][key] = prop
}
// Computed 用于 getters
const double = computed(() => {
setActivePinia(pinia)
return getters![name].call(store, store)
})
Pinia 级别
const scope = effectScope(true)
const state = scope.run(() =>
ref<Record<string, StateTree>>({})
)
pinia._e = scope
所有 Store 共享同一个 effectScope
Store 级别
let scope: EffectScope
const setupStore = pinia._e.run(() =>
(scope = effectScope())
.run(() => setup({ action }))
)
// $dispose 时停止
function $dispose() {
scope.stop()
}
每个 Store 有独立的 effectScope
// 全局状态结构
pinia.state.value = {
// Store 1 的状态
'user': {
name: 'Chuck',
email: '[email protected]',
roles: ['admin', 'user']
},
// Store 2 的状态
'cart': {
items: [
{ id: 1, name: 'Product A', qty: 2 },
{ id: 2, name: 'Product B', qty: 1 }
],
total: 199.99
},
// Store 3 的状态
'settings': {
theme: 'dark',
language: 'zh-CN'
}
}
// SSR 序列化
const state = JSON.stringify(pinia.state.value)
// 插件定义
interface PiniaPlugin {
(context: {
pinia: Pinia
app: App
store: Store
options: DefineStoreOptionsInPlugin
}): object | void
}
// 使用插件
const pinia = createPinia()
pinia.use(({ store }) => {
// 添加全局属性
store.$router = router
// 添加全局方法
store.$log = (msg) => console.log(`[${store.$id}]`, msg)
return { $router, $log }
})
// 所有 store 都会获得这些属性
const store = useUserStore()
store.$log('Hello') // [user] Hello
// 三种订阅方式
store.$subscribe(callback) // 状态变化订阅
store.$onAction(callback) // action 调用订阅
pinia.use(plugin) // 插件订阅(全局)
// 内部实现
let subscriptions: SubscriptionCallback[] = []
let actionSubscriptions: StoreOnActionListener[] = []
// 添加订阅
function addSubscription(list, callback, detached, onCleanup) {
list.push(callback)
return () => {
const idx = list.indexOf(callback)
if (idx > -1) {
list.splice(idx, 1)
onCleanup?.()
}
}
}
// 触发订阅
function triggerSubscriptions(list, ...args) {
list.slice().forEach(callback => callback(...args))
}
export function addSubscription<T extends _Method>(
subscriptions: T[],
callback: T,
detached?: boolean,
onCleanup: () => void = noop
) {
subscriptions.push(callback)
const removeSubscription = () => {
const idx = subscriptions.indexOf(callback)
if (idx > -1) {
subscriptions.splice(idx, 1)
onCleanup()
}
}
// 如果不是 detached 且在 effectScope 中
// 自动在 scope dispose 时清理
if (!detached && getCurrentScope()) {
onScopeDispose(removeSubscription)
}
return removeSubscription
}
💡 自动清理:非 detached 订阅会在组件卸载时自动清理
export function triggerSubscriptions<T extends _Method>(
subscriptions: T[],
...args: Parameters<T>
) {
// 使用 slice() 防止迭代时修改列表
subscriptions.slice().forEach((callback) => {
callback(...args)
})
}
// 使用示例
// 触发 $subscribe 回调
triggerSubscriptions(
subscriptions,
mutation,
pinia.state.value[$id]
)
// 触发 $onAction 回调
triggerSubscriptions(
actionSubscriptions,
{ args, name, store, after, onError }
)
💡 slice() 保护:防止回调中修改订阅列表导致迭代异常
// Store 类型定义
export type Store<
Id extends string = string,
S extends StateTree = {},
G = {},
A = {},
> = _StoreWithState<Id, S, G, A> &
UnwrapRef<S> &
_StoreWithGetters<G> &
A &
PiniaCustomProperties<Id, S, G, A>
// 类型推断
export type StoreState<SS> = SS extends Store<string, infer S, any, any>
? UnwrapRef<S>
: never
export type StoreGetters<SS> = SS extends Store<string, any, infer G, any>
? _StoreWithGetters<G>
: never
export type StoreActions<SS> = SS extends Store<string, any, any, infer A>
? A
: never
// Setup Store 类型推断
export type _ExtractStateFromSetupStore<SS> =
Pick<SS, _ExtractStateFromSetupStore_Keys<SS>>
export type _ExtractActionsFromSetupStore<SS> =
Pick<SS, _ExtractActionsFromSetupStore_Keys<SS>>
export type _ExtractGettersFromSetupStore<SS> =
Pick<SS, _ExtractGettersFromSetupStore_Keys<SS>>
// Keys 提取
type _ExtractStateFromSetupStore_Keys<SS> = keyof {
[K in keyof SS as SS[K] extends _Method | ComputedRef ? never : K]: any
}
type _ExtractActionsFromSetupStore_Keys<SS> = keyof {
[K in keyof SS as SS[K] extends _Method ? K : never]: any
}
type _ExtractGettersFromSetupStore_Keys<SS> = keyof {
[K in keyof SS as SS[K] extends ComputedRef ? K : never]: any
}
export enum MutationType {
/**
* 直接修改状态
* store.name = 'new name'
* store.$state.name = 'new name'
* store.list.push('new item')
*/
direct = 'direct',
/**
* 使用对象式 $patch
* store.$patch({ name: 'newName' })
*/
patchObject = 'patch object',
/**
* 使用函数式 $patch
* store.$patch(state => state.name = 'newName')
*/
patchFunction = 'patch function',
}
// 在 subscribe 回调中使用
store.$subscribe((mutation, state) => {
switch (mutation.type) {
case MutationType.direct:
console.log('Direct mutation')
break
case MutationType.patchObject:
console.log('Patch object:', mutation.payload)
break
case MutationType.patchFunction:
console.log('Patch function')
break
}
})
// createPinia 中自动注册
if (__USE_DEVTOOLS__ && IS_CLIENT && typeof Proxy !== 'undefined') {
pinia.use(devtoolsPlugin)
}
// DevTools 插件功能
const devtoolsPlugin = ({ store, pinia }) => {
// 1. 注册 store 到 devtools
registerPiniaDevtools(app, pinia)
// 2. 提供 HMR 支持
if (__DEV__) {
store._hotUpdate = (newStore) => {
// 热更新逻辑
}
}
// 3. 自定义属性标记
store._customProperties = new Set()
store._hmrPayload = {
state: [],
actions: {},
getters: {},
}
}
// vite.config.js
export default defineConfig({
plugins: [vue()],
})
// store 中使用
if (import.meta.hot) {
import.meta.hot.accept(
acceptHMRUpdate(useCounterStore, import.meta.hot)
)
}
// acceptHMRUpdate 实现
export function acceptHMRUpdate(
initialUseStore: StoreDefinition,
hot: any
) {
// 返回一个接受新 store 的函数
return (newModule) => {
const newUseStore = newModule[Object.keys(newModule)[0]]
if (newUseStore.$id !== initialUseStore.$id) {
console.warn('Store ID changed, skipping HMR')
return
}
// 获取现有 store 并热更新
const existingStore = pinia._s.get(newUseStore.$id)
if (existingStore) {
existingStore._hotUpdate(newUseStore())
}
}
}
// 服务器端
const pinia = createPinia()
const app = createApp(App)
app.use(pinia)
// 渲染前获取数据
await useUserStore().fetchUser()
// 序列化状态
const state = JSON.stringify(pinia.state.value)
// 客户端
const pinia = createPinia()
// 注水(恢复状态)
pinia.state.value = JSON.parse(window.__PINIA_STATE__)
const app = createApp(App)
app.use(pinia)
// hydrate 选项(自定义注水)
export const useStore = defineStore('main', {
state: () => ({
date: useLocalStorage('date', new Date())
}),
hydrate(storeState, initialState) {
storeState.date = useLocalStorage('date', new Date())
}
})
使用的模式
架构特点
// Pinia 单例
let activePinia: Pinia | undefined
export function setActivePinia(pinia: Pinia) {
activePinia = pinia
}
export function getActivePinia() {
return activePinia
}
// Store 单例缓存
function useStore(pinia?: Pinia | null): StoreGeneric {
pinia = pinia || getActivePinia()
// 检查缓存
if (!pinia._s.has(id)) {
// 创建新 store
if (isSetupStore) {
createSetupStore(id, setup, options, pinia)
} else {
createOptionsStore(id, options, pinia)
}
}
// 返回缓存的 store
return pinia._s.get(id)!
}
// 订阅者列表
let subscriptions: SubscriptionCallback[] = []
let actionSubscriptions: StoreOnActionListener[] = []
// 注册观察者
store.$subscribe((mutation, state) => {
console.log('State changed:', mutation)
})
store.$onAction(({ name, args }) => {
console.log('Action called:', name)
})
// 通知观察者
function $patch(partialStateOrMutator) {
// ... 修改状态
triggerSubscriptions(
subscriptions,
{ type: 'patch object', storeId: $id },
pinia.state.value[$id]
)
}
// 在 action 中通知
triggerSubscriptions(actionSubscriptions, {
args, name, store, after, onError
})
// defineStore 是工厂函数的工厂
export function defineStore(id, setup, options) {
// 返回工厂函数
function useStore(pinia?: Pinia | null): StoreGeneric {
pinia = pinia || getActivePinia()
if (!pinia._s.has(id)) {
// 根据类型创建不同的 store
if (isSetupStore) {
createSetupStore(id, setup, options, pinia)
} else {
createOptionsStore(id, options, pinia)
}
}
return pinia._s.get(id)!
}
useStore.$id = id
return useStore
}
// 使用
const useCounterStore = defineStore('counter', () => { ... })
const store = useCounterStore() // 工厂创建实例
// $state 代理
Object.defineProperty(store, '$state', {
get: () => pinia.state.value[$id],
set: (state) => {
$patch(($state) => {
assign($state, state)
})
}
})
// action 代理
const wrappedAction = function () {
setActivePinia(pinia)
// 前置处理
try {
return fn.apply(store, args)
// 后置处理
} catch (error) {
// 错误处理
}
}
// 响应式代理
const store = reactive(partialStore)
graph TD
A[Component] -->|useStore| B[Store]
B -->|read| C[State]
B -->|call| D[Actions]
B -->|computed| E[Getters]
D -->|$patch| C
C -->|reactive| F[Vue Reactivity]
F -->|trigger| G[Watchers]
G -->|update| A
D -->|$onAction| H[Action Subscribers]
C -->|$subscribe| I[State Subscribers]
J[Plugins] -->|extend| B
K[DevTools] -->|inspect| B
style C fill:#42b883
style D fill:#ff6b35
style E fill:#61dafb
sequenceDiagram
participant C as Component
participant S as Store
participant P as $patch
participant R as Reactivity
participant W as Watchers
C->>S: store.count = 1
S->>R: 触发 reactive set
R->>W: 通知 watchers
W->>C: 重新渲染
C->>S: store.$patch({ count: 2 })
S->>P: 调用 $patch
P->>P: 暂停监听 isListening = false
P->>P: mergeReactiveObjects
P->>P: 恢复监听 isListening = true
P->>W: triggerSubscriptions
W->>C: 重新渲染
sequenceDiagram
participant C as Component
participant W as WrappedAction
participant S as Subscribers
participant A as OriginalAction
participant R as Result
C->>W: store.increment()
W->>W: setActivePinia(pinia)
W->>S: triggerSubscriptions (before)
S->>S: 注册 after/onError 回调
W->>A: fn.apply(store, args)
alt Success
A->>R: return result
alt Is Promise
R->>R: .then(value)
R->>S: trigger after callbacks
else Sync
W->>S: trigger after callbacks
end
W->>C: return result
else Error
A-->>W: throw error
W->>S: trigger onError callbacks
W-->>C: throw error
end
优化策略
性能数据
| 指标 | 数值 |
|---|---|
| 包体积 | ~1KB gzipped |
| 创建 Store | <1ms |
| $patch 更新 | <0.1ms |
function $patch(partialStateOrMutator) {
let subscriptionMutation: SubscriptionCallbackMutation
// 暂停 watcher,避免多次触发
isListening = isSyncListening = false
debuggerEvents = []
if (typeof partialStateOrMutator === 'function') {
// 函数式:用户自己修改
partialStateOrMutator(pinia.state.value[$id])
} else {
// 对象式:合并对象
mergeReactiveObjects(pinia.state.value[$id], partialStateOrMutator)
}
// 恢复 watcher
const myListenerId = (activeListener = Symbol())
nextTick().then(() => {
if (activeListener === myListenerId) {
isListening = true
}
})
isSyncListening = true
// 统一触发订阅(只触发一次)
triggerSubscriptions(subscriptions, subscriptionMutation, state)
}
// 未使用的 store 会被 tree-shake
export const useUserStore = defineStore('user', () => { ... })
export const useCartStore = defineStore('cart', () => { ... })
export const useSettingsStore = defineStore('settings', () => { ... })
// 只使用 user store
import { useUserStore } from './stores'
const user = useUserStore()
// cart 和 settings 的代码不会打包
// defineStore 标记为无副作用
/*! #__NO_SIDE_EFFECTS__ */
export function defineStore(...): StoreDefinition {
// ...
}
// mapHelpers 使用动态导入
export const mapStores = __DEV__
? createMapHelper()
: noop
| 特性 | Vuex | Pinia |
|---|---|---|
| Mutations | ✅ 必须 | ❌ 移除 |
| 模块嵌套 | ✅ 需要 | ❌ 扁平化 |
| TypeScript | ⚠️ 需要额外配置 | ✅ 开箱即用 |
| DevTools | ✅ 支持 | ✅ 支持 |
| SSR | ✅ 支持 | ✅ 支持 |
| 包体积 | ~3KB | ~1KB |
| Setup 语法 | ❌ 不支持 | ✅ 支持 |
推荐做法
代码组织
// stores/
// ├── user.ts
// ├── cart.ts
// └── settings.ts
// user.ts
export const useUserStore = defineStore(
'user',
() => {
const user = ref(null)
const isLoggedIn = computed(() => !!user.value)
async function login(credentials) {
user.value = await api.login(credentials)
}
return { user, isLoggedIn, login }
}
)
// ❌ 直接解构(丢失响应性)
const { count, double } = useCounterStore()
// count 和 double 不是响应式的!
// ✅ 使用 storeToRefs
import { storeToRefs } from 'pinia'
const { count, double } = storeToRefs(useCounterStore())
// ❌ 在 getter 中修改状态
getters: {
doubleCount(state) {
state.count++ // 不要这样做!
return state.count * 2
}
}
// ❌ 嵌套 store
const useParentStore = defineStore('parent', () => {
const childStore = useChildStore() // 可能导致循环依赖
})
// ✅ 在需要时获取 store
const useParentStore = defineStore('parent', () => {
const getChildStore = () => useChildStore()
})
Q1: 如何在组件外使用 Store?
// ❌ 错误
const store = useUserStore()
// ✅ 正确
import { getActivePinia } from 'pinia'
const store = useUserStore(
getActivePinia()
)
Q2: 如何重置 Store?
// Options Store
store.$reset()
// Setup Store
const initial = { count: 0 }
const useStore = defineStore('id', () => {
const count = ref(initial.count)
const $reset = () => {
count.value = initial.count
}
return { count, $reset }
})
官方资源
相关技术
核心要点
源码亮点
💡 Pinia 证明了简单的设计往往是最强大的
| 资源 | 链接 |
|---|---|
| Pinia 官方文档 | pinia.vuejs.org |
| GitHub 仓库 | github.com/vuejs/pinia |
| Vue 3 文档 | vuejs.org |
| Vue Reactivity | Reactivity in Depth |
| TypeScript Handbook | typescriptlang.org |
感谢阅读!💚