Pinia和Vuex一样,是Vue的全局状态管理器。其实Pinia就是Vuex5,只是为了尊重原作者的贡献,才用了这个长相甜美的名字Pinia。本文将以Vue3的形式对比两者的不同实现方式,让你在以后的工作中使用Pinia或Vuex游刃有余。既然要比较两者的实现方式,那么首先要在我们的Vue3项目中引入这两个状态管理器(实际项目中,不要同时使用Vuex和Pinia,否则会被同事请去喝茶.看看它们的使用方法安装Vuexnpmivuex-ScopycodePinianpmipinia-ScopycodemountVuex并在src目录下新建vuexStore实际项目中只需要建一个store目录即可,由于我们需要两个状态管理器,我们需要将它们分开并创建两个存储目录。CreatenewvuexStore/index.jsimport{createStore}from'vuex'exportdefaultcreateStore({//全局状态,类似vue类型的datastate()){return{vuexmsg:"hellovuex",name:"xiaoyue",};},//修改状态函数mutations:{},//提交的mutation可以包含任何异步操作actions:{},//类似于vue中的computedpropertygetters:{},//将store拆分成模块(module),当应用很大时,使用modules:{}})复制代码main.jsimportimport{createApp}from'vue'importAppfrom'./App.vue'importstorefrom'@/store'createApp(应用程序)。use(store).mount('#app')复制代码App.vuetest复制代码页正常打印hellovuex,说明我们的Vuex已经挂载成功。Piniamain.js引入import{createApp}from"vue";importAppfrom"./App.vue";import{createPinia}from'pinia'constpinia=createPinia()createApp(App).use(pinia).mount(“#应用程序”);复制代码以从“pinia”创建一个新的piniaStore/storeA.jsimport{defineStore};exportconststoreA=defineStore("storeA",{state:()=>{return{piniaMsg:"hellopinia"",};},getters:{},actions:{},});复制代码App.vue使用复制代码从这里我们可以看出pinia中没有突变和模块,pinia不需要嵌套(通过modules的方式引入modules,因为它的每一个store都是一个module,比如storeA,storeB....我们在使用Vuex的时候,每次修改state值的时候,都需要调用mutations中的修改函数(下文会介绍),因为Vuex需要跟踪数据的变化,这让我们写起来比较麻烦。longer需要mutation,同步和异步都可以在action中操作,至于最后是怎么变成没有mutation的状态,这里就不深究了,看来应该解决了nhooks回调的形式(我没研究过,只是猜测。修改状态获取状态值。从上面我们已经可以一目了然了。让我们看看他们如何修改状态。vuexvuex直接修改组件中的state,比如App.vue{{vuexStore.state.vuexmsg}}
复制代码,可以看到可以直接修改组件中的state,还是responsive,但是如果这样,vuex就无法记录每一个状态变化记录,影响我们的调试。vuex在严格模式下,直接修改state会报错,所以官方建议我们开启严格模式。所有状态更改都在vuex内部进行,并在mutations中进行修改。例如vuexStore/index.js:import{createStore}from"vuex";exportdefaultcreateStore({strict:true,//全局状态,类似于vue的数据状态:{vuexmsg:"hellovuex",},//修改状态函数mutations:{setVuexMsg(state,data){state.vuexmsg=data;},},//提交的mutation可以包含任何异步操作action:{},//类似于vue中的computedpropertygetters:{},//将store拆分成模块(modules),应用大时使用模块:{},});复制代码当我们需要修改vuexmsg时,需要提交setVuexMsg方法,比如App.vue。{{vuexStore.state.vuexmsg}}
复制代码或者我们可以在操作中提交突变来修改状态:import{createStore}from"vuex";exportdefaultcreateStore({strict:true,//全局状态,类似于vue类型的数据state(){return{vuexmsg:"hellovuex",}},//修改状态函数;},},//提交的mutation可以包含任何异步操作action:{asyncgetState({commit}){//constresult=awaitxxxx假设这里发起请求,返回值commit("setVuexMsg","你好觉进");},}});在复制代码组件中使用dispatch来分发动作。{{vuexStore.state.vuexmsg}}
copycode一般来说,vuex中的流程就是action一般会把异步函数放在前面。以请求后端接口为例。当后端接口返回一个值的时候,actions会在mutations中提交一个函数,然后这个函数修改vuex中的状态(state),然后在组件中渲染这个状态,这样整个数据流在里面可以很容易的被检测到vuex。看图就知道了,一目了然。1f0c7f44205b2a793829d22509fac74.pngPinia直接修改相比Vuex,Pinia可以直接修改状态,调试工具可以记录每一次状态变化,比如App.vue。{{piniaStoreA.piniaMsg}}
复制代码$patch使用$patch方法修改多个状态下的值,比如我们在piniaStore/storeA.js中的状态添加一个名称。import{defineStore}from"pinia";exportconststoreA=defineStore("storeA",{state:()=>{return{piniaMsg:"hellopinia",name:"xiaoyue",};},getters:{},动作:{},});复制代码然后我们在App.vue中修改这两个状态。从'@/piniaStore/storeA'导入{storeA}让piniaStoreA=storeA()console.log(piniaStoreA.name);//xiaoyuepiniaStoreA.$patch({piniaMsg:'hellojuejin',name:'daming'})console.log(piniaStoreA.name);//daming复制代码当然也支持修改单个状态,比如piniaStoreA。$patch({name:'daming'})复制代码$patch也可以使用函数修改状态:import{storeA}from'@/piniaStore/storeA'letpiniaStoreA=storeA()cartStore.$patch((state)=>{state.name='daming'state.piniaMsg='hellojuejin'})将代码复制到actions中修改与Vuex不同的是Pinia去掉了mutations,所以在actions中修改state和在action中修改state是一样的Vuex中的突变。其实这也是我推荐的一种修改状态的方式。如前所述,整个数据流可以保存在状态管理器内部,便于管理。在piniaStore/storeA.js的action中添加修改name的函数。import{defineStore}from"pinia";exportconststoreA=defineStore("storeA",{state:()=>{return{piniaMsg:"hellopinia",name:"xiaoyue",};},actions:{setName(data){this.name=data;},},});复制代码组件App.vue调用,不使用dispatch函数,直接调用store方法即可。import{storeA}from'@/piniaStore/storeA'letpiniaStoreA=storeA()piniaStoreA.setName('daming')复制代码重置状态Pinia可以使用$reset将状态重置为初始值。import{storeA}from'@/piniaStore/storeA'letpiniaStoreA=storeA()piniaStoreA.$reset()复制代码Pinia解构(storeToRefs)当我们需要在我们的组件中使用状态中的多个参数时,使用解构方法来get值往往很方便,但是传统的ES6解构会导致state无响应。比如App.vue这个组件,我们先解构得到name值,然后修改name值,然后看页面有没有变化。{{name}}
复制代码浏览器显示如下:1657813193335.jpg我们可以发现浏览器没有更新页面为daming为了解决这个问题,Pinia提供了一个结构方法storeToRefs,我们使用storeToRefs解构组件App.vue。{{name}}
复制代码,查看页面变化。1657813178903.jpg发现页面已更新为大明。Getters实际上,Vuex中的getter的使用方式与Pinia中的getter相同。它们用于自动监听相应状态的变化,从而动态计算返回值(类似于Vue中的计算属性),getters的值也具有缓存特性。Pinia让我们先更改piniaStore/storeA.js。import{defineStore}from"pinia";exportconststoreA=defineStore("storeA",{state:()=>{return{count1:1,count2:2,};},getters:{sum(){console.log('我被叫了!')returnthis.count1+this.count2;},},});复制代码,在组件App.vue中获取sum{{piniaStoreA.sum}}
复制代码让我们看看什么是缓存功能。首先,让我们在组件中多次访问总和,然后查看控制台打印。从'@/piniaStore/storeA'导入{storeA}让piniaStoreA=storeA()console.log(piniaStoreA.sum)console.log(piniaStoreA.sum)console.log(piniaStoreA.sum)piniaStoreA.count1=2console.log(piniaStoreA.sum)copycode1657814372565.jpg从打印结果可以看出,getters中的sum只有在第一次使用或者改变sum依赖的值时才会被调用。VuexVuex中getter的使用和Pinia类似,这里就不用过多解释了。写法如下vuexStore/index.js。import{createStore}from"vuex";exportdefaultcreateStore({strict:true,//全局状态,类似vue的数据状态:{count1:1,count2:2,},//类似vue中的计算属性获取器:{sum(state){returnstate.count1+state.count2}}});复制代码模块如果项目比较大,使用单一的状态库,那么项目的状态库会集中在一个大对象上,非常Bloat,难以维护。所以Vuex允许我们把它分成模块(modules),每个模块都有自己的state、mutations、actions...。而Pinia的每个状态库本身就是一个模块。PiniaPinia没有模块。如果要使用多个store,可以直接定义多个store,传入不同的id,如:import{defineStore}from"pinia";exportconststoreA=defineStore("storeA",{...});exportconststoreB=defineStore("storeB",{...});exportconststoreC=defineStore("storeB",{...});复制代码Vuex一般会为每个模块新建一个文件,然后引入这个通用入口index.js,为了方便写在这??里。从“vuex”导入{createStore};constmoduleA={state:()=>({count:1}),mutations:{setCount(state,data){state.count=data;},},actions:{getuser(){//dosomething},},getters:{...}}constmoduleB={state:()=>({...}),mutations:{...},动作:{...}}exportdefaultcreateStore({strict:true,//全局状态,类似vue类型的datastate()){return{vuexmsg:"hellovuex",name:"xiaoyue",};},模块:{moduleA,moduleB},});复制代码usemoduleAimport{useStore}from'vuex'letvuexStore=useStore()console.log(vuexStore.state.moduleA.count)//1vuexStore.commit('setCount',2)console.log(vuexStore.state.moduleA.count)//2vuexStore.dispatch('getuser')复制代码一般为了防止提交一些mutation或者action中重名的方法,模块一般采用命名空间方法namespaced:true比如moduleA:constmoduleA={namespaced:true,state:()=>({count:1,}),mutations:{setCount(state,data){state.count=data;},},行动:{getuser(){//dosomething},},}如果我们调用setCount或getuservuexStore.commit('moduleA/setCount',2)vuexStore.dispatch('moduleA/getuser')最后复制代码通过以上案例,我们会发现Pinia比Vuex要简单的多,所以如果我们的项目是一个新项目,推荐使用Pinia。当然,如果我们的项目规模不是很大,其实不需要引入Vue的状态管理库。相反,盲目使用它。只会增加精神负担。如果您觉得本文对您有帮助,请点个赞orz。