ae39e45460
NOVAS SKILLS: - next-best-practices v0.1.0 (CLEAN) — Next.js App Router, RSC, caching, data - nextjs-patterns v1.0.0 (CLEAN) — Next.js 15: Server Actions, route handlers - vite v1.0.0 (CLEAN) — env vars, aliases, proxy, CJS compat - uncle-bob v1.0.0 (CLEAN) — Clean Code, SOLID, Clean Architecture - clean-code-review v1.0.0 (CLEAN) — naming, guard clauses, anti-patterns, refactoring - vue v1.0.0 (CLEAN) — Vue framework - vue-composition-api-best-practices v1.0.0 (CLEAN) — composables, Pinia, reactivity BIBLIOTECA INTELIGENTE libs/ (10 dominios, 11 arquivos): - typescript/ — TS safe + generics gotchas - react/ — Next.js App Router + Vite config - vue/ — Composition API + Pinia - linux/ — System diagnostic cheatsheet - database/ — PostgreSQL + MySQL patterns - browser/ — Chromium CLI + E2E testing - security/ — SAST audit (OWASP Top 10) - best-practices/ — Clean Code + SOLID + Clean Architecture - deploy/ — Docker multi-stack + OpenClaw ops - + INDEX.md como guia de navegacao .learnings/ — LRN-20260519-003 criado (biblioteca compartilhada)
4.9 KiB
4.9 KiB
Store Without 模式
问题
Pinia 的 useStore() 默认依赖 Vue 组件上下文(inject/provide)。在组件外(hooks、utils、plugins、路由守卫、axios 拦截器)直接调用会抛出错误:
Error: "getActivePinia()" was called but there was no active Pinia.
解决方案:Store Without 模式
每个 store 模块额外导出一个 useXxxStoreWithOut 函数,接收全局 pinia 实例作为参数,使 store 可在任意上下文中安全访问。
模式定义
// store/modules/app.ts
import { defineStore } from 'pinia'
import { store } from '@/store'
export const useAppStore = defineStore('app', {
// ... store 定义
})
// 在组件外使用时,传入全局 pinia 实例
export const useAppStoreWithOut = () => {
return useAppStore(store)
}
全局 Pinia 实例
// store/index.ts
import { createPinia } from 'pinia'
const pinia = createPinia()
export default pinia
// 导出 store 供 Without 函数使用
export const store = pinia
使用规则
何时使用哪个
| 函数 | 使用场景 | 原因 |
|---|---|---|
useAppStore() |
Vue 组件 <script setup> 内 |
自动从组件上下文获取 pinia |
useAppStoreWithOut() |
hooks、utils、plugins、路由守卫等 | 组件上下文不可用,需显式传入 pinia |
在 Vue 组件中(始终使用标准方式)
// ✅ GOOD:组件内使用标准方式
<script setup lang="ts">
import { useAppStore } from '@/store/modules/app'
const appStore = useAppStore()
</script>
// ❌ BAD:组件内使用 WithOut 是多余的
<script setup lang="ts">
import { useAppStoreWithOut } from '@/store/modules/app'
const appStore = useAppStoreWithOut() // 可以工作但不必要
</script>
在 Hooks / Utils / Plugins 中(必须使用 WithOut)
// ✅ GOOD:hooks 中使用 WithOut
// hooks/web/useSideCategory.ts
import { useBusinessStoreWithOut } from '@/store/modules/business'
export function useSideCategory() {
const businessStore = useBusinessStoreWithOut()
const categories = computed(() => businessStore.getSideCategory)
return { categories }
}
// ✅ GOOD:utils 中使用 WithOut
// utils/migration.ts
import { useBusinessStoreWithOut } from '@/store/modules/business'
export async function migrateOnlineIcons() {
const businessStore = useBusinessStoreWithOut()
// ...
}
// ❌ BAD:组件外直接使用标准方式会报错
// utils/migration.ts
import { useBusinessStore } from '@/store/modules/business'
export async function migrateOnlineIcons() {
const businessStore = useBusinessStore() // Error: no active Pinia
}
命名规范
所有模块遵循统一命名规范:
| Store 模块 | 标准函数 | WithOut 函数 |
|---|---|---|
app.ts |
useAppStore |
useAppStoreWithOut |
business.ts |
useBusinessStore |
useBusinessStoreWithOut |
dict.ts |
useDictStore |
useDictStoreWithOut |
locale.ts |
useLocaleStore |
useLocaleStoreWithOut |
规则:use{ModuleName}StoreWithOut — 模块名首字母大写 + Store + WithOut(注意大小写)。
实现清单
每个 store 模块必须:
- 导出标准
useXxxStore函数(defineStore的返回值) - 导出
useXxxStoreWithOut函数,内部调用useXxxStore(store) - 从
@/store导入全局store实例 - WithOut 函数放在文件底部,紧跟标准函数之后
为什么不直接使用 useXxxStore(pinia)?
理论上可以直接调用 useAppStore(pinia),但 WithOut 函数提供了:
- 语义明确 — 函数名直接表达"在组件外使用"的意图
- 统一入口 — 不需要每个调用方都 import
store,减少依赖 - 集中管理 — 如果 pinia 实例获取方式变更,只需改 WithOut 函数
- 可搜索 — 搜索
WithOut即可找到所有组件外使用 store 的地方
替代方案:storeToRefs 注意事项
注意 useXxxStoreWithOut() 返回的是 store 实例,如需解构响应式属性,仍需使用 storeToRefs:
import { storeToRefs } from 'pinia'
import { useAppStoreWithOut } from '@/store/modules/app'
export function useAppInfo() {
const appStore = useAppStoreWithOut()
const { pure, theme } = storeToRefs(appStore) // 保持响应式
return { pure, theme }
}
总结
| 优势 | 说明 |
|---|---|
| 解决组件外访问 | 核心价值,让 store 可在任意上下文使用 |
| 命名约定清晰 | WithOut 后缀一目了然 |
| 减少样板代码 | 调用方无需 import store |
| 易于维护 | pinia 实例变更只需改一处 |
| 可追溯 | 搜索 WithOut 可定位所有组件外使用 |