Skip to content
快速导航

Pinia快速上手

简介

Pinia一个全新的状态管理库,是Vuex的替代者,有以下优势:

  • [x] Vue2Vue3都支持,并且支持TypeScript
  • [x] 抛弃 Mutation ,只有 state, getteraction ,简化状态管理库✨
  • [x] 具有Option Api模式也具有Compositon Api模式✨
  • [x] 非常nice的代码自动分割✨
  • [x] 体积小只有1~2kb左右✨

快速开始

Option Api,适合入门😋

安装

可以用自己喜欢的包管理器下载,我这里使用pnpm😋

bash
# 使用npm
npm i pinia
# 使用yarn
yarn add pinia
# 使用pnpm
pnpm add pinia

挂载

如果您使用的是Vue3可以创建一个pinia并将其传递给应用程序:

ts
// src/main.ts
import { createPinia } from 'pinia'
const pinia = createPinia()
app.use(pinia)

如果您使用的是Vue2,则还需要一个插件PiniaVuePlugin

ts
// 其他...
import { createPinia, PiniaVuePlugin } from 'pinia'

Vue.use(PiniaVuePlugin)
const pinia = createPinia()

new Vue({
  el: '#app',
  // 其他...
  // 一个 `pinia` 实例可以在多个 Vue 应用程序中使用
  // 同一个页面
  pinia,
})

创建store

请您在src下创建stores文件夹,随后创建counter.ts或者为counter.js,添加如下内容:

ts
// src/stores/counter.ts
import { defineStore } from "pinia";

// 使用options API模式定义
export const useCounterStore = defineStore("counter", {
  // 定义state,就是状态/数据
  state: () => {
    return {
      count: 0,
    };
  },
  // getters相当于计算属性computed
  getters: {
    doubleCount: (state) => {
      return state.count * 2;
    },
  },
  // actions支持同步和异步
  actions: {
    increment() {
      this.count++
    },
  },
});

使用store

我们可以任意一个组件使用,为了方便,我这边在App.vue中直接使用:

vue
<!-- src/App.vue -->
<script setup lang="ts">
    
import { useCounterStore } from "./stores/counter";

const counter = useCounterStore();

// ES解构,不具有响应性!!!!
const { count, doubleCount } = counter;

const increment = () => {
  counter.increment();
};
    
</script>

<template>
  <div>count的值为:{{ count }}</div>
  <div>count值的双倍为:{{ doubleCount }}</div>
  <button @click="increment">+1</button>
  <!-- <MyChild /> -->
</template>

<style scoped lang="less"></style>

您可能会发现,当点击按钮的时候,视图上的数据并不能更新😅,但实际上count已经增加了。对于此,我们可以使用storeToRefs让他具有响应性:

vue
<!-- src/App.vue -->
<script setup lang="ts">
// 其他...  
import { storeToRefs } from "pinia";
// 其他... 
const { count, doubleCount } = storeToRefs(counter);
// 其他...    
</script>

当你再次点击+1按钮的时候就会发现视图也会更新了😋

定义state

故名思意,state就是用于定义状态的,推荐使用箭头函数返回一个对象:

ts
// src/stores/counter.ts

// 使用Options Api模式定义
export const useCounterStore = defineStore("counter", {
  // 定义state,就是状态/数据
  state: () => {
    return {
      count: 0,
    };
  },
});

访问state

访问state非常简单,你可以使用解构,也可以使用.运算符访问:

ts
// src/App.vue

import { useCounterStore } from "./stores/counter";

const counter = useCounterStore();

// ES解构,不具有响应性!!!!
const { count, doubleCount } = counter;
// 或者直接使用counter.count

定义getter

getter相当于computed属性,一般用于定义state的派生值,即使是多次使用也只会调用一次:

ts
// src/stores/counter.ts 

getters: {
    // 函数会就接收state对象,我们可以对state的某些属性值进行衍生
    doubleCount: (state) => {
      return state.count * 2;
    },
  },

修改state

  • 简单使用下,我们可以通过实例直接对它进行修改:
ts
// src/App.vue

const counter = useCounterStore();
counter.count++;
  • 需要替换的时候,可以设置一个新对象来替换整个counter$state
ts
// src/App.vue

const counter = useCounterStore();
counter.$state = {count:999}
  • 使用$patch方法,$patch 方法可以接受两个类型的参数,对象函数
ts
// src/App.vue

const counter = useCounterStore();
// $patch + 对象 的形式
const increment = () => {
  counter.$patch({
    count: counter.count + 1,
  });
};

// 或者: $patch + 函数 的形式,函数可以接收到state
const increment_2 = () => {
  counter.$patch((state) => {
    return {
      count: state.count + 1,
    };
  });
};
  • 使用action进行修改,比较推荐使用,说明如下:
ts
// src/stores/counter.ts 

