Pinia 是 Vue 的存储库,它允许您跨组件/页面共享状态。 如果您熟悉 Composition API,您可能会认为您已经可以通过一个简单的 export const state = reactive({})
来共享全局状态。但是这样在SPA页面中还行,在SSR应用中则会使您的应用程序暴露于安全漏洞。
Vuex: State、Gettes、Mutations(同步)、Actions(异步)
Pinia: State、Gettes、Actions(同步异步都支持)
Tips: 从 2022-02-07 在 Vue 3 被设置为默认版本开始, Pinia 已正式被官方推荐作为全局状态管理的工具。
- yarn add pinia
- # or with npm
- npm install pinia
查看你的 package.json ,看看里面的 dependencies
是否成功加入了 Pinia 和它的版本号(下方是示例代码,以实际安装的最新版本号为准):
- {
- "dependencies": {
- "pinia": "^2.0.11",
- },
- }
以 Vue3 + TypeScript
为例
打开 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 目录下创建一个 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:
前面我们创建了一个store,假如我们要在App.vue里面使用它,该如何使用呢?我们在页面中如何访问 state 里的属性 count?
- /src/App.vue
- <div>{{ user_store.count }}div>
- <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 中的状态但不调用任何操作时,这很有用:
- <div>{{ count }}div>
- <script lang="ts" setup>
- import { storeToRefs } from 'pinia'
- import { userStore } from '../src/store/user'
- const { count } = storeToRefs(userStore)
- script>
- /src/App.vue
- <div>{{ user_store.count }}div>
- <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
实例访问状态来直接读取和写入状态;重置状态:可以通过调用 store 上的 $reset()
方法将状态 重置 到其初始值。
有缓存功能。如下在页面中多次使用,第一次会调用 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: {
- },
- })
我们在App.vue中想要调用getters内的方法:
- // /src/App.vue
- <p>新年龄:{{ user_store.getAddAge }}p>
- <script setup lang="ts">
- import { useStore } from "../src/store/user";
- const user_store= useStore();
- script>
添加一个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。
我们在App.vue中想要调用actions内的方法:
- // /src/App.vue
- <div>{{ user_store.count }}div>
- <script setup lang="ts">
- import { useStore } from "../src/store/user";
- const user_store= useStore();
- const saveName = () => {
- user_store.saveName("我是哆啦A梦,希望有个一键三连!");
- };
- script>
如果还有兴趣学习pinia的其它特点,比如插件、订阅等等,可以移步官网:pinia官网。