定制化开发Vue3 状态管理之 Pinia 的使用

Vue3 定制化开发新的发展方向(定制化开发来源于尤大知乎)

  • Vue 3 将在 2022 年 2 月 7 日 定制化开发成为新的默认版本

  • 基于 定制化开发的极速构建工具链

  • <script setup> 定制化开发带来的开发体验更丝滑的组合式 API 语法

  • 定制化开发提供的单文件组件 IDE 支持

  • 定制化开发提供的针对单文件组件定制化开发的命令行类型检查和生成

  • 定制化开发提供的更简洁的状态管理

  • 定制化开发新的开发者工具扩展,同时支持 Vue 2/Vue 3,定制化开发并且提供一个来允许社定制化开发区库自行扩展开发者工具面板。

一、Pinia 简介与基础

1.1 Pinia 简介

  • 官方地址:
  • PiniaVuex4 的升级版,也就是 Vuex5
  • Pinia 极大的简化了Vuex的使用,是 Vue3的新的状态管理工具
  • Piniats的支持更好,性能更优, 体积更小,无 mutations,可用于 Vue2Vue3
  • Pinia支持Vue Devtools、 模块热更新和服务端渲染

1.2 Pinia 基础

  Vuex 与 Pinia 对比

  • Vuex 中核心部分: StateGettersMutations(同步) 和 Actions(异步)
  • Pinia 中核心部分: StateGettersActions(同步异步均支持)

  Pinia 各部分作用

  • State 类似于组件中data,用于存储全局状态
  • Getters 类似于组件中的computed,根据已有的State封装派生数据,也具有缓存的特性
  • Actions 类似于组件中的methods,用于封装业务逻辑,同步异步均可以

  Pinia 官方示例JS版本