// 支持同步也支持异步
actions: {
    // 增加increment方法,其中 `this` 代指 `state`
    increment() {
      this.count++;
    },
  },

当我们需要调用的时候使用store.方法名

ts
// src/App.vue

const counter = useCounterStore();
const increment = () => {
  counter.increment();
};
  • 有些时候我们可能需要重置state,对于Option Api模式来说,可以使用$reset方法进行重置:
ts
// src/App.vue

const counter = useCounterStore();
counter.$reset()

一点点进阶

Compositon Api模式

Compositon Api模式

让我们先回顾一下Option Api模式的写法:

ts
// src/stores/counter.ts
import { defineStore } from "pinia";

// 使用Options Api模式定义
export const useCounterStore = defineStore("counter", {
  // 定义state,就是状态/数据
  state: () => {
    return {
      count: 0,
    };
  },
  // getters相当于计算属性computed
  getters: {
    doubleCount: (state) => {
      return state.count * 2;
    },
  },
  // actions支持同步和异步
  actions: {
    increment() {
      this.count++
    },
  },
});

在这里,我们直接给出结果😆:

  • Option Apistate相当于Compositon Apirefreactive定义的变量
  • Option Apigetter相当于Compositon Apicomputed
  • Option Apiaction相当于Compositon Apifunction

我们可以得到如下代码😋:

ts
// src/stores/counter.ts
import { ref, computed } from "vue";
import { defineStore } from "pinia";
// 使用Compositon Api模式定义
export const useCounterStore = defineStore("counter", () => {
  // 相当于state
  const count = ref<number>(0);
    
  const doubleCount = computed(() => count.value * 2);
    
  const increment = () => {
    count.value++;
  };

  return { count, doubleCount, increment };
});

这样的书写方式跟我们平常写Compositon Api没有什么区别,但是这个写法也是有缺陷的,因为会不支持某些内置方法(例如$reset()),如果您想重置state,那么就需要重写reset方法,例如:

ts
// 使用Composition Api模式
export const useCounterStore = defineStore('counter', () => {
  // 初始状态
  const initState = {
    count: 1
  };

  const count = ref<number>(initState.count);
    
  const doubleCount = computed(() => count.value * 2);
    
  function increment() {
    count.value++;
  }

  // 重写reset
  function reset() {
    count.value = initState.count;
  }

  return { count, doubleCount, increment, reset };
});

到底用哪一种呢?其实我也说不好,具体情况看团队如何规定吧。

本地数据持久化

使用pinia-plugin-persist存储到sessionStoragelocalStorage,只支持Option Api模式😭

有些时候我们还需要将数据存储到本地,一般情况下可以直接存储,这里就不过多说明。这里会提供pinia-plugin-persist插件实现本地数据持久化😋

安装

bash
# npm
npm i pinia-plugin-persist
# yarn
yarn add pinia-plugin-persist
# pnpm
pnpm add pinia-plugin-persist

使用

pinia使用插件
ts
// src/main.ts
import { createPinia } from 'pinia'
import piniaPluginPersist from 'pinia-plugin-persist'

const pinia = createPinia()

pinia.use(piniaPluginPersist)

// ....
给tsconfig增加type字段

如果使用的是js可以跳过这一步

ts
{
  "compilerOptions": {
    "types": [
      "pinia-plugin-persist"
    ]
  },
}
存储到sessionStorage
ts
// src/stores/userInfo.ts

import { defineStore } from 'pinia'
export const useUserInfoStore = defineStore('userInfo', {
  // 定义state
  state: () => {
    return {
      name: 'songnian',
      age: 19,
      token: 'abcdefg',
    }
  },
  getters: {},
  actions: {},
  // persist就是插件的设置,默认情况下存储到`sessionStorage`,并且`key`值为`storeID`
  persist: {
    enabled: true,
  },
})
存储到localStorage
ts
  persist: {
    enabled: true,
     strategies: [
         // key可以自定义,没有paths属性的时候就默认选择全部状态
  		{ key: 'allInfo', storage: localStorage},
	],
  },
自定义存储

当然我们也可以自定义存储,在strategies配置项修改即可。比如我需要把keymyInfo,值为nameage存储到sessionStorage;需要把keymyToken,值为token存储到localStorage。我们可以有如下代码:

ts
  // 自定义存储
  persist: {
    enabled: true,
    strategies: [
        // paths属性存放需要持久化的状态的`key`值
  		{ key: 'myInfo', storage: sessionStorage, paths: ['name', 'age'] },
  		{ key: 'myToken', storage: localStorage, paths: ['token'] },
	],
  },

tips: state初始化的时候并不会存储到相应的位置,当state状态变更的时候,才会存储到对应的位置

结语

本篇文章主要用于快速学习Pinia,做了一份简单的总结,更多的玩法还要查阅更多的资料

Released under the MIT License.