一文解析 Pinia 和 Vuex ,带你全面理解这两个 Vue 状态管理模式

2022-12-23 0 636

原副标题:该文导出 Pinia 和 Vuex ,带你全面性认知这三个 Vue 状况区域化

↓所推荐高度关注↓

Pinia和Vuex那样都是是vue的自上而下状况命令行。只不过Pinia是Vuex5,或者说为的是认同原作者的重大贡献就延用了那个看上去焦香的英文名字Pinia。

责任编辑将透过Vue3的方式对二者的相同同时实现方式展开对照,阳光普照之后组织工作中不论采用到Pinia却是Vuex的这时候都能得心应手。

难道他们要对照二者的同时实现方式,因此他们的确要先在他们的Vue3工程项目中导入这三个状况命令行(前述工程项目中一千万千万别狸尾豆Vuex又用Pinia,要不然你会被同僚请去饮茶的。上面瞧瞧他们看下它的采用方式吧

加装

Vuex

npm i vuex -S

Pinia

npm i pinia -S

装载Vuex

在src产品目录下增建vuexStore,前述工程项目中你只须要建两个store产品目录方可,虽然他们须要三种状况命令行,因此须要将其合二为一并建立三个store产品目录

增建vuexStore/index.js

import{ createStore } fromvuex

exportdefaultcreateStore({

//自上而下state,类似于vue种的data

state {

return{

vuexmsg: “hello vuex”,

name: “xiaoyue”,

};

},

//修改state函数

mutations: {

},

//提交的mutation可以包含任意异步操作

actions: {

},

//类似于vue中的计算属性

getters: {

},

//将store分割成模块(module),应用较大时采用

modules: {

}

})

main.js导入

import{ createApp }fromvue

importApp from./App.vue

importstore from@/vuexStore

createApp(App).use(store).mount( #app)

App.vue测试

< template>

< div> </ div>

</ template>

< setup>

import{ useStore } fromvuex

letvuexStore = useStore

console.log(vuexStore.state.vuexmsg);//hello vuex

</ >

页面正常打印hello vuex说明他们的Vuex已经装载成功了

Pinia

main.js导入

import{ createApp }from “vue”;

importApp from“./App.vue”;

import{createPinia} frompinia

constpinia = createPinia

createApp(App).use(pinia).mount(“#app”);

建立Store

src下增建piniaStore/storeA.js

import{ defineStore } from“pinia”;

exportconststoreA = defineStore( “storeA”, {

state: => {

return{

piniaMsg: “hello pinia”,

};

},

getters: {},

actions: {},

});

App.vue采用

< template>

< div> </ div>

</ template>

< setup>

import{ storeA } from@/piniaStore/storeA

letpiniaStoreA = storeA

console.log(piniaStoreA.piniaMsg); //hello pinia

</ >

从这里他们可以看出pinia中没有了mutations和modules,pinia不必以嵌套(透过modules导入)的方式导入模块,因为它的每个store便是两个模块,如storeA,storeB… 。

在他们采用Vuex的这时候每次修改state的值都须要调用mutations里的修改函数(上面会说到),因为Vuex须要追踪数据的变化,这使他们写起来比较繁琐。而pinia则不再须要mutations,同步异步都可在actions展开操作,至于它没有了mutations具体是如何最终到state变化的,这里他们不过多深究,大概好像应该是透过hooks回调的方式解决的把(我也没研究过,瞎猜的

修改状况

目了然的看到了,上面让他们看看他俩修改state的方法吧

vuex

vuex在组件中直接修改state,如App.vue

< template>

< div> {{vuexStore.state.vuexmsg}}</ div>

</ template>

< setup>

import{ useStore } fromvuex

letvuexStore = useStore

vuexStore.state.vuexmsg = hello juejin

console.log(vuexStore.state.vuexmsg)

</ >

可以看出他们是可以直接在组件中修改state的而且却是响应式的,但是如果这样做了,vuex不能记录每一次state的变化记录,影响他们的调试。

当vuex开启严格模式的这时候,直接修改state会抛出错误,因此官方建议他们开启严格模式,所有的state变更都在vuex内部展开,在mutations展开修改。例如vuexStore/index.js:

import{ createStore } from“vuex”;

exportdefaultcreateStore({

strict: true,

//自上而下state,类似于vue种的data

state: {

vuexmsg: “hello vuex”,

},

//修改state函数

mutations: {

setVuexMsg(state, data) {

state.vuexmsg = data;

},

},

//提交的mutation可以包含任意异步操作

actions: {},

//类似于vue中的计算属性

getters: {},

//将store分割成模块(module),应用较大时采用

modules: {},

});

当他们须要修改vuexmsg的这时候须要提交setVuexMsg方法,如App.vue

< template>

< div> {{ vuexStore.state.vuexmsg }}</ div>

</ template>

< setup>

import{ useStore } fromvuex

letvuexStore = useStore

vuexStore.commit(setVuexMsg, hello juejin)

console.log(vuexStore.state.vuexmsg) //hello juejin

</ >

或者他们可以在actions中展开提交mutations修改state:

import{ createStore } from“vuex”;

exportdefaultcreateStore({

strict: true,

//自上而下state,类似于vue种的data

state {

return{

vuexmsg: “hello vuex”,

}

},

//修改state函数

mutations: {

setVuexMsg(state, data) {

state.vuexmsg = data;

},

},

//提交的mutation可以包含任意异步操作

actions: {

asyncgetState({ commit }) {

//const result = await xxxx 假设这里展开了请求并拿到了返回值

commit( “setVuexMsg”, “hello juejin”);

},

}

});

组件中采用dispatch展开分发actions

< template>

< div> {{ vuexStore.state.vuexmsg }} </ div>

</ template>

< setup>

import{ useStore } fromvuex

letvuexStore = useStore

vuexStore.dispatch( getState)

</ >

一般来说,vuex中的流程是首先actions一般放异步函数,拿请求后端接口为例,当后端接口返回值的这时候,actions中会提交两个mutations中的函数,然后那个函数对vuex中的状况(state)展开两个修改,组件中再渲染那个状况,从而同时实现整个数据流程都在vuex内部展开便于检测。直接看图,一目了然

一文解析 Pinia 和 Vuex ,带你全面理解这两个 Vue 状态管理模式

Pinia

直接修改

相比于Vuex,Pinia是可以直接修改状况的,并且调试工具能记录到每一次state的变化,如App.vue

< template>

< div> {{ piniaStoreA.piniaMsg }} </ div>

</ template>

< setup>

import{ storeA } from@/piniaStore/storeA

letpiniaStoreA = storeA

console.log(piniaStoreA.piniaMsg);//hello pinia

piniaStoreA.piniaMsg = hello juejin

console.log(piniaStoreA.piniaMsg); //hello juejin

</ >

$patch

采用$patch方法可以修改多个state中的值,比如他们在piniaStore/storeA.js中的state增加两个name

import{ defineStore }from“pinia”;

exportconststoreA = defineStore( “storeA”, {

state: => {

return{

piniaMsg: “hello pinia”,

name: “xiaoyue”,

};

},

getters: {},

actions: {},

});

然后他们在App.vue中展开修改这三个state

import{ storeA } from@/piniaStore/storeA

letpiniaStoreA = storeA

console.log(piniaStoreA.name); //xiaoyue

piniaStoreA.$patch({

piniaMsg: hello juejin,

name: daming

})

console.log(piniaStoreA.name); //daming

当然也是支持修改单个状况的如

piniaStoreA. $patch({

name: daming

})

$patch还可以采用函数的方式展开修改状况

import { storeA } from @/piniaStore/storeA

let piniaStoreA = storeA

cartStore.$patch((state) => {

state.name = daming

state.piniaMsg = hello juejin

})

在actions中展开修改

相同于Vuex的是,Pinia去掉了mutations,因此在actions中修改state就行Vuex在mutations修改state那样。只不过这也是我比较所推荐的一种修改状况的方式,就像上面说的,这样可以同时实现整个数据流程都在状况命令行内部,便于管理。

在piniaStore/storeA.js的actions添加两个修改name的函数

import{ defineStore } from“pinia”;

exportconststoreA = defineStore( “storeA”, {

state: => {

return{

piniaMsg: “hello pinia”,

name: “xiao yue”,

};

},

actions: {

setName(data) {

this.name = data;

},

},

});

组件App.vue中调用不须要再采用dispatch函数,直接调用store的方法方可

import{ storeA }from@/piniaStore/storeA

letpiniaStoreA = storeA

piniaStoreA.setName( daming)

重置state

Pinia可以采用$reset将状况重置为初始值

import{ storeA } from@/piniaStore/storeA

letpiniaStoreA = storeA

piniaStoreA.$reset

Pinia解构(storeToRefs)

当他们组件中须要用到state中多个参数时,采用解构的方式取值往往是很方便的,但是传统的ES6解构会使state失去响应式,比如组件App.vue,他们先解构取得name值,然后再去改变name值,然后看页面是否变化

< template>

< div> {{ name }} </ div>

</ template>

< setup>

import{ storeA } from@/piniaStore/storeA

letpiniaStoreA = storeA

let{ piniaMsg, name } = piniaStoreA

piniaStoreA.$patch({

name: daming

})

</ >

浏览器展示如下

他们可以发现浏览器并没有更新页面为daming

为的是解决那个问题,Pinia提供了两个结构方法 storeToRefs,他们将组件App.vue采用 storeToRefs解构

< template>

< div> {{ name }} </ div>

</ template>

< setup>

import{ storeA } from@/piniaStore/storeA

import{ storeToRefs }frompinia

letpiniaStoreA = storeA

let{ piniaMsg, name } = storeToRefs(piniaStoreA)

piniaStoreA.$patch({

name: daming

})

</ >

再看下页面变化

他们发现页面已经被更新成daming了

getters

只不过Vuex中的getters和Pinia中的getters用法是一致的,用于自动监听对应state的变化,从而动态计算返回值(和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;

},

},

});

< template>

< div> {{ piniaStoreA.sum }}</ div>

</ template>

< setup>

import{ storeA } from@/piniaStore/storeA

letpiniaStoreA = storeA

console.log(piniaStoreA.sum) //3

</ >

让他们来看下什么是缓存特性。首先他们在组件多次访问sum再看下控制台打印

import{ storeA } from@/piniaStore/storeA

letpiniaStoreA = storeA

console.log(piniaStoreA.sum)

console.log(piniaStoreA.sum)

console.log(piniaStoreA.sum)

piniaStoreA.count1 = 2

console.log(piniaStoreA.sum)

从打印结果他们可以看出只有在首次采用用或者当他们改变sum所依赖的值的这时候,getters中的sum才会被调用

Vuex

Vuex中的getters采用和Pinia的采用方式类似,就不再展开过多说明,写法如下vuexStore/index.js

import{ createStore }from“vuex”;

exportdefaultcreateStore({

strict: true,

//自上而下state,类似于vue种的data

state: {

count1: 1,

count2: 2,

},

//类似于vue中的计算属性

getters: {

sum(state){

returnstate.count1 + state.count2

}

}

});

modules

如果工程项目比较大,使用单一状况库,工程项目的状况库就会集中到两个大对象上,显得十分臃肿难以维护。因此Vuex就允许他们将其分割成模块(modules),每个模块都拥有自己state,mutations,actions…。而Pinia每个状况库本身是两个模块。

Pinia

Pinia没有modules,如果想采用多个store,直接定义多个store传入相同的id方可,如:

import { defineStore } from “pinia”;

export const storeA = defineStore(“storeA”, {…});

export const storeB = defineStore(“storeB”, {…});

export const storeC = defineStore(“storeB”, {…}); Vuex

一般来说每个module都会增建两个文件,然后再导入那个总的入口index.js中,这里为的是方便就写在了一起

import{ createStore } from“vuex”;

constmoduleA = {

state: => ({

count: 1

}),

mutations: {

setCount(state, data) {

state.count = data;

},

},

actions: {

getuser {

//do something

},

},

getters: { … }

}

constmoduleB = {

state: => ({ … }),

mutations: { … },

actions: { … }

}

exportdefaultcreateStore({

strict: true,

//自上而下state,类似于vue种的data

state {

return{

vuexmsg: “hello vuex”,

name: “xiaoyue”,

};

},

modules: {

moduleA,

moduleB

},

});

采用moduleA

import{ useStore } fromvuex

letvuexStore = useStore

console.log(vuexStore.state.moduleA.count)//1

vuexStore.commit( setCount, 2)

console.log(vuexStore.state.moduleA.count) //2

vuexStore.dispatch(getuser)

一般他们为的是防止提交一些mutation或者actions中的方法重名,modules一般会采用命名空间的方式 namespaced: true如moduleA:

const moduleA = {

namespaced: true,

state: => ({

count: 1,

}),

mutations: {

setCount(state, data) {

state.count = data;

},

},

actions: {

getuser {

//do something

},

},

}

此时如果他们再调用setCount或者getuser

vuexStore.commit(moduleA/setCount, 2)

vuexStore.dispatch(moduleA/getuser)

作者:东方小月

– EOF –

点击副标题可跳转

1、 新版 Vue 3 中文文档上线

2、 面试官问:Vue3 对照 Vue2 有哪些变化?

3、 在 Vue 中更优雅的封装第三方组件

↓所推荐高度关注↓

「大前端技术之路」分享 Web前端,Node.js、React Native等大前端技术栈精选

点赞和在看是最大的支持❤️

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务