快速了解 Pinia (上)基础篇
前言
Pinia
是 Vue 生态的状态管理工具,由 Vue 官方团队成员打造,是事实上的Vuex 5
。
Pinia
最大的优点就是简单。以一个个独立的Store
为核心,摒弃了Vuex
复杂的Mutations
、同步和异步统一合并到Actions
中等设计,配合优秀的Devtools
,使用体验提升巨大。
本文分上下两篇,首先谈谈基本使用,接下来通过一个购物车案例对它有一个比较生动的认识。请配合 Pinia 官方文档 阅读。
一、初始化项目
本例采用Vite
,在目录下新建一个Vite
项目:
yarn cerate vite
一路选下去(记得是vue-ts
),然后在新项目内安装Pinia
依赖:
yarn add pinia
最后安装项目全部依赖,便可开始编写工作:
yarn install
二、将 Pinia
实例 挂载到 Vue
应用
在工作区下的main.ts
,导包后创建实例,并用.use()
挂载到当前App
上:
// main.ts
import { createApp } from "vue";
import { createPinia } from "pinia"; // +
import "./style.css";
import App from "./App.vue";
const app = createApp(App);
//创建 pinia 实例 // +
const pinia = createPinia(); // +
app.use(pinia); // +
app.mount("#app");
三、创建Store
在工作区目录下新建 Store/
,用来放置所有的Store
。 推荐一个 .ts
文件内只放一个Store
。
使用defineStore()
创建并返回一个Store
实例,接收两个参数
name:string `Store`的唯一 id ,Pinia 将用它来连接 `store` 和 `devtools`。
options:object 一个对象,内部规定了 state / acions / getters
state
类似 Vue 组件的data
,本质是一个箭头函数。数据要定义在箭头函数的return {...}
内。- 必须是函数:避免在服务端渲染时,交叉请求导致的数据状态污染
- 必须是箭头函数:为了 TS 更好的类型推导
getters
类似 Vue 组件的computed
,本质是一个对象。内部的函数如果想获取state
内部值,需要声明参数state
actions
类似 Vue 组件的methods
,本质是一个对象。内部的函数如果想获state
内部值,需要使用this
最后记得把这个Pinia
实例导出去
实例名一般为
use
Xxxx
Store
defineStore()
的name
命名为xxxx
// src/store/index.ts
import { defineStore } from "pinia";
export const useMainStore = defineStore("main", {
state: () => {
return {
count: 100,
foo:"bar",
arr:[1,2,3],
};
},
getters: {
// 参数 state 是可选的
count10(state):number{
console.log("getters !");
return state.count+10;
}
},
//action 不可用箭头函数定义
actions: {
changeState(num:number){
this.count+=num;
this.foo="fixed via Store-action"
this.arr.push(8)
}
},
});
四、使用 store
1. 组件中导入store
并实例化
// src/componet/HelloWorld.vue
import { useMainStore } from "../store/index";
import { storeToRefs } from "pinia";
//获得 Store 实例
const mainStore = useMainStore();
如果希望从store
引用的数据是响应式的,即在本组件修改的state
后能实时更新到store
里,还需导入storeToRefs
2.响应式数据
出于响应式数据的目的,这样使用数据是错的:
const { count, foo } = mainStore;
因为这只是复制了一份数据,修改后并不能更新store
状态。
正确用法是将解构出来的数据做 ref 响应式处理,如下:
const { count, foo, arr } = storeToRefs(mainStore);
3.在组件中访问、修改 state
- 方式一:直接访问 / 修改单个数据
const mainStore = useStore()
mainStore.count++
mainStore.foo = "hello"
mainStore.$reset() // 重置回初始值
- 方式二:通过
$patch()
批量修改多个数据,并不是写法上的简便,而是为了性能上的优化
mainStore.$patch({
count: mainStore.count + 1,
foo: "hello",
arr: [...mainStore.arr, 4], // 对于 array 等复杂结构,这样写法太低效
})
但是上面这种做法也是有问题的:虽然可以通过直接赋值修改状态,但是对于像 Array
等数据类型,无法获取该对象,这意味着所有的类方法都不能使用。
- 方式三:
$patch()
参数内改用回调函数:
mainStore.$patch((state) => {
state.count++;
state.foo = "hello";
state.arr.push(5); // 可以调用方法了
});
- 方式四:当然了,逻辑再复杂的话,直接调用
store
的acions
定义的函数即可。
// src/store/index.ts
export const useMainStore = defineStore("main", {
...
actions: {
changeState(num:number){
this.count+=num;
this.foo="fixed via Store-actions"
this.arr.push(8)
}
},
});
// src/componet/HelloWorld.vue
<template>
<div>{{mainStore.count}}</div>
<div>{{mainStore.foo}}</div>
<hr />
<div>{{mainStore.count10}} 第1次调用 getters,调用count10()计算count+10</div>
<div>{{mainStore.count10}} 第2次调用 getters,属性值不变,不再调用count10()计算</div>
<div>{{mainStore.count10}} 第3次调用 getters,属性值不变,不再调用count10()计算</div>
<p>
<button @click="handleChangeCount">修改count</button>
</p>
</template>
<script lang="ts" setup>
import { useMainStore } from "../store"; // 文件路径默认为 index ,不写也行
import { storeToRefs } from "pinia";
//获得 Store 实例
const mainStore = useMainStore();
const { count, foo, arr } = storeToRefs(mainStore);
const handleChangeCount = () => {
//方式4:数据操作比较多时,可在 actions 封装函数,然后在普通组件中调用这些现成的函数
mainStore.changeState(10);
};
</script>
4. getters
(computed
) 的特性
getters
返回state
中某个变量进行特定计算后的结果,这个结果是一个缓存值。如果该变量状态没发生变化,那么
getters
无论调用多少次,值都不变。
本篇介绍了Pinia
的基础使用,下篇将以一个购物车案例,将会有更生动的了解。
转载自:https://juejin.cn/post/7160934086314295303