介绍——为什么要使用 Pinia?
Pinia 是 Vue 的存储库,它允许您跨组件/页面共享状态。 如果您熟悉 Composition API,您可能会认为您已经可以通过一个简单的 export const state = reactive({})
来共享全局状态。但是这样在SPA页面中还行,在SSR应用中则会使您的应用程序暴露于安全漏洞。
Pinia 和 Vuex对比
Vuex: State、Gettes、Mutations(同步)、Actions(异步)
Pinia: State、Gettes、Actions(同步异步都支持)
Pinia 核心特性:
- dev-tools 支持
- 跟踪动作、突变的时间线
- Store 出现在使用它们的组件中
- time travel 和 更容易的调试
- 热模块更换
- 在不重新加载页面的情况下修改您的 Store
- 在开发时保持任何现有状态
- 支持options api 和 composition api
- Actions 支持同步和异步
- 去除 mutations 比vuex更加精简
- 为 JS 用户提供更好的 TypeScript 支持
- 无需手动添加 store,它的模块默认情况下创建就自动注册的
- 服务器端渲染支持
- 轻量 压缩后的体积只有1kb左右
Tips: 从 2022-02-07 在 Vue 3 被设置为默认版本开始, Pinia 已正式被官方推荐作为全局状态管理的工具。
Pinia安装
yarn add pinia
# or with npm
npm install pinia
查看你的 package.json ,看看里面的 dependencies
是否成功加入了 Pinia 和它的版本号(下方是示例代码,以实际安装的最新版本号为准):
{
"dependencies": {
"pinia": "^2.0.11",
},
}
Pinia使用
以 Vue3 + TypeScript
为例
引入Pinia
打开 src/main.ts
文件,添加下面那两行有注释的新代码:
import { createApp } from 'vue'
import { createPinia } from 'pinia' // 导入 Pinia
import App from '@/App.vue'
createApp(App)
.use(createPinia()) // 启用 Pinia
.mount('#app')
在上面的片段中,你将Pinia添加到Vue项目中,这样你就可以在你的代码中使用Pinia的全局对象。
定义Store
在 store 目录下创建一个 users.ts
为例,我们先定义并导出一个名为 users
的模块
// /src/store/user.ts
// 想要使用必须先引入 defineStore;
import { defineStore } from 'pinia';
// 这里我们使用的是es6 的模块化规范进行导出的。
// defineStore 方法有两个参数,第一个参数是模块化名字
// 第二个参数是选项,对象里面有三个属性,相比于vuex 少了一个 mutations.
export const useStore = defineStore('users', {
state(){ // 存放的就是模块的变量
return {
count: 1,
arr: []
}
},
getters:{ // 相当于vue里面的计算属性,可以缓存数据
},
actions:{ // 可以通过actions 方法,改变 state 里面的值。
}
})
我们需要知道 Store 是使用 defineStore()
定义的,并且它需要一个唯一名称,作为第一个参数传递。defineStore
函数接收两个参数name、options:
- name:一个字符串,必传项,该store的唯一id。
- options:
- 其中 state 用来存储全局状态,它必须是箭头函数,为了在服务端渲染的时候避免交叉请求导致的数据状态污染所以只能是函数,而必须用箭头函数则为了更好的 TS 类型推导;
- getters 就是用来封装计算属性,它有缓存的功能;
- actions 就是用来封装业务逻辑,修改 state。
使用Store
访问state
前面我们创建了一个store,假如我们要在App.vue里面使用它,该如何使用呢?我们在页面中如何访问 state 里的属性 count?
/src/App.vue
<template>
<div>{{ user_store.count }}</div>
</template>
<script setup lang="ts">
import { useStore } from "../src/store/user";
const user_store= useStore();
console.log(user_store);
console.log(user_store.count);
// 解构
// const { count } = userStore() // 这个是错误的,拿到的数据不是响应式
</script>
上述代码就可以轻松拿到store内的属性值count。
但是,请注意,store
是一个用reactive
包裹的对象,就像setup
中的props
一样,我们不能对其进行解构。这样拿到的数据不是响应式的。
为了从 Store 中提取属性同时保持其响应式,您需要使用storeToRefs()。 它将为任何响应式属性创建 refs。 当您仅使用 store 中的状态但不调用任何操作时,这很有用:
<template>
<div>{{ count }}</div>
</template>
<script lang="ts" setup>
import { storeToRefs } from 'pinia'
import { userStore } from '../src/store/user'
const { count } = storeToRefs(userStore)
</script>
修改state,重置state
/src/App.vue
<template>
<div>{{ user_store.count }}</div>
</template>
<script setup lang="ts">
import { useStore } from "../src/store/user";
const user_store= useStore();
// 通过 store 实例访问状态来直接修改状态
user_store.count++
// 重置 store 上的 $reset() 方法将状态 重置 到其初始值:
user_store.$reset()
</script>
修改状态:
- 可以通过
store
实例访问状态来直接读取和写入状态; - 可以通过 $patch方法传递一个函数来修改;
- 可以通过 $patch方法传递一个对象来修改;
- 当逻辑比较多或者请求的时候,我们就可以封装到示例中 src/store/user.ts 里的 actions 里。(后面讲action会细讲)
重置状态:可以通过调用 store 上的 $reset()
方法将状态 重置 到其初始值。
getters
添加getters
有缓存功能。如下在页面中多次使用,第一次会调用 getters,数据没有改变的情况下之后会读取缓存。
// /src/store/user.ts
import { defineStore } from 'pinia';
export const useStore = defineStore('users', {
state(){ // 存放的就是模块的变量
return {
name: "回忆哆啦没有A梦",
age: 25,
sex: "男",
}
},
getters: {
// 方法一,接收一个可选参数 state
getAddAge(state){
console.log('调用了') // 页面中使用了多次,这里只会执行一次,然后缓存起来了
return state.age+ 1
},
// 方法二,不传参数,使用 this
// 但是必须指定函数返回值的类型,否则类型推导不出来
getAddAge(): number{
return this.age+ 1
}
},
actions: {
},
})
使用getters
我们在App.vue中想要调用getters内的方法:
// /src/App.vue
<template>
<p>新年龄:{{ user_store.getAddAge }}</p>
</template>
<script setup lang="ts">
import { useStore } from "../src/store/user";
const user_store= useStore();
</script>
actions
添加actions
添加一个actions方法,修改state。
// /src/store/user.ts
import { defineStore } from 'pinia';
export const useStore = defineStore('users', {
state(){ // 存放的就是模块的变量
return {
name: "回忆哆啦没有A梦",
age: 25,
sex: "男",
}
},
getters: {
},
actions: {
saveName(name: string) {
this.name = name;
},
},
})
上段代码中我们定义了一个非常简单的actions方法changeState,在实际场景中,该方法可以是任何逻辑,比如发送请求、存储token等等。大家把actions方法当作一个普通的方法即可,特殊之处在于该方法内部的this指向的是当前store。
使用actions
我们在App.vue中想要调用actions内的方法:
// /src/App.vue
<template>
<div>{{ user_store.count }}</div>
</template>
<script setup lang="ts">
import { useStore } from "../src/store/user";
const user_store= useStore();
const saveName = () => {
user_store.saveName("我是哆啦A梦,希望有个一键三连!");
};
</script>
如果还有兴趣学习pinia的其它特点,比如插件、订阅等等,可以移步官网:pinia官网。