无论是vuex
还是pina
,本质上都是为了解决项目中的全局数据存储的问题,没有什么更优或者更劣的说法。
不过,vuex
主要是应用在vue2的项目,pina
则是随着Vue3升级产生的社区产物。
pina由原vuex团队,为了配合vue3的升级,特意写的新工具,所以二者在使用上会有一定相似性。
正文
在正式开始写这篇文档之前,稍微科普一下,vuex和pina都是一个团队开发的,所以这二者在使用上会有不少类似的地方。
随着vue3的推广,pina也逐渐开始在社区中推广,成了vue3的开源项目新宠儿。
早期也有很多开发者在vue2的项目中用pina,或在vue3的项目中用vuex。
不过,随着时间推移,vue2中使用vuex,vue3中pina,这成了社区约定的常规习惯,大多vue开源项目你都能看到他们这么用。
如果要问为什么,只是社区逐渐应用产生的习惯罢了,并没有什么特殊的讲究。
vuex
Vuex
设计为一个全局状态管理的单例模式,有一个中心化的存储来管理应用的所有状态。
它基于Vue 2的Options API,强调状态的集中管理和严格的规则,包括State、Getters、Mutations(用于同步更改状态)和Actions(可以处理异步逻辑)。
其使用与pinia的CompositionAPI 的写法还是略有不同的。
action和mutation的区别
很多人早期在使用vuex的时候,可能有些分不清action
和mutation
的区别。
所以,在正式介绍vuex
之前,我们先在简单介绍一下二者的区别。
mutation
更专注于修改state
,必须是同步执行(无法在mutation中使用异步方法)。
action
提交的是mutation
,而不是直接更新数据,可以是异步的,如业务代码,异步请求。
action
可以包含多个mutation
main.js
文件
1 2 3 4 5 6 7 8
| import { createApp } from 'vue' import './style.css' import App from './App.vue' import store from './store'
const app=createApp(App) app.use(store) app.mount('#app')
|
store
文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| import { createStore } from 'vuex' const state = { counter: 0, user: null } const actions = { increment: ({ commit }) => { commit('increment') }, }
const mutations = { increment(state) { state.counter++ } }
const getters = { doubleCount: (state) => { return state.counter * 2 } }
const store = createStore({state,actions,mutations,getters}) export default store
|
App.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <template> <h2>App.vue:</h2> <div> count: {{ store.state.counter }} </div> <button @click="increment">+</button> <div> doubleCount: {{store.getters.doubleCount}} </div> <AppHeader /> </template>
<script setup> import {useStore} from 'vuex' import AppHeader from './components/app-header.vue' const store=useStore() const increment = () => { store.dispatch('increment') } </script>
|
pinia
Pinia针对Vue 3设计,充分利用Composition API,提倡更简洁和直观的状态管理方式。
每个store在Pinia中都是独立的,同时为了兼容Vuex的写法Pinia仍然支持使用选项式API,也是现在主流的状态管理库。
main.js
文件
1 2 3 4 5 6 7 8
| import { createApp } from 'vue' import App from './App.vue' import { createPinia } from 'pinia'
const pinia=createPinia() createApp(App) .use(pinia) .mount('#app')
|
store
文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| import { defineStore } from "pinia"; import { ref, computed } from 'vue'; export const useCounterStore = defineStore('counter', () => { const count = ref(0); const increment = () => { count.value++; }; const decrement = () => { count.value--; }; const reset = () => { count.value = 0; }; const doubleCount = computed(() => { return count.value * 2; }); return { count, increment, decrement, reset, doubleCount }; }, ); export const useStore = defineStore('store', () => { const someState=ref('hello Pinia'); const incrementSomeState = () => { someState.value += '!' } return { someState , incrementSomeState } }, );
|
App.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <template> <div> <h1>App</h1> <h1>Counter: {{ store.count }}</h1> <h1>Double:{{ store.doubleCount }}</h1> <button @click="store.increment">Increment</button> <button @click="store.decrement">decrement</button> <button @click="store.reset">reset</button> </div> <h1>String: {{ hel.someState }}</h1> <button @click="hel.incrementSomeState">+ ! </button> <Child /> </template> <script setup> import {useCounterStore,useStore} from '@/Pinia/counter.js' import Child from '@/components/child.vue' const store=useCounterStore() const hel=useStore() </script>
|
二者区别
- 相对于
vuex
,pina
是用tyepscript
完成的,所以,代码提示效果更好。
- 在
main.js
文件中,vuex使用的是导出的store仓库,而pinia是使用实例化对象。
pina
弃用了mutation
,如果想要完成数据修改,可以直接用action操作就可以了。
pina
中不再使用modules,而是直接声明的对应的对象调用即可。
modules
如果项目比较大,使用单一状态库,项目的状态库就会集中到一个大对象上,显得十分臃肿难以维护。
所以Vuex就允许我们将其分割成模块(modules),每个模块都拥有自己state,mutations,action等。
pina
而Pinia每个状态库本身就是一个模块,pinia
没有modules,如果想使用多个store,直接定义多个store传入不同的id即可。
1 2 3 4 5
| import { defineStore } from "pinia";
export const storeA = defineStore("storeA", {...}); export const storeB = defineStore("storeB", {...}); export const storeC = defineStore("storeB", {...});
|
Vuex
一般来说每个module都会新建一个文件,然后再引入这个总的入口index.js中,这里为了方便就写在了一起
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| import { createStore } from "vuex"; const moduleA = { state: () => ({ count:1 }), mutations: { setCount(state, data) { state.count = data; }, }, actions: { getuser() { }, }, getters: { ... } }
const moduleB = { state: () => ({ ... }), mutations: { ... }, actions: { ... } }
export default createStore({ strict: true, state() { return { vuexmsg: "hello vuex", name: "xiaoyue", }; }, modules: { moduleA, moduleB }, });
|
使用moduleA
1 2 3 4 5 6
| import { useStore } from 'vuex' let vuexStore = useStore() console.log(vuexStore.state.moduleA.count) vuexStore.commit('setCount', 2) console.log(vuexStore.state.moduleA.count) vuexStore.dispatch('getuser')
|
一般我们为了防止提交一些mutation或者actions中的方法重名,modules一般会采用命名空间的方式 namespaced: true 如moduleA:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| const moduleA = { namespaced: true, state: () => ({ count: 1, }), mutations: { setCount(state, data) { state.count = data; }, }, actions: { getuser() { }, }, }
|
此时如果我们再调用setCount或者getuser
1 2
| vuexStore.commit('moduleA/setCount', 2) vuexStore.dispatch('moduleA/getuser')
|
store
仓库中的actions
是提交修改方法名,而真正修改是通过mutations
,actions
就像是秘书,而mutation
像是boss,你不能直接找到boss修改属性,而必须通过dispatch
派出increment action
的方法,让mutation
去修改这个值。
- vuex中的
getter
是不需要引入computed这个方法的,他会自动一直监听内部的状态是否发生改变。
结语
pina和vuex的区别,算是vue3的常规面试题之一,我之前虽然大致整理过,但是没有系统性的梳理。
如今借着别人的文档仔细梳理了一遍,心里对二者的使用心得也是更上一层楼。
不过,随着vue3的逐渐推广,未来pina
一定会在社区中提高占用比。
如果要是手里有新项目的朋友,最好还是在项目中使用pina
,这也是对社区不断发展的一种适应。
参考
简介 | Pinia (vuejs.org)
开始 | Vuex (vuejs.org)
vue3通信大全(三)—— 全局状态管理库pinia,vuex