目录
状态管理技术选型
1. Pinia概述
2. Vuex的应用
2.1 Vuex 的使用
2.2 测试组件
3. Pinia 的应用
3.1 Pinia 的使用
3.2 Pinia 的持久化
3.3 测试组件
4. Pinia 与 Vuex的比较总结
前端项目需要使用到状态管理框架,目前社区流行的有Vuex和Pinia两个框架。经过对比后,我们选择Pinia作为我们的状态管理库。下面将分点说明Pinia特点。
背景
Pinia是根据Vue3而设计的,相比于Vuex来说历史包袱更少,它吸收了Vuex下个版本的很多核心概念以及项目开发经验而产生。
框架量级
Pinia是轻量级的,体积很小。
开发体验
Pinia有完整的 TypeScript 支持,与在 Vuex 中添加 TypeScript 相比,添加 TypeScript 更容易。
在使用上可以使用贴近Vuex的API风格来编写代码,也可以使用Vue3组合式API的风格来编写代码。
Store 的 action 被调度为常规的函数调用,而不是使用 dispatch 方法或 MapAction 辅助函数,这在 Vuex 中很常见。
社区和生态系统
Pinia目前是Vue.js生态系统中增长最快的状态管理库之一,社区正在快速增长。然而Vuex毕竟是Vue.js核心团队推荐的状态管理库,拥有庞大的社区,核心团队成员做出了重大贡献。 Stack Overflow 上很容易找到 Vuex 错误的解决方案。
Pinia能力评估
接下来是评估Pinia是否能满足我们项目对于状态管理库的要求。
跨组件/页面数据共享(支持)
热更新(支持)
插件机制(支持)
Devtools(支持)
数据持久化(支持,可通过插件机制实现)
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
安装Vuex
yarn add vuex@next --save
# 或者使用 npm
npm install vuex@next --save
安装Vuex 后在main.ts入口文件注册Vuex
import { createApp } from 'vue';
import App from './App.vue';
import router from './router/index';
import store from './vuex/store'; // 引入store对象
const app = createApp(App);
app.use(router);
app.use(store); // 把store对象添加到vue实例上
app.mount('#app');
src文件夹下新建store文件夹,然后在store文件夹下新建store.ts文件
import { Store, createStore } from 'vuex';
// 配置vue+ts的项目里面使用vuex
declare module '@vue/runtime-core' {
// declare your own store states
interface State {
count: number;
list: string[];
msg: string;
}
// provide typings for `this.$store`
interface ComponentCustomProperties {
$store: Store;
}
}
const store = createStore({
// 数据
state() {
return {
count: 1,
list: ['马总', '刘总'],
msg: '你好vue',
};
},
// 方法 Mutations里面的函数必须是同步操作
mutations: {
// 方法的作用修改state中的变量,state是必须默认参数
increment(state: any) {
state.count++;
},
// 增加一个带参数的mutations方法
setCount(state: any, num: number) {
state.count = num;
},
setMsg(state: any, msg: string) {
state.msg = msg;
},
},
// 计算属性
getters: {
// 获取修饰后的name,第一个参数state为必要参数,必须写在形参上
reverseMsg(state: any) {
return state.msg.split('').reverse().join('');
},
num(state: any) {
return state.count + 10;
},
},
// 执行mutations里面的方法 异步操作放在actions
actions: {
// 默认第一个参数是context,其值是复制的一份store
increment(context) {
// 执行mutations里面的increment
context.commit('increment');
},
// action就是去提交mutation的,什么异步操作都在action中消化了,最后再去提交mutation的
// 直接将context结构掉,解构出commit,下面就可以直接调用了
setMsg({ commit }, msg: string) {
// 执行mutations里面的increment
setTimeout(() => {
commit('setMsg', msg);
}, 1000);
},
},
});
// vuex的模块
// import storeModule1 from './storeModule1';
// import storeModule2 from './storeModule2';
// const store = createStore({
// modules: {
// storeModule1: storeModule1,
// storeModule2: storeModule2,
// },
// });
export default store;
新建test.vue的测试组件,使用Vuex
vuex测试组件
vuex--{{ count }}
- {{ item }}
获取getters的数据--{{ reverseMsg }}
获取getters的数据--{{ num }}
触发actions里面的方法
Pinia 是 Vue 的状态管理库,它允许您跨组件/页面共享状态。
安装Pinia
yarn add pinia#
#或者使用 npm
npm install pinia
安装pinia后在main.ts入口文件注册pinia
import { createApp } from 'vue';
import App from './App.vue';
import router from './router/index';
import { createPinia } from 'pinia';
const app = createApp(App);
app.use(router);
const pinia = createPinia();
app.use(pinia);
app.mount('#app');
src文件夹下新建store文件夹,然后在store文件夹下新建mian.ts文件
import { defineStore } from 'pinia';
type User = {
name: string;
age?: number;
sex?: string;
msg?: string;
};
const login = (user: User): Promise => {
return new Promise(resolve => {
setTimeout(() => {
user.msg = '登陆成功';
resolve(user);
}, 2000);
});
};
export const useMainStore = defineStore({
id: 'mian',
state: () => ({
name: '超级管理员',
count: 0,
user: {},
}),
// computed 计算属性
getters: {
nameLength: state => state.name.length,
newName(): string {
return `$-${this.name}--${this.nameLength}`;
},
},
// methods 可以做同步、异步 提交state
actions: {
setCount(count: number) {
this.count = count;
},
setUser(user: User) {
this.user = user;
},
async setUserLogin(user: User) {
const result = await login(user);
this.user = result;
this.setSex('女');
},
setSex(sex: string) {
this.user.sex = sex;
},
},
});
在store文件夹下封装一个Pinia 插件,文件名为piniaPlugin.ts
import { PiniaPluginContext } from 'pinia';
import { toRaw } from 'vue';
type Options = {
key?: string;
};
const _piniaKey_ = 'pinia';
const setStorage = (key: string, value: any) => {
localStorage.setItem(key, JSON.stringify(value));
};
const getStorage = (key: string) => {
return localStorage.getItem(key) ? JSON.parse(localStorage.getItem(key) as string) : {};
};
export const piniaPlugin = (options: Options) => {
return (context: PiniaPluginContext) => {
const { store } = context;
const data = getStorage(`${options?.key ?? _piniaKey_}-${store.$id}`);
store.$subscribe(() => {
setStorage(`${options?.key ?? _piniaKey_}-${store.$id}`, toRaw(store.$state));
});
return {
...data,
};
};
};
在main.ts入口文件注册Pinia 持久化插件
import { createApp } from 'vue';
import App from './App.vue';
import router from './router/index';
import { createPinia } from 'pinia';
import { piniaPlugin } from './store/piniaPlugin';
const app = createApp(App);
app.use(router);
const pinia = createPinia();
pinia.use(piniaPlugin({ key: 'pinia' }));
app.use(pinia);
app.mount('#app');
新建test.vue的测试组件,使用Pinia
state
用户名:{{ mainStore.name }}--- count:{{ mainStore.count }}
getters
nameLength:{{ mainStore.nameLength }}
newName:{{ mainStore.newName }}
修改store中的变量
pinia解构不具有响应性解决办法
用户名:{{ name }}--- count:{{ count }}
actions
用户名:{{ mainStore.user }}
reset