Vue3 定制化开发新的发展方向(定制化开发来源于尤大知乎)
-
Vue 3 将在 2022 年 2 月 7 日 定制化开发成为新的默认版本
-
基于 定制化开发的极速构建工具链
-
<script setup>
定制化开发带来的开发体验更丝滑的组合式 API 语法 -
定制化开发提供的单文件组件 IDE 支持
-
定制化开发提供的针对单文件组件定制化开发的命令行类型检查和生成
-
定制化开发提供的更简洁的状态管理
-
定制化开发新的开发者工具扩展,同时支持
Vue 2/Vue 3
,定制化开发并且提供一个来允许社定制化开发区库自行扩展开发者工具面板。
一、Pinia 简介与基础
1.1 Pinia 简介
- 官方地址:
Pinia
是Vuex4
的升级版,也就是Vuex5
Pinia
极大的简化了Vuex
的使用,是Vue3
的新的状态管理工具Pinia
对ts
的支持更好,性能更优, 体积更小,无mutations
,可用于Vue2
和Vue3
Pinia
支持Vue Devtools
、 模块热更新和服务端渲染
1.2 Pinia 基础
Vuex 与 Pinia 对比
Vuex
中核心部分:State
、Getters
、Mutations
(同步) 和Actions
(异步)Pinia
中核心部分:State
、Getters
和Actions
(同步异步均支持)
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