在2022年2月7号Vue3成为默认版本。Pinia的诞生严重威胁了Vuex 的地位,相信每个人都会抛弃 Vuex ,因为 Pinia 确实太方便和简单了。
记录2022年3月2号学习 Pinia
1.pinia 的优势
-
- 无论对 Vue2 还是 Vue 3 都能做到完美的兼容,新老项目都可以使用。
- 抛弃了 Mutations 的操作,只有 state、getters、actions。极大的简化了状态管理库的使用,让代码编写更加容易直视。
- 不需要嵌套模块,符合 Vue3 的 Composition api,让代码更加扁平化。
- 完美的 TypeScript 支持,Vuex 对 ts 的支持就不够完美。
- 实现更好的代码分割。
- 尤大大的强烈推荐,怎么能不算优势。
其实 pinia 的开发团队就是 vuex 的开发团队,pinia 取代 vuex 只是时间问题。
1.1 环境安装
我在使用的时候安装的版本为 2.0.11 ,为了保证使用方式相同,可以安装指定版本
npm install pinia -S
# or
yarn add pinia
# 安装指定版本
npm install pinia@2.0.11
2. 创建一个 Store
安装好 pinia 之后 直接在 /src/main.ts 即入口文件中引入 pinia。通过 createPinia() 方法得到 pinia 的实例和挂载到 Vue 跟实例上。
2.1 引用
/src/main.ts
import {createApp} from 'vue';
import App from './App.vue';
import router from './router/index';
import './utils/rem'
import {createPinia} from 'pinia'
const app = createApp(App);
// 使用路由
app.use(router);
// 挂在状态管理库 pinia
app.use(createPinia());
// 挂载
app.mount('#app');
2.2 创建状态管理库 store
/src/store/index.ts
/*
* 这里的代码只做三件事:
* 定义状态容器(仓库)
* 修改容器(仓库)中的 state
* 仓库中的 action 的使用
**/
import {defineStore} from 'pinia';
// 方法的第一个参数:相当于为容器起一个名字。注意:这里的名字必须唯一,不能重复
// 方法的第二个参数:可以简单理解为一个配置对象,里边是对容器仓库的配置说明。
export const useMainStore = defineStore('main', {
// 用来存储全局的状态,这里定义就可以作为 SPA 里全局的状态了
state: () => ({
userinfo: {},
count: 0
}),
// 用来监视或者说是计算状态变化的,有缓存功能
getters: {},
// 对 state 里数据变化的业务逻辑需求不同编写逻辑不同,就是修改 state 全局状态数据的
actions: {}
})
2.3 在 Vue3 组件中读取 Store 数据
<template>
<div class='home'>
<img :src='mainStore.userinfo.face' alt='头像'>
<div>{{ mainStore.count }}</div>
</div>
</template>
<script lang='ts' setup>
import {useMainStore} from '@/store';
// 通过 useMainStore 得到 mainStore 实例,就可以在组件里调用 mainStore 里的 state 定义的状态数据了
const mainStore = useMainStore();
</script>
3. Pinia 改变状态数据和注意事项
这里说下状态数据修改,和状态数据解构时遇到的问题
3.1 实现状态数据的改变
跟组件中修改 userinfo 数据
/src/app.vue
<template>
<router-view />
</template>
<script lang='ts' setup>
import {getUserinfo} from '@/api/global.js'
import {useMainStore} from '@/store';
const mainStore = useMainStore();
getUserinfo().then((res: any) => {
// 直接改变数据,别的组件中的数据会同步更新
mainStore.userinfo = res.obj;
})
</script>
3.2 解构 store 数据的坑
把 mainStore 中的数据直接进行解构
<template>
<div class='home'>
<img :src='userinfo.face' alt='头像'>
<div>{{ count }}</div>
</div>
</template>
<script lang='ts' setup>
import {useMainStore} from '@/store';
// 解构出来的 store 中的数据
const mainStore = useMainStore();
const {userinfo, count} = mainStore;
</script>
这里通过解构的数据,只有一次作用,不是响应式数据。也就是说当你改变数据状态时,解构的状态数据不会发生变化。
官方文档中有解决这个问题的方法,storeToRefs() 方法。
<template>
<div class='home'>
<img :src='userinfo.face' alt='头像'>
<div>{{ count }}</div>
</div>
</template>
<script lang='ts' setup>
import {useMainStore} from '@/store';
import {storeToRefs} from 'pinia';
// 解构出来的 store 中的数据
const mainStore = useMainStore();
// 对 mainStore 使用该方法,其实就是把解构出来的数据作了 ref 响应式处理,所以数据有了响应式的能力
const {userinfo, count} = storeToRefs(mainStore);
</script>
4. Pinia 修改状态数据的多种方式
上一节只是修改数据的一种方式,还有三种方式可以修改 store 中的数据。
4.1 使用 $patch 修改多条数据
/src/app.vue
<template>
<router-view />
</template>
<script lang='ts' setup>
import {getUserinfo} from '@/api/global.js'
import {useMainStore} from '@/store';
const mainStore = useMainStore();
getUserinfo().then((res: any) => {
// 直接改变数据,别的组件中的数据会同步更新
// 使用 $patch 同时修改多条数据
mainStore.$patch({
userinfo: res.obj,
count: 100
})
})
</script>
下面这种直接写两行代码也可以实现这样的效果。但是并不推荐下面这种写法。
mainStore.count = 100;
mainStore.userinfo = res.obj
因为 Pinia 官网已经说明 patch。
4.2 $patch 加函数的形式修改状态数据
上面的 $patch 方法,参数使用的是一个对象,还有一种传递函数的方式,适合复杂数据的修改(数组、对象)
// 这个时候的 state 就是 store 仓库里的 state,所以我们可以直接在函数里改变任何状态数据的值
mainStore.$patch(state => {
state.userinfo = res.obj;
state.count = 100;
})
4.3 在 actions 中写好逻辑,再调用 actions
如果修改数据的过程非常复杂,可以先在 store 中定义好 actions 中的函数,然后在组件里再掉用函数。
/src/store/index.ts 中的 actions 中添加方法
actions: {
// 这里不能使用箭头函数,因为箭头函数绑定的是外部的 this,这里注意下
changeState(){
this.count = 100;
this.userinfo = {a: 111}
}
}
/src/app.vue 在 app 组件中调用
<template>
<router-view />
</template>
<script lang='ts' setup>
import {getUserinfo} from '@/api/global.js'
import {useMainStore} from '@/store';
const mainStore = useMainStore();
getUserinfo().then((res: any) => {
// 调用 store 里 action 中的方法修改数据
mainStore.changeState();
})
</script>
5. Pinia 中 Getters 使用
Pinia 中的 Getter 和 Vue 中的计算属性几乎一样,就是在获取 State 的值时作一些处理。比如我们有这样一个需求,就是在 state 里有一个状态数据是电话号码,想输出的时候中间四位数为 **** ,这时用 Getter 就可以完美解决。
5.1 使用
/src/store/index.ts 中 state 数据
state: () => ({
phone: 18766668888,
count: 1
})
getters 中编写方法,通过正则表达式隐藏手机号中间四位数
getters: {
newPhone(state):string {
return state.phone.toString().replace(/^(\d{3})\d{4}(\d{4})$/, '$1****$2')
}
}
读取数据
<template>
<div class='home'>
<div>{{ count }}</div>
<div>{{ phone }}</div>
<div>{{ newPhone }}</div>
<div>{{ mainStore.newPhone }}</div>
</div>
</template>
<script lang='ts' setup>
import {useMainStore} from '@/store';
import {storeToRefs} from 'pinia';
const mainStore = useMainStore();
const {phone, newphone, count} = storeToRefs(mainStore);
</script>
5.2 Getters 的缓存属性
Getter 是由缓存特性的,5.1 中调用了两次 newPhone,其实 newPhone 中的代码只执行了一次,因为 getter 是由缓存的。当 newPhone 方法中的 phone 参数改变的时候,newPhone 方法也会相应调用一次,更新数据,清楚以前的数据缓存。
在第4节 actions 中可以直接使用 this 关键字操作,其实在 getters 中也一样可以使用 this 关键字。
getters: {
newPhone():string {
return this.phone.toString().replace(/^(\d{3})\d{4}(\d{4})$/, '$1****$2')
}
}
6. Pinia 中 Store 的互相调用
在真实的项目中我们会创建多个 store 仓库,多个 store 仓库之间可能会涉及内部相互调用问题。
创建一个新的 store
/src/store/znStore.ts
import {defineStore} from 'pinia';
// 这里注意传入的第一个参数 id 需要是唯一的
export const znStore = defineStore('znMain', {
state: () => ({
like: ['刘亦菲', '佟丽娅']
}),
getters: {},
actions: {}
})
在 /src/store/index.ts 中调用
// 引入 store
import {znStore} from './znStore';
actions: {
changeState(){
this.count = 100;
this.userinfo = {a: 111};
},
getLike(){
// 这里是个方法,使用的时候要注意
console.log(znStore().list);
}
}
在 app.vue 中调用该方法,会打印 znStore 的数据出来
<template>
<div class='home'>
</div>
</template>
<script lang='ts' setup>
import {useMainStore} from '@/store';
import {storeToRefs} from 'pinia';
const mainStore = useMainStore();
// 执行 mainStore 中的方法
mainStore.getLike()
</script>
7. 安装 vue-devtools 调试
更加便利的开发。
以上学完 Pinia 的内容。
学习的来源地址来自于 jspang:jspang.com/detailed?id…