基础篇七 Nuxt4 Pinia 集成:更强大的状态管理

张开发
2026/4/13 15:53:55 15 分钟阅读

分享文章

基础篇七 Nuxt4 Pinia 集成:更强大的状态管理
文章目录一、安装配置二、创建 Store三、使用 Store四、解构使用五、用户 Store 示例六、购物车 Store七、Store 组合八、Pinia 插件九、DevTools 调试十、SSR 注意事项总结个人网站上一篇我们学了useState适合简单的状态管理。但项目复杂了useState就有点力不从心了——没有 DevTools、没有插件生态、缺少时间旅行调试……这时候就需要 Pinia 出场了。一、安装配置Pinia 是 Vue 官方推荐的状态管理库Nuxt 集成非常简单pnpmaddpinia/nuxt pinia// nuxt.config.tsexportdefaultdefineNuxtConfig({modules:[pinia/nuxt]})就这么简单Pinia 已经配置好了二、创建 StorePinia 使用defineStore创建状态仓库。推荐使用 Setup Store 语法// stores/counter.tsexportconstuseCounterStoredefineStore(counter,(){// stateconstcountref(0)// gettersconstdoubleCountcomputed(()count.value*2)// actionsconstincrement()count.valueconstdecrement()count.value--constreset()count.value0return{count,doubleCount,increment,decrement,reset}})也可以用 Options Store 语法// stores/counter.tsexportconstuseCounterStoredefineStore(counter,{state:()({count:0}),getters:{doubleCount:(state)state.count*2},actions:{increment(){this.count},decrement(){this.count--},reset(){this.count0}}})两种写法效果一样推荐 Setup 语法更符合 Vue 3 Composition API 风格。三、使用 Storescript setup langts const counter useCounterStore() // 直接访问 state console.log(counter.count) // 访问 getter console.log(counter.doubleCount) // 调用 action const handleIncrement () { counter.increment() } // 重置状态 const handleReset () { counter.$reset() } /script template div p计数: {{ counter.count }}/p p双倍: {{ counter.doubleCount }}/p button clickcounter.increment1/button button clickcounter.decrement-1/button button clickhandleReset重置/button /div /template四、解构使用直接解构会失去响应性需要用storeToRefsscript setup langts import { storeToRefs } from pinia const counter useCounterStore() // ❌ 错误失去响应性 const { count, doubleCount } counter // ✅ 正确使用 storeToRefs const { count, doubleCount } storeToRefs(counter) // actions 可以直接解构 const { increment, decrement, reset } counter /script template p{{ count }}/p button clickincrement1/button /template五、用户 Store 示例实际项目中的用户状态管理// stores/user.tsinterfaceUser{id:numbername:stringemail:stringavatar:stringrole:admin|user}exportconstuseUserStoredefineStore(user,(){constuserrefUser|null(null)consttokenrefstring|null(null)constisLoggedIncomputed(()!!user.value!!token.value)constisAdmincomputed(()user.value?.roleadmin)constloginasync(email:string,password:string){constresponseawait$fetch(/api/login,{method:POST,body:{email,password}})user.valueresponse.user token.valueresponse.token// 持久化到 cookieconsttokenCookieuseCookie(token)tokenCookie.valueresponse.token}constlogoutasync(){await$fetch(/api/logout)user.valuenulltoken.valuenullconsttokenCookieuseCookie(token)tokenCookie.valuenull}constfetchUserasync(){if(!token.value)returntry{constresponseawait$fetch(/api/user)user.valueresponse.user}catch{logout()}}constupdateUser(updates:PartialUser){if(user.value){user.value{...user.value,...updates}}}return{user,token,isLoggedIn,isAdmin,login,logout,fetchUser,updateUser}})使用script setup langts const userStore useUserStore() const { user, isLoggedIn, isAdmin } storeToRefs(userStore) /script template div v-ifisLoggedIn img :srcuser?.avatar / span{{ user?.name }}/span span v-ifisAdmin classbadge管理员/span button clickuserStore.logout退出/button /div /template六、购物车 Store// stores/cart.tsinterfaceCartItem{id:numberproductId:numbername:stringprice:numberquantity:numberimage:string}exportconstuseCartStoredefineStore(cart,(){constitemsrefCartItem[]([])consttotalItemscomputed(()items.value.reduce((sum,item)sumitem.quantity,0))consttotalPricecomputed(()items.value.reduce((sum,item)sumitem.price*item.quantity,0))constaddItem(product:OmitCartItem,quantity){constexistingitems.value.find(itemitem.productIdproduct.productId)if(existing){existing.quantity}else{items.value.push({...product,quantity:1})}}constremoveItem(productId:number){constindexitems.value.findIndex(itemitem.productIdproductId)if(index-1){items.value.splice(index,1)}}constupdateQuantity(productId:number,quantity:number){constitemitems.value.find(itemitem.productIdproductId)if(item){item.quantityMath.max(0,quantity)if(item.quantity0){removeItem(productId)}}}constclear(){items.value[]}return{items,totalItems,totalPrice,addItem,removeItem,updateQuantity,clear}})七、Store 组合多个 Store 可以组合使用// stores/checkout.tsexportconstuseCheckoutStoredefineStore(checkout,(){constcartStoreuseCartStore()constuserStoreuseUserStore()constshippingAddressrefAddress|null(null)constpaymentMethodrefstring|null(null)constcanCheckoutcomputed(()cartStore.totalItems0userStore.isLoggedInshippingAddress.value!nullpaymentMethod.value!null)constcheckoutasync(){if(!canCheckout.value)returnconstresponseawait$fetch(/api/orders,{method:POST,body:{items:cartStore.items,shippingAddress:shippingAddress.value,paymentMethod:paymentMethod.value}})cartStore.clear()returnresponse}return{shippingAddress,paymentMethod,canCheckout,checkout}})八、Pinia 插件Pinia 支持插件扩展功能比如持久化pnpmaddpinia-plugin-persistedstate// plugins/pinia.tsimport{defineNuxtPlugin}from#appimport{createPinia}frompiniaimportpiniaPluginPersistedstatefrompinia-plugin-persistedstateexportdefaultdefineNuxtPlugin((nuxtApp){constpiniacreatePinia()pinia.use(piniaPluginPersistedstate)nuxtApp.vueApp.use(pinia)})在 Store 中启用持久化exportconstuseCartStoredefineStore(cart,(){// ...},{persist:true// 启用持久化})九、DevTools 调试安装 Vue DevTools 浏览器插件可以查看所有 Store 状态实时修改状态时间旅行调试查看 actions 调用历史开发必备神器十、SSR 注意事项Pinia 在 SSR 中的数据传递// 在插件中初始化用户数据exportdefaultdefineNuxtPlugin(async(nuxtApp){constuserStoreuseUserStore()if(import.meta.server){// 服务端获取用户数据consteventuseRequestEvent()consttokengetCookie(event,token)if(token){awaituserStore.fetchUser()}}})总结Pinia 核心概念概念说明State状态数据Getters计算属性Actions方法支持异步Store状态仓库Plugins扩展功能Pinia vs useStateuseState简单、轻量、适合小状态Pinia功能完整、DevTools、插件生态下一篇聊聊中间件学会请求拦截与权限校验。相关文章入门篇三Nuxt4组件自动导入写代码少敲一半字入门篇二Nuxt 4路由自动生成告别手动配置路由的日子延伸阅读nuxt4完整系列持续更新中。。欢迎来逛逛内容有帮助点赞、收藏、关注三连评论区等你

更多文章