
第10课 Pinia 状态管理理解为什么需要 Pinia跨组件共享状态掌握 store 的 state / getters / actions 三件套在组件中正确读写 store含storeToRefs的作用结合 localStorage 实现登录状态持久化Pinia官网Pinia | The intuitive store for Vue.js1. 为什么需要 Pinia1.1 props / emit 的局限父子组件逐层传递数据中间组件被迫当“传声筒”跨页面、跨层级共享数据十分麻烦。1.2 Pinia 的作用全局共享状态任意组件直接读写无需层层传递状态变化自动通知所有使用方响应式官方出品与 Vue3 完美配合1.3 什么数据适合放 store多个组件都要用到数据会发生变化例当前登录用户、购物车、主题色、权限信息组件内部用完即丢的数据留在组件自己的ref/reactive里不要什么都丢进 store。2. Store 三件套state / getters / actions成员职责类比对应 Vue 知识state存储数据储物柜的柜体ref/reactivegetters根据 state 计算派生数据有缓存取件窗口现用现算computedactions修改数据支持异步、调用其它 action业务柜台办手续methods写法规则state必须是函数返回对象避免多个实例共享同一对象getters接收state作为参数箭头函数actions用普通函数内部通过this访问 state 和其它 action3. 安装与注册npminstallpinia在main.js中注册import{createApp}fromvueimport{createPinia}frompinia// 1. 引入importAppfrom./App.vueconstappcreateApp(App)app.use(createPinia())// 2. 注册一定要在用 store 之前app.mount(#app)4. 创建第一个 store计数器示例文件src/stores/counter.jsimport{defineStore}frompiniaexportconstuseCounterStoredefineStore(counter,{state:()({count:0}),getters:{doubleCount:(state)state.count*2},actions:{increment(){this.count// 用 this 访问 state},addBy(step){this.countstep}}})defineStore第一个参数是 store 的唯一 ID全项目不可重复。5. 在组件中使用 store5.1 基本读写script setup import { storeToRefs } from pinia import { useCounterStore } from ../stores/counter const counterStore useCounterStore() // 获取 store 实例 // 解构 state/getters 并保持响应式 const { count, doubleCount } storeToRefs(counterStore) // actions 可直接解构函数没有响应式丢失问题 const { increment } counterStore function handleAdd() { counterStore.increment() // 调用 action 修改 } /script template p{{ count }}/p p{{ doubleCount }}/p button clickhandleAdd1/button /template5.2 直接解构 vsstoreToRefs直接解构(const { count } counterStore)数据会丢失响应式因为 store 内部用了reactive直接解构会断开响应式连接storeToRefs解构出来的每个值都是ref保持响应式模板中自动解包脚本中需.valueactions 可以直接解构函数没有响应式问题5.3 修改数据的两种姿势直接修改store.count适合简单操作通过 actionstore.increment()有业务逻辑、需要校验或组合多个字段时推荐6. 实战登录状态管理核心案例6.1 创建用户 Store (src/stores/user.js)import{defineStore}frompinia// localStorage 键名统一管理避免拼写错误constSTORAGE_KEYadmin_user// 从 localStorage 恢复登录状态functionloadUserFromStorage(){try{constsavedlocalStorage.getItem(STORAGE_KEY)returnsaved?JSON.parse(saved):null}catch{returnnull// 数据损坏时按未登录处理不崩溃}}exportconstuseUserStoredefineStore(user,{state:()({userInfo:loadUserFromStorage()// 初始化时自动恢复}),getters:{isLoggedIn:(state)state.userInfo!null,displayName:(state){if(!state.userInfo)return未登录return${state.userInfo.name}${state.userInfo.role}}},actions:{// 登录保存用户信息到 state同时写入 localStoragelogin(userInfo){this.userInfouserInfo localStorage.setItem(STORAGE_KEY,JSON.stringify(userInfo))},// 退出清空 state同时删除 localStoragelogout(){this.userInfonulllocalStorage.removeItem(STORAGE_KEY)}}})6.2 登录页改造 (LoginView.vue)import{useUserStore}from../stores/userconstuserStoreuseUserStore()// 登录成功时调用userStore.login({name:loginForm.username,role:管理员,loginTime:newDate().toLocaleString()})router.replace({name:dashboard})6.3 主布局顶部栏显示登录用户 (MainLayout.vue)script setup import { useUserStore } from ../stores/user const userStore useUserStore() function handleLogout() { userStore.logout() router.replace({ name: login }) } /script template span你好{{ userStore.displayName }}/span button clickhandleLogout退出登录/button /template6.4 路由守卫改造 (router/index.js)import{useUserStore}from../stores/userrouter.beforeEach((to,from,next){// 必须在守卫回调内部调用 useUserStore()此时 Pinia 已激活constuserStoreuseUserStore()if(to.path!/login!userStore.isLoggedIn){next({name:login})}else{next()}})7. 持久化三句话localStorage 配合时机操作代码位置登录时写入localStorage.setItemloginaction初始化时恢复localStorage.getItemstate初始化函数退出时清除localStorage.removeItemlogoutaction三个容易踩的坑对象必须JSON.stringify写入JSON.parse读出解析存档时用try/catch防止数据损坏导致页面崩溃退出时 store 和 localStorage两处同时清空否则刷新会“死而复生”8. 常见错误排查现象原因解决getActivePinia was called with no active Pinia忘记app.use(createPinia())在main.js中注册解构的 state 值不更新直接解构 store使用storeToRefs()actions 中this是 undefinedactions 写成箭头函数actions 必须用普通函数state 写成对象而非函数state: { count: 0 }改为state: () ({ count: 0 })路由守卫报 store 相关错误在文件顶层调用了useXxxStore()移到beforeEach回调内部退出后刷新又回到后台只清了 store 没清 localStoragelogout 中同时removeItem刷新后页面崩溃/白屏localStorage 数据损坏JSON.parse抛异常恢复函数加try/catch总结Pinia 全局公告栏任意组件读写共享数据无需 props 传递Store 三件套state 存、getters 算、actions 改组件使用useXxxStore()获取实例storeToRefs保持响应式解构持久化登录写入 → 初始化恢复 → 退出清除三句话守卫升级从localStorage.isLogin升级为store.isLoggedIn数据更完整