本文作者:LIO
前言
这是一套企业级可直接落地的 Vue3 + Pinia 完整方案,包含:封装、异步请求、路由权限、持久化、模块化、TS 类型,复制即用。
一、目录结构(标准企业版)
src/
├── stores/ # Pinia 状态管理
│ ├── modules/ # 按模块拆分
│ │ ├── user.ts # 用户、登录、权限
│ │ ├── app.ts # 全局配置、主题、菜单
│ │ └── counter.ts # 示例
│ └── index.ts # 统一导出
├── api/ # 接口请求
│ └── user.ts
├── router/ # 路由
│ └── index.ts
├── types/ # TS 类型
└── main.ts
二、统一封装入口:stores/index.ts
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
export * from './modules/user'
export * from './modules/app'
export * from './modules/counter'
export default pinia
在 main.ts 引入:
import { createApp } from 'vue'
import pinia from './stores'
import App from './App.vue'
import router from './router'
const app = createApp(App)
app.use(pinia)
app.use(router)
app.mount('#app')
三、最常用模块:用户权限(user.ts)
stores/modules/user.ts
import { defineStore } from 'pinia'
import type { UserInfo, LoginParams } from '@/types/user'
import { loginApi, getUserInfoApi } from '@/api/user'
export const useUserStore = defineStore('user', {
persist: true,
state: () => ({
token: '',
userInfo: null as UserInfo | null,
roles: [] as string[],
permissions: [] as string[],
}),
getters: {
isLogin: (state) => !!state.token,
isAdmin: (state) => state.roles.includes('admin'),
},
actions: {
// 登录
async login(loginForm: LoginParams) {
const res = await loginApi(loginForm)
this.token = res.token
return res
},
// 获取用户信息 + 权限
async fetchUserInfo() {
const res = await getUserInfoApi()
this.userInfo = res.userInfo
this.roles = res.roles
this.permissions = res.permissions
return res
},
// 退出登录
logout() {
this.token = ''
this.userInfo = null
this.roles = []
this.permissions = []
},
},
})
四、全局配置模块(app.ts)
import { defineStore } from 'pinia'
export const useAppStore = defineStore('app', {
persist: true,
state: () => ({
theme: 'light',
sidebarCollapse: false,
language: 'zh-CN',
}),
actions: {
toggleSidebar() {
this.sidebarCollapse = !this.sidebarCollapse
},
switchTheme(theme: string) {
this.theme = theme
},
},
})
五、异步请求封装(api/user.ts)
// 模拟接口
export async function loginApi(data: any) {
return new Promise(resolve => {
setTimeout(() => {
resolve({ token: 'TOKEN_' + Date.now() })
}, 300)
})
}
export async function getUserInfoApi() {
return new Promise(resolve => {
setTimeout(() => {
resolve({
userInfo: { id: 1, name: '管理员' },
roles: ['admin'],
permissions: ['user:add', 'user:edit', 'user:del'],
})
}, 300)
})
}
六、路由权限控制(企业核心)
router/index.ts
import { createRouter, createWebHistory } from 'vue-router'
import { useUserStore } from '@/stores'
const routes = [
{
path: '/login',
component: () => import('@/views/login.vue'),
},
{
path: '/',
component: () => import('@/layout/index.vue'),
redirect: '/home',
children: [
{ path: 'home', component: () => import('@/views/home.vue') },
{ path: 'user', component: () => import('@/views/user.vue') },
],
},
]
const router = createRouter({
history: createWebHistory(),
routes,
})
// 路由守卫
router.beforeEach((to, from, next) => {
const userStore = useUserStore()
const isLogin = userStore.isLogin
if (to.path === '/login') {
next()
return
}
if (!isLogin) {
next('/login')
return
}
next()
})
export default router
七、组件内使用(完整版)
<template>
<div>
<div v-if="userStore.isLogin">
欢迎:{{ userStore.userInfo?.name }}
<button @click="userStore.logout">退出</button>
</div>
<button @click="handleLogin">登录</button>
</div>
</template>
<script setup lang="ts">
import { useUserStore } from '@/stores'
import { storeToRefs } from 'pinia'
const userStore = useUserStore()
const { isLogin, userInfo } = storeToRefs(userStore)
const handleLogin = async () => {
await userStore.login({ username: 'admin', password: '123456' })
await userStore.fetchUserInfo()
}
</script>
八、Pinia 高频实用技巧
1. 解构保持响应式
import { storeToRefs } from 'pinia'
const { count, doubleCount } = storeToRefs(counterStore)
2. 批量修改 state
userStore.$patch({
token: 'new-token',
roles: ['editor'],
})
3. 重置整个 store
userStore.$reset()
4. 监听 state 变化
userStore.$subscribe((mutation, state) => {
console.log('user 状态变化', state)
})
5. 监听 actions 执行
userStore.$onAction(({ name, after }) => {
if (name === 'login') {
after(() => console.log('登录完成'))
}
})
九、给你一套可直接复制的 JS 简化版
如果你不用 TS,直接用这个:
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
persist: true,
state: () => ({
token: '',
userInfo: null,
roles: [],
}),
actions: {
async login(form) {
const res = await loginApi(form)
this.token = res.token
},
logout() {
this.$reset()
},
},
})
——个人观点 · 仅供参考——