pinia负责状态管理,组合式函数封装业务逻辑,二者职责分离又协同:状态定义在store,逻辑封装在usexxx.ts中调用store,组件按需消费。

Pinia 配合 Vue 3 组合式函数(Composable)是当前主流的状态管理 + 逻辑复用方案:Pinia 负责跨组件、持久化、可调试的状态管理;组合式函数负责封装可复用的业务逻辑(比如表单校验、请求处理、权限判断等),两者职责分明,又能自然协同。
状态定义在 Pinia,逻辑封装在 Composable
不要把状态和逻辑都塞进一个 composable 里。推荐做法是:
- 在 Pinia store 中定义响应式状态(
ref、computed)、动作(actions)和获取器(getters) - 在独立的
useXXX.ts文件中编写组合式函数,内部调用 store 实例(通过useStore()),专注流程控制、副作用处理、事件响应等 - 组件中同时使用
useStore()和useXXX(),按需消费状态与逻辑
例如:用户登录逻辑可拆为
stores/user.ts
export const useUserStore = defineStore('user', () => {
const token = ref<string null>(null)
const userInfo = ref<user null>(null)
const isLoggedIn = computed(() => !!token.value)
function setAuth(data: { token: string; user: User }) {
token.value = data.token
userInfo.value = data.user
}
function clearAuth() {
token.value = null
userInfo.value = null
}
return { token, userInfo, isLoggedIn, setAuth, clearAuth }
})
</user></string>
composables/useLogin.ts
使用 `@ainative/vue-sdk` 为 Vue 3 应用添加 AI 聊天功能。适用于以下场景:(1) 安装 `@ainative/vue-sdk`,(2) 在 Vue 组件中使用 `useChat` 组合式函数,(3) 提供……
export function useLogin() {
const userStore = useUserStore()
const router = useRouter()
const loading = ref(false)
const error = ref<string null>(null)
async function login(credentials: { username: string; password: string }) {
loading.value = true
error.value = null
try {
const res = await api.login(credentials)
userStore.setAuth({
token: res.token,
user: res.user
})
router.push('/dashboard')
} catch (e) {
error.value = e?.message || '登录失败'
} finally {
loading.value = false
}
}
return { loading, error, login }
}
</string>
避免 store 实例重复创建或滥用
Pinia store 是单例,但需注意调用时机和作用域:
- 在 composable 中调用
useUserStore()是安全的,它会自动绑定当前组件的生命周期(如组件卸载时自动清理监听) - 不要在 composable 外部(比如普通工具函数)直接调用 store —— 这会导致脱离响应式上下文或 SSR 不兼容
- 若 composable 需要“多实例”行为(如多个独立的搜索框逻辑),可接受参数并返回闭包,store 实例仍由调用方传入,保持解耦
组合式函数可主动订阅 store 变化
当需要对 store 状态变更做出反应(非响应式模板渲染场景),可用 store.$subscribe 或 watch:
-
store.$subscribe:适合轻量、全局性响应(如 token 变更后刷新请求头) -
watch:适合组件级、带副作用的响应(如 userInfo 改变后触发埋点)
示例:在 useUserProfile.ts 中监听用户信息加载完成
export function useUserProfile() {
const userStore = useUserStore()
watch(
() => userStore.userInfo,
(newVal) => {
if (newVal) {
trackEvent('user_profile_loaded', { id: newVal.id })
}
},
{ immediate: true }
)
return { profile: userStore.userInfo }
}
测试与 Tree-shaking 更友好
拆分后,逻辑层(composable)可脱离 Vue 组件单独单元测试(mock store 即可);而 store 层本身也支持 Jest/Vitest 测试。同时,未使用的 composable 不会引入 store,打包时更容易被摇掉 —— 尤其适合中大型项目按功能动态导入。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










