vue3 ts pinia
下面给你一套 「能直接复制用、结构清晰、企业级规范」的 Pinia 分组 + 模块间调用完整示例代码。
✅ Vue 3
✅ TypeScript
✅ Pinia
✅ 模块化
✅ 有 state / getter / action
✅ 有模块间调用
一、目录结构(重点)
src/
├── store/
│ ├── modules/
│ │ ├── user.ts
│ │ └── order.ts
│ ├── index.ts
│ └── types.ts
二、类型定义(store/types.ts)
// 用户
export interface UserInfo {
id: number
name: string
}
// 订单
export interface Order {
id: number
title: string
price: number
}
三、user 模块(store/modules/user.ts)
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import type { UserInfo } from '../types'
export const useUserStore = defineStore('user', () => {
// ===== state =====
const token = ref('')
const userInfo = ref<UserInfo | null>(null)
// ===== getter =====
const isLogin = computed(() => !!token.value)
// ===== action =====
function setToken(val: string) {
token.value = val
}
function setUserInfo(info: UserInfo) {
userInfo.value = info
}
function logout() {
token.value = ''
userInfo.value = null
}
return {
token,
userInfo,
isLogin,
setToken,
setUserInfo,
logout,
}
})
四、order 模块(store/modules/order.ts)
✅ 这里演示「调用 user 模块」
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import { useUserStore } from './user'
import type { Order } from '../types'
export const useOrderStore = defineStore('order', () => {
// ===== state =====
const orderList = ref<Order[]>([])
// ===== getter =====
const totalPrice = computed(() =>
orderList.value.reduce((sum, item) => sum + item.price, 0)
)
// ===== action =====
function submitOrder(order: Order) {
const userStore = useUserStore()
if (!userStore.isLogin) {
throw new Error('请先登录')
}
orderList.value.push(order)
}
return {
orderList,
totalPrice,
submitOrder,
}
})
✅ 关键点
const userStore = useUserStore()
userStore.isLogin
五、统一导出(store/index.ts)
import { createPinia } from 'pinia'
import type { App } from 'vue'
export const pinia = createPinia()
export function setupStore(app: App) {
app.use(pinia)
}
// 模块统一导出
export * from './modules/user'
export * from './modules/order'
六、main.ts 注册
import { createApp } from 'vue'
import App from './App.vue'
import { setupStore } from './store'
const app = createApp(App)
setupStore(app)
app.mount('#app')
七、在组件中使用(完整示例)
1️⃣ 使用 user store
<script setup lang="ts">
import { useUserStore } from '@/store'
const userStore = useUserStore()
</script>
<template>
<div>
<p>是否登录:{{ userStore.isLogin }}</p>
<button @click="userStore.setToken('abc123')">登录</button>
<button @click="userStore.logout">退出</button>
</div>
</template>
2️⃣ 使用 order store(并调用 user)
<script setup lang="ts">
import { useOrderStore } from '@/store'
import { useUserStore } from '@/store'
const orderStore = useOrderStore()
const userStore = useUserStore()
</script>
<template>
<div>
<button
:disabled="!userStore.isLogin"
@click="orderStore.submitOrder({ id: 1, title: '商品A', price: 99 })"
>
提交订单
</button>
<ul>
<li v-for="item in orderStore.orderList" :key="item.id">
{{ item.title }} - ¥{{ item.price }}
</li>
</ul>
<p>总价:¥{{ orderStore.totalPrice }}</p>
</div>
</template>
八、模块调用关系图(很重要)
App
├── useUserStore
└── useOrderStore
└── useUserStore(跨模块调用)
✅ Pinia 官方推荐这种方式
✅ 无需 event bus
✅ 无层级嵌套
一、Pinia 是什么(核心认知)
✅ Vue 官方状态管理库
✅ Vuex 的继任者
✅ 专为 Vue 3 + Composition API 设计
核心特点:
- 没有 mutations ✅
- 完整的 TS 支持 ✅
- 模块化天生支持 ✅
- 支持 DevTools ✅
- 支持服务端渲染(SSR)✅
二、Pinia 基础知识点(必会)
1️⃣ 创建 Pinia
import { createPinia } from 'pinia'
const pinia = createPinia()
app.use(pinia)
2️⃣ 定义 Store(两种写法)
✅ 组合式(推荐)
export const useUserStore = defineStore('user', () => {
const token = ref('')
function setToken(val: string) {
token.value = val
}
return { token, setToken }
})
⚠️ 选项式(了解)
export const useUserStore = defineStore('user', {
state: () => ({ token: '' }),
actions: {
setToken(val: string) {
this.token = val
},
},
})
3️⃣ State(状态)
const count = ref(0)
等价于:
state: () => ({ count: 0 })
✅ 响应式
✅ 支持 ref / reactive
4️⃣ Getter(派生状态)
const double = computed(() => count.value * 2)
✅ 缓存
✅ 类似 Vue computed
5️⃣ Action(修改状态)
function increment() {
count.value++
}
✅ 同步 / 异步都可以
✅ 不需要 mutation
三、Pinia 进阶知识点(中高级)
6️⃣ Store 的使用方式
const userStore = useUserStore()
userStore.setToken('xxx')
✅ 可在:
- setup
- 普通函数
- service
- 非组件代码中调用
7️⃣ 多 Store 之间通信
const userStore = useUserStore()
const orderStore = useOrderStore()
✅ 天然支持
✅ 无嵌套地狱
8️⃣ Store 分组 / 模块化
store/
├── user.ts
├── order.ts
✅ 推荐按业务拆分
✅ 不要一个 store 管全部
9️⃣ Store 重置(reset)
userStore.$reset()
✅ 重置为初始 state
🔟 订阅状态变化
userStore.$subscribe((mutation, state) => {
console.log(mutation, state)
})
✅ 用于埋点 / 日志 / 调试
四、Pinia 高级知识点(企业级)
1️⃣1️⃣ 持久化(非常常见)
npm i pinia-plugin-persistedstate
export const useUserStore = defineStore(
'user',
() => { ... },
{
persist: true,
}
)
1️⃣2️⃣ SSR / Nuxt 支持
const store = useUserStore(pinia)
✅ 同构渲染
✅ hydration 自动处理
1️⃣3️⃣ Store 的解构(必须知道)
❌ 错误(失去响应性)
const { token } = userStore
✅ 正确
import { storeToRefs } from 'pinia'
const { token } = storeToRefs(userStore)
1️⃣4️⃣ Store 作为参数传递(解耦)
function logout(userStore: ReturnType<typeof useUserStore>) {
userStore.$reset()
}
1️⃣5️⃣ 自定义插件(高级)
export function myPiniaPlugin({ store }) {
store.$onAction(({ name, args }) => {
console.log(name, args)
})
}
五、Pinia 与 Vuex 对比(面试高频)
| 对比项 | Pinia | Vuex |
|---|---|---|
| mutations | ❌ | ✅ |
| TS 支持 | ✅✅✅ | ⚠️ |
| 模块化 | 原生 | 手动 |
| 代码体积 | 更小 | 较大 |
| 官方态度 | ✅ 推荐 | ❌ 维护模式 |
六、Pinia 最佳实践(企业级总结)
✅ 一个业务一个 store
✅ 用组合式 API
✅ store 只管状态,不管请求
✅ 请求放在 service 层
✅ 大型项目用 domain 分层
七、Pinia 学习路线(建议)
基础用法
↓
模块化分组
↓
TS 类型设计
↓
持久化
↓
工程化拆分(store / service / model)
↓
性能 & 架构优化
下面我按 「面试标准答案 + 加分点」 的方式,把这 6 个 Pinia 高频问题一次性讲清楚,你可以直接背,也可以理解后用自己的话讲。
1️⃣ Pinia 和 Vuex 的区别?(必考)
✅ 标准答案
| 对比点 | Pinia | Vuex |
|---|---|---|
| 官方定位 | Vue3 官方推荐 | Vue2 时代产物 |
| mutations | ❌ 移除 | ✅ 必须有 |
| actions | 支持同步 / 异步 | 支持 |
| 模块化 | 天生支持 | 需要 namespace |
| TypeScript | 原生友好 | 类型复杂 |
| 代码体积 | 更小 | 较大 |
| 心智模型 | 简单 | 偏重 |
✅ 加分一句话
Pinia 是 Vuex 的精神继承者,专门为 Vue 3 和 Composition API 设计。
2️⃣ 为什么 Pinia 不需要 mutation?
✅ 标准答案
- mutation 的存在是为了 追踪同步变更
- Pinia 使用
ref / reactive - Vue DevTools 已经能追踪
.value的变化 - 同步 / 异步都可以在 action 中完成
✅ 面试原话
在 Pinia 中,state 本身就是响应式的,action 可以直接修改 state,不再需要 mutation 这一层约束。
3️⃣ Pinia 的 state 本质是什么?
✅ 标准答案
在 组合式写法 中:
const count = ref(0)
👉 state 的本质就是 ref 或 reactive 对象
Pinia 内部会把它们收集起来,暴露给 DevTools 和插件。
✅ 一句话总结
Pinia 的 state = Composition API 的响应式数据
4️⃣ 如何在组件外使用 Pinia?(高频)
✅ 场景
- service
- utils
- 请求拦截器
- 非 Vue 文件
✅ 标准写法
import { createPinia } from 'pinia'
const pinia = createPinia()
const userStore = useUserStore(pinia)
✅ 在项目中(已注册)
import { useUserStore } from '@/store'
const userStore = useUserStore()
✅ 面试加分点
Pinia 不依赖组件上下文,只要传入 pinia 实例即可在任何地方使用。
5️⃣ Pinia 如何实现持久化?
✅ 标准答案(最常用)
使用官方生态插件:
npm i pinia-plugin-persistedstate
export const useUserStore = defineStore(
'user',
() => { ... },
{
persist: true,
}
)
✅ 可配置
persist: {
key: 'user-store',
storage: localStorage,
}
✅ 加分点
Pinia 本身不负责持久化,通过插件机制实现,符合单一职责原则。
6️⃣ storeToRefs 是干嘛的?
✅ 问题背景
const { token } = userStore // ❌ 失去响应性
✅ 正确答案
import { storeToRefs } from 'pinia'
const { token } = storeToRefs(userStore)
✅ 原理简述
- store 本身是 reactive
- 直接解构会破坏响应性
storeToRefs只把 state / getter 转成ref
storeToRefs用于在解构 Pinia store 时保留响应性。
✅ Composition API
✅ 去掉 mutation
✅ TypeScript 原生支持
更多推荐
所有评论(0)