vue3 快速入门系列 —— 状态管理 pinia
其他章节请看:
pinia
vue3 状态管理这里选择 pinia。
虽然 vuex4 已支持 Vue 3 的 Composition API,但是 vue3 官网推荐新的应用使用 pinia —— vue3 pinia
集中式状态管理
redux、mobx、vuex、pinia都是集中式状态管理工具。与之对应的就是分布式。
Pinia 符合直觉
的 Vue.js 状态管理库 甚至让你忘记正在使用的是一个状态库 —— 官网
安装 pinia 环境
首先下载安装包:
PS hello_vue3> npm i pinia
added 2 packages, and audited 71 packages in 11s
10 packages are looking for funding
run `npm fund` for details
1 moderate severity vulnerability
To address all issues, run:
npm audit fix
Run `npm audit` for details.
"dependencies": {
"pinia": "^2.1.7",
"vue": "^3.4.15",
"vue-router": "^4.3.0"
},
在 main.ts 中依次:引入、创建和安装 pinia,在浏览器 vue 开发者工具中就能看到 pinia(一个菠萝图标)。
import {createApp} from 'vue'
import App from './App.vue'
import router from './router'
// 引入
import { createPinia } from 'pinia'
const app = createApp(App)
// 创建
const pinia = createPinia()
app.use(router)
// 安装:就像安装 vue-router 一样使用
app.use(pinia)
app.mount('#app')
有时这个菠萝
没出现,可以关闭浏览器或重启服务。
Tip: 详细请看 pinia 安装官网
第一个示例
vuex 的核心概念有 State、Getters、Mutations、Actions和Modules。其中 State 是数据,我们不能直接修改数据。
pinia 比 vuex 更轻量,更易使用。比如拿到数据后就能直接改,符合直觉
。
请看示例:
pinia 的数据从项目目录上说,会放在 store 文件夹中。
通常我们会对状态进行分类,比如用户相关的数据放在 store/user.ts 中:
// src/store/user.ts
import { defineStore } from 'pinia'
// 你可以任意命名 `defineStore()` 的返回值,但最好使用 store 的名字,同时以 `use` 开头且以 `Store` 结尾。
// (比如 `useUserStore`,`useCartStore`,`useProductStore`)
// 第一个参数是你的应用中 Store 的唯一 ID。 Pinia 将用它来连接 store 和 devtools
export const useUserStore = defineStore('user', {
// actions 里面放一个一个的方法,用于响应组件中的动作
actions: {
changeNameAndAge() {
// this Proxy(Object)
// 里面有 $state。在 vue2 中有 $watch、$on等以$开头的都是给程序员用的实例方法
console.log('this', this);
// 没必要通过 $state,直接访问即可
this.name += '~'
this.$state.age += 1
}
},
state: () => {
return {
name: 'peng',
age: 18,
}
},
})
通过 defineStore 定义一个 store,第一个参数是 store 的id,命名建议规范,例如使用文件名,导出方式这里选择分别导出,导出的名字使用 use+user+store。
state 是一个函数,返回的就是数据
actions 中是一个一个的方法,但不需要像 vuex 中需用 dispatch 触发。
接着在需要使用状态的地方使用。读取状态的方式有2种,修改状态的方式有3种:
// Home.vue
<template>
<div>
<!-- 读取方式1 -->
<p>{{ userStore.name }}</p>
<!-- 读取方式2。方式1更方便 -->
<p>{{ userStore.$state.age }}</p>
<p><button @click="changeNameAndAge">修改方式1:change age and name</button></p>
<p><button @click="changeNameAndAge2">修改方式2:change age and name</button></p>
<p><button @click="changeNameAndAge3">修改方式3:change age and name</button></p>
</div>
</template>
<script lang="ts" setup name="App">
// 写 '@/store/user.ts' vscode 报错:An import path can only end with a '.ts' extension when 'allowImportingTsExtensions' is enabled
// 去掉 .ts 即可
import {useUserStore} from '@/store/user'
const userStore = useUserStore()
// userStore: Proxy(Object)
console.log('userStore: ', userStore);
function changeName() {
// 修改数据方式1:直接操作数据
// vue2中的vuex必须通过mutation更新数据,不能直接修改数据
userStore.name += '~'
}
function changeNameAndAge() {
userStore.$state.name += '~'
userStore.$state.age += 1
}
function changeNameAndAge2() {
// $patch 用于批量修改
// patch 中文“碎片”,比如age 就是 pinia 中一个数据碎片
userStore.$patch({
name: userStore.name + '~',
age: userStore.age + 1
})
}
function changeNameAndAge3() {
// 调用 actions
userStore.changeNameAndAge()
}
</script>
Tip: changeNameAndAge 会触发两次修改,而 changeNameAndAge2 使用 $patch 会进行批量修改,从开发者时间线中看到,只执行一次。如果很多数据同时修改,推荐使用 patch。
优雅的读取数据
前面我们是这么读取 store 中数据:
<p>{{ userStore.name }}</p>
const userStore = useUserStore()
如果需要读取的数据太多,在模板中就会出现很多 userStore,于是我们想到用 toRefs 解构解决。就像这样:
<p>优雅的读:{{ name }}</p>
import {toRefs} from 'vue'
const userStore = useUserStore()
const {name} = toRefs(userStore)
但是 toRefs(userStore) 太重,通过console.log(toRefs(userStore))
你会发现toRefs将 store 所有属性(包括方法)都转成 ref,其实我们只需要将数据转成 ref 即可。
pinia 也想到了这个问题,于是可以用 storeToRefs 替代。就像这样:
<p>优雅的读:{{ name }}</p>
import {storeToRefs} from 'pinia'
const userStore = useUserStore()
const {name} = storeToRefs(userStore)
// storeToRefs(userStore): {name: ObjectRefImpl, age: ObjectRefImpl}
console.log('storeToRefs(userStore): ', storeToRefs(userStore));
// toRefs(userStore): {$id: ObjectRefImpl, $onAction: ObjectRefImpl, $patch: ObjectRefImpl, $reset: ObjectRefImpl, $subscribe: ObjectRefImpl, …}
console.log('toRefs(userStore): ', toRefs(userStore));
Getters
和 vuex 中 Getters 作用相同,用法类似。
这里用了两种方式定义 getters:
state: () => {
return {
name: 'Peng',
age: 18,
}
},
getters: {
// 推荐使用箭头函数,参数会传入 state
bigName: (state) => state.name.toLocaleUpperCase(),
// 如果需要访问其他 getters 属性,可以通过非箭头函数,通过 this 访问
lowerName2(): string{
return this.bigName.toLocaleLowerCase()
}
},
数据取得的方式和 state 相同:
<p>优雅的读:{{ name }}</p>
<p>bigName:{{ bigName }}</p>
<p>lowerName2:{{ lowerName2 }}</p>
const userStore = useUserStore()
const {name, bigName, lowerName2} = storeToRefs(userStore)
Tip:详细请看 pinia getters
订阅
类似于 Vuex 的 subscribe 方法,你可以通过 store 的 $subscribe() 方法侦听 state 及其变化
只要 userStore 中的数据变化了,函数就会被调用,我们通常关心第二个参数:
// 只要 userStore 数据变化,这个
userStore.$subscribe((mutation, state) => {
// {storeId: 'user', type: 'direct', events: {…}}
console.log('mutation: ', mutation);
// Proxy(Object) {name: 'Peng~', age: 19}
console.log('state: ', state);
// 每当状态发生变化时,将整个 state 持久化到本地存储。
localStorage.setItem('userStore', JSON.stringify(state))
})
我们可以将 state 存储到本地,这样就可以实现页面刷新,状态不丢失。
Tip: 细节请看 订阅 state。
组合式写法
目前 actions state 写法属于声明式的:
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
actions: {
changeNameAndAge() {
this.name += '~'
this.$state.age += 1
}
},
state: () => {
return {
name: 'Peng',
age: 18,
}
},
getters: {
bigName: (state) => state.name.toLocaleUpperCase(),
lowerName2(): string{
return this.bigName.toLocaleLowerCase()
}
},
})
将其改成组合式。代码如下:
import { defineStore } from 'pinia';
import {ref, computed} from 'vue'
export const useUserStore = defineStore('user', () => {
// 数据用 ref 或 reactive 定义
const name = ref('Peng')
const age = ref(18)
// getters 用计算属性
const bigName = computed(() => name.value.toLocaleUpperCase())
const lowerName2 = computed(() => bigName.value.toLocaleLowerCase())
// actions 用方法定义
function changeNameAndAge() {
name.value += '~';
age.value += 1;
}
// 最后必须暴露出去
return {
// vscode 中数据一个颜色、方法另一个颜色
name,
age,
bigName,
lowerName2,
changeNameAndAge,
};
});
Tip:组合式写法更灵活(请看 组合式 Store),层级少,但必须返回。具体如何选择自行决定。
扩展
ref 数据要不要 .value
const a = reactive({
x: 1,
y: 2,
z: ref(3)
})
const b = ref(4)
console.log(a.x)
// ref 如果在里面,则不需要拆包
console.log(a.z)
console.log(b.value)
读取响应式对象中的 ref 不需要 .value
其他章节请看:
vue3 快速入门系列 —— 状态管理 pinia的更多相关文章
- vue 快速入门 系列 —— vue-cli 上
其他章节请看: vue 快速入门 系列 Vue CLI 4.x 上 在 vue loader 一文中我们已经学会从零搭建一个简单的,用于单文件组件开发的脚手架:本篇,我们将全面学习 vue-cli 这 ...
- 快速入门系列--WebAPI--01基础
ASP.NET MVC和WebAPI已经是.NET Web部分的主流,刚开始时两个公用同一个管道,之后为了更加的轻量化(WebAPI是对WCF Restful的轻量化),WebAPI使用了新的管道,因 ...
- 快速入门系列--WebAPI--03框架你值得拥有
接下来进入的是俺在ASP.NET学习中最重要的WebAPI部分,在现在流行的互联网场景下,WebAPI可以和HTML5.单页应用程序SPA等技术和理念很好的结合在一起.所谓ASP.NET WebAPI ...
- 快速入门系列--MVC--01概述
虽然使用MVC已经不少年,相关技术的学习进行了多次,但是很多技术思路的理解其实都不够深入.其实就在MVC框架中有很多设计模式和设计思路的体现,例如DependencyResolver类就包含我们常见的 ...
- Linux快速入门03-系统管理
这部分将涉及常用的各类linux命令和一些系统高级管理特性,尤其是shell script的创建,这部分在系统自动化运维时会很有作用. Linux系列文章 快速入门系列--Linux--01基础概念 ...
- [转]快速入门系列--WebAPI--01基础
本文转自:http://www.cnblogs.com/wanliwang01/p/aspnet_webapi_base01.html ASP.NET MVC和WebAPI已经是.NET Web部分的 ...
- vue 快速入门 系列 —— 初步认识 vue
其他章节请看: vue 快速入门 系列 初步认识 vue vue 是什么 Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架. 所谓渐进式,就是你可以一步一步.有阶段 ...
- vue 快速入门 系列 —— vue 的基础应用(上)
其他章节请看: vue 快速入门 系列 vue 的基础应用(上) Tip: vue 的基础应用分上下两篇,上篇是基础,下篇是应用. 在初步认识 vue一文中,我们已经写了一个 vue 的 hello- ...
- vue 快速入门 系列 —— vue 的基础应用(下)
其他章节请看: vue 快速入门 系列 vue 的基础应用(下) 上篇聚焦于基础知识的介绍:本篇聚焦于基础知识的应用. 递归组件 组件是可以在它们自己的模板中调用自身的.不过它们只能通过 name 选 ...
- vue 快速入门 系列 —— 使用 vue-cli 3 搭建一个项目(上)
其他章节请看: vue 快速入门 系列 使用 vue-cli 3 搭建一个项目(上) 前面我们已经学习了一个成熟的脚手架(vue-cli),笔者希望通过这个脚手架快速搭建系统(或项目).而展开搭建最好 ...
随机推荐
- 记录--有关CSS盒模型之内边距、边框、外边距的十九问题
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 本篇文章主要探讨盒模型,以及内边距.边框.外边距的面试题与思考,也希望您能把您的思考和遇到的问题以评论的方式补充下,后期,我将会补充到文章 ...
- 记录--微信小程序获取用户信息(附代码、流程图)
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 本篇文章主要总结了微信小程序开发,获取用户信息的整个流程步骤.补充了网上很多碎片化的代码,本人梳理了思路写下了这篇文章. 思路 1.在js ...
- verilog之readmemb
verilog之readmemb 1.基本作用 用于读取存储器的值的系统函数.这里首先要知道什么是存储器.在verilog中,有一些比较大的数据是需要存储的,一般需要使用存储器,语法结构类似二维数组. ...
- IDEA 2018 激活(UMTIMATE)
IDEA延长使用期限 这是我的软件About,2018版本,延期至2089. 先下载压缩包解压后得到jetbrains-agent.jar. 下载页面:https://zhile.io/2018/08 ...
- #阶梯NIM,树形dp#CF1498F Christmas Game
题目 Alice 和 Bob 在一棵 \(n\) 个点的树上玩游戏,第 \(i\) 个节点上有 \(a_i\) 个石子, 每轮可以选择一个深度至少为 \(k\) 的节点并移动任意多石子到其 \(k\) ...
- #博弈论#Poj 2484 A Funny Game
题目 \(n\)个石子排成一圈,每次可以取一个或相邻的一对, 取完为胜,问先手是否必胜 分析 无论先手如何取,后手都能模仿先手的取法. 比如说,当石子个数为奇数时先手取相邻的一对,后手可以将对面的那一 ...
- go~在阿里mse上使用redis.call
相关依赖 github.com/higress-group/proxy-wasm-go-sdk github.com/alibaba/higress/plugins/wasm-go 标准的redis ...
- 使用site-maven-plugin在github上搭建公有仓库
目录 简介 前期准备 在maven中配置GitHub权限 配置deploy-plugin 配置site-maven-plugin 怎么使用这个共享的项目 总结 简介 Maven是我们在开发java程序 ...
- HMS Core上新啦!
HMS Core上新啦!分析服务营销分析报告全新上线:运动健康服务支持目标场景事件订阅:音频编辑服务提供专业的三维声音频编辑与渲染能力,更多HMS Core能力可点击网页链接了解. 了解更多详情> ...
- Vue3 + TypeScript 开发指南
0x00 概述 阅读以下内容需要具备一定的 Vue2 基础 代码采用规范为:TypeScript + 组合式 API + setup 语法糖 (1)Vue3 简介 Vue3 第一个正式版发布于 202 ...