其他章節(jié)請看:文章來源:http://www.zghlxwxcb.cn/news/detail-859117.html
vue3 快速入門 系列文章來源地址http://www.zghlxwxcb.cn/news/detail-859117.html
pinia
vue3 狀態(tài)管理這里選擇 pinia。
雖然 vuex4 已支持 Vue 3 的 Composition API,但是 vue3 官網(wǎng)推薦新的應(yīng)用使用 pinia —— vue3 pinia
集中式狀態(tài)管理
redux、mobx、vuex、pinia都是集中式狀態(tài)管理工具。與之對應(yīng)的就是分布式。
Pinia 符合直覺
的 Vue.js 狀態(tài)管理庫 甚至讓你忘記正在使用的是一個(gè)狀態(tài)庫 —— 官網(wǎng)
安裝 pinia 環(huán)境
首先下載安裝包:
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 中依次:引入、創(chuàng)建和安裝 pinia,在瀏覽器 vue 開發(fā)者工具中就能看到 pinia(一個(gè)菠蘿圖標(biāo))。
import {createApp} from 'vue'
import App from './App.vue'
import router from './router'
// 引入
import { createPinia } from 'pinia'
const app = createApp(App)
// 創(chuàng)建
const pinia = createPinia()
app.use(router)
// 安裝:就像安裝 vue-router 一樣使用
app.use(pinia)
app.mount('#app')
有時(shí)這個(gè)菠蘿
沒出現(xiàn),可以關(guān)閉瀏覽器或重啟服務(wù)。
Tip: 詳細(xì)請看 pinia 安裝官網(wǎng)
第一個(gè)示例
vuex 的核心概念有 State、Getters、Mutations、Actions和Modules。其中 State 是數(shù)據(jù),我們不能直接修改數(shù)據(jù)。
pinia 比 vuex 更輕量,更易使用。比如拿到數(shù)據(jù)后就能直接改,符合直覺
。
請看示例:
pinia 的數(shù)據(jù)從項(xiàng)目目錄上說,會放在 store 文件夾中。
通常我們會對狀態(tài)進(jìn)行分類,比如用戶相關(guān)的數(shù)據(jù)放在 store/user.ts 中:
// src/store/user.ts
import { defineStore } from 'pinia'
// 你可以任意命名 `defineStore()` 的返回值,但最好使用 store 的名字,同時(shí)以 `use` 開頭且以 `Store` 結(jié)尾。
// (比如 `useUserStore`,`useCartStore`,`useProductStore`)
// 第一個(gè)參數(shù)是你的應(yīng)用中 Store 的唯一 ID。 Pinia 將用它來連接 store 和 devtools
export const useUserStore = defineStore('user', {
// actions 里面放一個(gè)一個(gè)的方法,用于響應(yīng)組件中的動作
actions: {
changeNameAndAge() {
// this Proxy(Object)
// 里面有 $state。在 vue2 中有 $watch、$on等以$開頭的都是給程序員用的實(shí)例方法
console.log('this', this);
// 沒必要通過 $state,直接訪問即可
this.name += '~'
this.$state.age += 1
}
},
state: () => {
return {
name: 'peng',
age: 18,
}
},
})
通過 defineStore 定義一個(gè) store,第一個(gè)參數(shù)是 store 的id,命名建議規(guī)范,例如使用文件名,導(dǎo)出方式這里選擇分別導(dǎo)出,導(dǎo)出的名字使用 use+user+store。
state 是一個(gè)函數(shù),返回的就是數(shù)據(jù)
actions 中是一個(gè)一個(gè)的方法,但不需要像 vuex 中需用 dispatch 觸發(fā)。
接著在需要使用狀態(tài)的地方使用。讀取狀態(tài)的方式有2種,修改狀態(tài)的方式有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 報(bào)錯(cuò):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() {
// 修改數(shù)據(jù)方式1:直接操作數(shù)據(jù)
// vue2中的vuex必須通過mutation更新數(shù)據(jù),不能直接修改數(shù)據(jù)
userStore.name += '~'
}
function changeNameAndAge() {
userStore.$state.name += '~'
userStore.$state.age += 1
}
function changeNameAndAge2() {
// $patch 用于批量修改
// patch 中文“碎片”,比如age 就是 pinia 中一個(gè)數(shù)據(jù)碎片
userStore.$patch({
name: userStore.name + '~',
age: userStore.age + 1
})
}
function changeNameAndAge3() {
// 調(diào)用 actions
userStore.changeNameAndAge()
}
</script>
Tip: changeNameAndAge 會觸發(fā)兩次修改,而 changeNameAndAge2 使用 $patch 會進(jìn)行批量修改,從開發(fā)者時(shí)間線中看到,只執(zhí)行一次。如果很多數(shù)據(jù)同時(shí)修改,推薦使用 patch。
優(yōu)雅的讀取數(shù)據(jù)
前面我們是這么讀取 store 中數(shù)據(jù):
<p>{{ userStore.name }}</p>
const userStore = useUserStore()
如果需要讀取的數(shù)據(jù)太多,在模板中就會出現(xiàn)很多 userStore,于是我們想到用 toRefs 解構(gòu)解決。就像這樣:
<p>優(yōu)雅的讀:{{ name }}</p>
import {toRefs} from 'vue'
const userStore = useUserStore()
const {name} = toRefs(userStore)
但是 toRefs(userStore) 太重,通過console.log(toRefs(userStore))
你會發(fā)現(xiàn)toRefs將 store 所有屬性(包括方法)都轉(zhuǎn)成 ref,其實(shí)我們只需要將數(shù)據(jù)轉(zhuǎn)成 ref 即可。
pinia 也想到了這個(gè)問題,于是可以用 storeToRefs 替代。就像這樣:
<p>優(yōu)雅的讀:{{ 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: {
// 推薦使用箭頭函數(shù),參數(shù)會傳入 state
bigName: (state) => state.name.toLocaleUpperCase(),
// 如果需要訪問其他 getters 屬性,可以通過非箭頭函數(shù),通過 this 訪問
lowerName2(): string{
return this.bigName.toLocaleLowerCase()
}
},
數(shù)據(jù)取得的方式和 state 相同:
<p>優(yōu)雅的讀:{{ name }}</p>
<p>bigName:{{ bigName }}</p>
<p>lowerName2:{{ lowerName2 }}</p>
const userStore = useUserStore()
const {name, bigName, lowerName2} = storeToRefs(userStore)
Tip:詳細(xì)請看 pinia getters
訂閱
類似于 Vuex 的 subscribe 方法,你可以通過 store 的 $subscribe() 方法偵聽 state 及其變化
只要 userStore 中的數(shù)據(jù)變化了,函數(shù)就會被調(diào)用,我們通常關(guān)心第二個(gè)參數(shù):
// 只要 userStore 數(shù)據(jù)變化,這個(gè)
userStore.$subscribe((mutation, state) => {
// {storeId: 'user', type: 'direct', events: {…}}
console.log('mutation: ', mutation);
// Proxy(Object)?{name: 'Peng~', age: 19}
console.log('state: ', state);
// 每當(dāng)狀態(tài)發(fā)生變化時(shí),將整個(gè) state 持久化到本地存儲。
localStorage.setItem('userStore', JSON.stringify(state))
})
我們可以將 state 存儲到本地,這樣就可以實(shí)現(xiàn)頁面刷新,狀態(tài)不丟失。
Tip: 細(xì)節(jié)請看 訂閱 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', () => {
// 數(shù)據(jù)用 ref 或 reactive 定義
const name = ref('Peng')
const age = ref(18)
// getters 用計(jì)算屬性
const bigName = computed(() => name.value.toLocaleUpperCase())
const lowerName2 = computed(() => bigName.value.toLocaleLowerCase())
// actions 用方法定義
function changeNameAndAge() {
name.value += '~';
age.value += 1;
}
// 最后必須暴露出去
return {
// vscode 中數(shù)據(jù)一個(gè)顏色、方法另一個(gè)顏色
name,
age,
bigName,
lowerName2,
changeNameAndAge,
};
});
Tip:組合式寫法更靈活(請看 組合式 Store),層級少,但必須返回。具體如何選擇自行決定。
擴(kuò)展
ref 數(shù)據(jù)要不要 .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)
讀取響應(yīng)式對象中的 ref 不需要 .value
其他章節(jié)請看:
vue3 快速入門 系列
到了這里,關(guān)于vue3 快速入門系列 —— 狀態(tài)管理 pinia的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!