import { defineStore } from 'pinia'export const todos = defineStore('todos', {  state: () => ({    /** @type {{ text: string, id: number, isFinished: boolean }[]} */    todos: [],    /** @type {'all' | 'finished' | 'unfinished'} */    filter: 'all',    // type will be automatically inferred to number    nextId: 0,  }),  getters: {    finishedTodos(state) {      // autocompletion! ✨      return state.todos.filter((todo) => todo.isFinished)    },    unfinishedTodos(state) {      return state.todos.filter((todo) => !todo.isFinished)    },    /**     * @returns {{ text: string, id: number, isFinished: boolean }[]}     */    filteredTodos(state) {      if (this.filter === 'finished') {        // call other getters with autocompletion ✨        return this.finishedTodos      } else if (this.filter === 'unfinished') {        return this.unfinishedTodos      }      return this.todos    },  },  actions: {    // any amount of arguments, return a promise or not    addTodo(text) {      // you can directly mutate the stat 00e      this.todos.push({ text, id: this.nextId++, isFinished: false })    },  },})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

二、Pinia 在Vue3-Vite中的使用

2.1 基础使用流程

  • ① 创建一个vue vite项目
PS C:\Users\FORGET\Desktop\vue-pinia-demo> npm init vite@latestNeed to install the following packages:  create-vite@latestOk to proceed? (y) y√ Project name: ... pinia-demo√ Select a framework: » vue√ Select a variant: » vue-tsScaffolding project in C:\Users\FORGET\Desktop\vue-pinia-demo\pinia-demo...Done. Now run:  cd pinia-demo  npm install  npm run devPS C:\Users\FORGET\Desktop\vue-pinia-demo> cd .\pinia-demo\PS C:\Users\FORGET\Desktop\vue-pinia-demo\pinia-demo> npm install
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • ② 安装 pinia-S是为了将其保存至package.json中,便于Git管理给其他人的使用
PS C:\Users\FORGET\Desktop\vue-pinia-demo\pinia-demo> npm install pinia -S
  • 1
# package.json文件中 "dependencies": {    "pinia": "^2.0.9",    "vue": "^3.2.25"  },
  • 1
  • 2
  • 3
  • 4
  • 5
  • ③ 创建 pinia 实例并挂载到 vue
// main.ts 文件import { createApp } from 'vue'import App from './App.vue'import {createPinia} from 'pinia'// 创建 Pinia 实例const pinia = createPinia()// 创建 Vue 实例const app = createApp(App)// 挂载到 Vue 根实例app.use(pinia)app.mount('#app')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • ④ 在src文件下创建一个store文件夹,并添加index.ts
// store/index.tsimport { defineStore } from 'pinia'// 1. 定义容器、导出容器// 参数1:容器的ID,必须是唯一的,后面Pinia会把所有的容器挂载到根容器// 参数2:一些选项对象,也就是state、getter和action// 返回值:一个函数,调用即可得到容器实例export const useMainStore =  defineStore('main',{    // 类似于Vue2组件中的data,用于存储全局状态数据,但有两个要求    // 1. 必须是函数,目的是为了在服务端渲染的时候避免交叉请求导致的数据状态污染    // 2. 必须是箭头函数,这样是为了更好的 TS 类型推导    state:()=>{        return {            info:"pinia 可以使用"        }    },    getters:{},    actions:{}})// 2. 使用容器中的 state// 3. 通过 getter 修改 state // 4. 使用容器中的 action 同步和异步请求
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • ⑤ 在组件中使用
<template>  <h1>{{ mainStore.info}}</h1></template><script lang="ts" setup>import { useMainStore } from "../store";const mainStore = useMainStore();</script><style></style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

2.2 state 中数据的解构访问

  • 状态管理中
// store/index.tsstate:()=>{        return {            info:"pinia 可以使用",            count:10        }    },
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 组件中
<template>  <h1>{{ mainStore.count }}</h1>  <h1>{{ mainStore.info }}</h1>  <hr />  <h1>{{ count }}</h1>  <h1>{{ info }}</h1>  <p>    <button @click="alertData">修改数据</button>  </p></template><script lang="ts" setup>import { toRefs } from 'vue'import { storeToRefs } from 'pinia'import { useMainStore } from "../store";const mainStore = useMainStore();// 解构数据,但是得到的数据是不具有响应式的,只是一次性的// 相当于仅仅只是...mainStore而已,只是做了reactive处理,并没有做toRefs// const { count, info } = useMainStore();// 解决方法:// 1. 通过使用toRefs函数,因为前面所说相当于是通过reactive处理,因此可以// const { count, info } = toRefs(mainStore);// 2. 通过pinia中提供的storeToRefs方法来解决,推荐使用const { count, info } = storeToRefs(mainStore);const alertData = () => {  mainStore.count += 10}</script><style></style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

2.3 state 中数据的修改方式(actions和组件中)

  • 一般的修改
const alertData = () => {  // 方式一:最简单的方法,如下  // 解构后更改方式  // count.value += 10  // 结构前更改方式  // mainStore.count += 10  // 方式二:若要同时修改多个数据,建议使用$patch来实现批量更新,在内部做了优化  // mainStore.$patch({  //   count: mainStore.count + 1,  //   info: "hello"  // })  // 方式三:更好的批量更新方法,通过$patch传递一个函数来实现,这里的state就是useMainStore容器中的state  mainStore.$patch(state => {    state.count += 10    state.info = "pinia批量更新"  })}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 通过actions修改
// store/index.ts// 类似于vue2组件的methods,用于封装业务逻辑,修改state// // 注意:不能使用箭头函数来定义actions,因为箭头函数绑定外部的this    actions:{        changeState (){            this.count += 10            this.info = "actions修改数据"        },        changeStates (num:number){            this.count += num + 2            this.info = "actions修改数据"        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
const alertData = () => {  // 方式一:最简单的方法,如下  // 解构后更改方式  // count.value += 10  // 结构前更改方式  // mainStore.count += 10  // 方式二:若要同时修改多个数据,建议使用$patch来实现批量更新,在内部做了优化  // mainStore.$patch({  //   count: mainStore.count + 1,  //   info: "hello"  // })  // 方式三:更好的批量更新方法,通过$patch传递一个函数来实现,这里的state就是useMainStore容器中的state  // mainStore.$patch(state => {  //   state.count += 10  //   state.info = "pinia批量更新"  // })  // 方式四:通过 actions 来修改数据  mainStore.changeState()  mainStore.changeStates(10)}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

2.4 getters 的使用

  • 定义
// 类似于组件的computed,用来封装计算属性,具有缓存的功能    getters:{    	 // 函数接收一个可选参数:state状态对象        count10(state){            return state.count += 10        },        count10(state){            return this.count += 10        },        // 若使用this.count,则必须指明返回数据的类型        count11():number{            return this.count += 11        }    },
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 使用
<h1>{{ mainStore.count10 }}</h1>
  • 1

三、Pinia 数据持久化

  • 保存至localStorage中
import { defineStore } from 'pinia';const useLoginStore = defineStore({  id: 'login',  //   state: () => ({  //     num: 1,  //   }),  state: () => ({    info: 'pinia 可以使用',  }),  getters: {},  actions: {    alertInfo() {      this.info = '可以可以,这个秒';    },  },});// 数据持久化// 1. 保存数据const instance = useLoginStore();instance.$subscribe((_, state) => {  localStorage.setItem('login-store', JSON.stringify({ ...state }));});// 2. 获取保存的数据,先判断有无,无则用先前的const old = localStorage.getItem('login-store');if (old) {  instance.$state = JSON.parse(old);}export default useLoginStore;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 使用 插件 pinia-plugin-persist 可以辅助实现数据持久化功能
# 安装插件pnpm install pinia-plugin-persist --save
  • 1
  • 2
// main.ts文件中import { createPinia } from 'pinia';import { createApp } from 'vue';import App from './App.vue';import router from './router';import piniaPluginPersist from 'pinia-plugin-persist';const pinia = createPinia();pinia.use(piniaPluginPersist);const app = createApp(App);app.use(router);app.use(pinia);app.mount('#app');
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
// 接着在对应的 store 里开启 persist 即可。数据默认存在 sessionStorage 里,并且会以 store 的 id 作为 key。import { defineStore } from 'pinia';import piniaPluginPersist from 'pinia-plugin-persist';const useLoginStore = defineStore({  id: 'login',  //   state: () => ({  //     num: 1,  //   }),  state: () => ({    info: 'pinia 可以使用',  }),  // 开启数据缓存  persist: {    enabled: true,  },  getters: {},  actions: {    alertInfo() {      this.info = '可以可以,这个秒';    },  },});export default useLoginStore;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 其它设置,自定义保存名称,保存位置和需要保存的数据
// 开启数据缓存  persist: {    enabled: true,    strategies: [      {        // 自定义名称        key: 'login_store',        // 保存位置,默认保存在sessionStorage        storage: localStorage,        // 指定要持久化的数据,默认所有 state 都会进行缓存,你可以通过 paths 指定要持久化的字段,其他的则不会进行持久化。        paths: ['age'],      },    ],  },
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
网站建设定制开发 软件系统开发定制 定制软件开发 软件开发定制 定制app开发 app开发定制 app开发定制公司 电商商城定制开发 定制小程序开发 定制开发小程序 客户管理系统开发定制 定制网站 定制开发 crm开发定制 开发公司 小程序开发定制 定制软件 收款定制开发 企业网站定制开发 定制化开发 android系统定制开发 定制小程序开发费用 定制设计 专注app软件定制开发 软件开发定制定制 知名网站建设定制 软件定制开发供应商 应用系统定制开发 软件系统定制开发 企业管理系统定制开发 系统定制开发