寫(xiě)在前面:本文參考小滿(mǎn)大牛的pinia專(zhuān)欄
一、Vuex與Pinia
Vuex 和 Pinia 均是 Vue.js 的狀態(tài)管理庫(kù),它們?yōu)?Vue 應(yīng)用程序提供了一種集中式的、可預(yù)測(cè)的狀態(tài)管理解決方案。
Vuex 是 Vue.js 官方推薦的狀態(tài)管理庫(kù)之一。它的核心概念包括 state、mutation、action 和 getter。其中,state 代表應(yīng)用程序的狀態(tài)數(shù)據(jù),在 Vuex 中存儲(chǔ)為唯一的來(lái)源,mutation 用于修改狀態(tài)數(shù)據(jù)并確保數(shù)據(jù)變化的可追蹤性,action 用于處理異步操作或組合多個(gè) mutation 操作,getter 可以讓我們對(duì) state 進(jìn)行計(jì)算和派生,并使其變得更加易于訪(fǎng)問(wèn)。一個(gè) Vuex store 實(shí)例是一個(gè)全局 JavaScript 對(duì)象,可以在所有組件中通過(guò)注入來(lái)進(jìn)行訪(fǎng)問(wèn)和操作。
Pinia 是一個(gè)新的狀態(tài)管理庫(kù),也是專(zhuān)門(mén)為 Vue 3 開(kāi)發(fā)的。它提供了一個(gè)類(lèi)似于 Vuex 的狀態(tài)管理模式,但采用最新的 Vue 3 API 構(gòu)建。相比 Vuex,Pinia 更簡(jiǎn)單、更輕量,更加靈活,并支持 TypeScript 類(lèi)型檢查。
與 Vuex 相比,Pinia 沒(méi)有嚴(yán)格的命名約定,可以自由拆分邏輯、支持獨(dú)立實(shí)例、執(zhí)行更輕量級(jí)的代碼等。但不同于 Vuex,它并沒(méi)有內(nèi)置支持模塊化和嚴(yán)格調(diào)試工具。
以下是Pinia的特點(diǎn):
- 完整的 ts 的支持;
- 足夠輕量,壓縮后的體積只有1kb左右;
- 去除 mutations,只有 state,getters,actions;
- actions 支持同步和異步;
- 代碼扁平化沒(méi)有模塊嵌套,只有 store 的概念,store 之間可以自由使用,每一個(gè)store都是獨(dú)立的
- 無(wú)需手動(dòng)添加 store,store 一旦創(chuàng)建便會(huì)自動(dòng)添加;
- 支持Vue3 和 Vue2
Pinia官方文檔
二、安裝、引入Pinia
安裝:npm install pinia
或 yarn add pinia
引入注冊(cè):
Vue3的引入方法main.ts
import { createApp } from 'vue'
import App from './App.vue'
// Vue3的引入方法
import {createPinia} from 'pinia'
const store = createPinia()
let app = createApp(App)
app.use(store)
app.mount('#app')
Vue2的引入方法main.ts
import { createPinia, PiniaVuePlugin } from 'pinia'
Vue.use(PiniaVuePlugin)
const pinia = createPinia()
new Vue({
el: '#app',
pinia,
})
三、初始化創(chuàng)建Store
新建文件夾index.ts
用于管理倉(cāng)庫(kù),store-name.ts
用于存儲(chǔ)所有枚舉的倉(cāng)庫(kù)名。store-name.ts
// 枚舉所有倉(cāng)庫(kù)名并暴露
export const enum Names {
TEST = 'TEST'
}
index.ts
// 導(dǎo)入定義倉(cāng)庫(kù)的方法
import { defineStore } from "pinia";
// 導(dǎo)入枚舉的所有倉(cāng)庫(kù)名
import { Names } from "./store-name";
// defineStore()定義一個(gè)倉(cāng)庫(kù),第一個(gè)參數(shù)作為名稱(chēng),也可看作是id
// 這個(gè)id(名稱(chēng))是必要的,Pinia使用它來(lái)講store連接DevTools,可以通過(guò)調(diào)試工具查看
export const useTestStore = defineStore(Names.TEST, {
state: () => {
return {
// 定義初始化的值
current: 1,
name: '小5'
}
},
// 類(lèi)似于computed可以幫我們修飾我們的值
getters: {
},
// 可以操作異步 和 同步 提交state
actions: {
}
})
簡(jiǎn)單的使用倉(cāng)庫(kù)的數(shù)據(jù)App.vue
<template>
<div>pinia-current:{{ Test.current }}</div>
<div>pinia-name:{{ Test.name }}</div>
</template>
<script setup lang="ts">
import { useTestStore } from "./store";
const Test = useTestStore();
</script>
<style lang="scss" scoped></style>
結(jié)果展示:
四、修改State值的五種方式
1.直接修改
State是允許不在倉(cāng)庫(kù)中直接修改值的,這與Vuex不同,Vuex要通過(guò)commit
或dispatch
方法調(diào)用倉(cāng)庫(kù)修改。
<template>
<div>pinia-current:{{ Test.current }}</div>
<div>pinia-name:{{ Test.name }}</div>
<div style="display: flex; flex-direction: column">
<button @click="change">change</button>
</div>
</template>
<script setup lang="ts">
import { useTestStore } from "./store";
const Test = useTestStore();
const change = () => {
Test.current++;
};
</script>
<style lang="scss" scoped></style>
2.批量修改
在倉(cāng)庫(kù)的實(shí)例上有$patch方法可以批量修改多個(gè)值
<template>
<div>pinia-current:{{ Test.current }}</div>
<div>pinia-name:{{ Test.name }}</div>
<div style="display: flex; flex-direction: column">
<button @click="bulkChange">批量change</button>
</div>
</template>
<script setup lang="ts">
import { useTestStore } from "./store";
const Test = useTestStore();
const bulkChange = () => {
Test.$patch({
current: 888,
name: "小4",
});
};
</script>
<style lang="scss" scoped></style>
3.批量修改工廠函數(shù)形式
推薦使用函數(shù)形式 可以自定義修改邏輯
<template>
<div>pinia-current:{{ Test.current }}</div>
<div>pinia-name:{{ Test.name }}</div>
<div style="display: flex; flex-direction: column">
<button @click="funChange">工廠函數(shù)實(shí)現(xiàn)批量change</button>
</template>
<script setup lang="ts">
import { useTestStore } from "./store";
const Test = useTestStore();
const funChange = () => {
Test.$patch((state) => {
(state.current = 999), (state.name = "小3");
});
};
</script>
<style lang="scss" scoped></style>
4.通過(guò)原始對(duì)象修改整個(gè)實(shí)例
$state您可以通過(guò)將store的屬性設(shè)置為新對(duì)象來(lái)替換store的整個(gè)狀態(tài)
缺點(diǎn)就是必須修改整個(gè)對(duì)象的所有屬性,可以用結(jié)構(gòu)賦值的方式解決這個(gè)缺點(diǎn)。
<template>
<div>pinia-current:{{ Test.current }}</div>
<div>pinia-name:{{ Test.name }}</div>
<div style="display: flex; flex-direction: column">
<button @click="allChange">必須修改全部的寫(xiě)法(不推薦寫(xiě)法)</button>
</div>
</template>
<script setup lang="ts">
import { useTestStore } from "./store";
const Test = useTestStore();
const allChange = () => {
Test.$state = {
...Test.$state,
name: "小2",
};
};
</script>
<style lang="scss" scoped></style>
5.通過(guò)actions修改
在倉(cāng)庫(kù)中定義Actions
在倉(cāng)庫(kù)的actions 中直接使用this就可以指到state里面的值store/index.ts
import { defineStore } from "pinia";
import { Names } from "./store-name";
export const useTestStore = defineStore(Names.TEST, {
state: () => {
return {
current: 1,
name: '小5'
}
},
getters: {
},
// 可以操作異步 和 同步 提交state
actions: {
// 不能寫(xiě)箭頭函數(shù) 否則this會(huì)指向錯(cuò)誤
setCurrent(num:number) {
this.current = num
}
}
})
直接在App.vue
實(shí)例中調(diào)用
<template>
<div>pinia-current:{{ Test.current }}</div>
<div>pinia-name:{{ Test.name }}</div>
<div style="display: flex; flex-direction: column">
<button @click="actionsChange">使用actions修改</button>
</div>
</template>
<script setup lang="ts">
import { useTestStore } from "./store";
const Test = useTestStore();
const actionsChange = () => {
Test.setCurrent(555);
};
</script>
<style lang="scss" scoped></style>
6.結(jié)果展示
App.vue
<template>
<div>pinia-current:{{ Test.current }}</div>
<div>pinia-name:{{ Test.name }}</div>
<div style="display: flex; flex-direction: column">
<button @click="change">change</button>
<button @click="bulkChange">批量change</button>
<button @click="funChange">工廠函數(shù)實(shí)現(xiàn)批量change</button>
<button @click="allChange">必須修改全部的寫(xiě)法(不推薦寫(xiě)法)</button>
<button @click="actionsChange">使用actions修改</button>
</div>
</template>
<script setup lang="ts">
import { useTestStore } from "./store";
const Test = useTestStore();
const change = () => {
Test.current++;
};
const bulkChange = () => {
Test.$patch({
current: 888,
name: "小4",
});
};
const funChange = () => {
Test.$patch((state) => {
(state.current = 999), (state.name = "小3");
});
};
const allChange = () => {
Test.$state = {
...Test.$state,
name: "小2",
};
};
const actionsChange = () => {
Test.setCurrent(555);
};
</script>
<style lang="scss" scoped></style>
結(jié)果展示:
五、解構(gòu)store
在Pinia是不允許直接解構(gòu)state的數(shù)據(jù)的,數(shù)據(jù)會(huì)失去響應(yīng)式。
const Test = useTestStore()
// 直接解構(gòu)失去響應(yīng)式
const { current, name } = Test
差異對(duì)比:
<template>
<div>響應(yīng)式的值:{{ Test.current }}</div>
<div>解構(gòu)出的非響應(yīng)式值:{{ current }}--{{ name }}</div>
<button @click="change">change</button>
</template>
<script setup lang="ts">
import { useTestStore } from "./store";
const Test = useTestStore();
const { current, name } = Test;
const change = () => {
Test.current++;
};
</script>
<style lang="scss" scoped></style>
結(jié)果展示:
解決方案:
使用pinia的storeToRefs()
方式將數(shù)據(jù)響應(yīng)式化。如下:
// 引入
import { storeToRefs } from "pinia";
import { useTestStore } from "./store";
const Test = useTestStore();
// 響應(yīng)式化
const { current, name } = storeToRefs(Test);
const change = () => {
Test.current++;
};
結(jié)果展示:
六、actions和getters
store/index.ts
import { defineStore } from "pinia";
import { Names } from "./store-name";
type User = {
name: string,
age: number
}
let result:User = {
name: '小5',
age: 18
}
const Login = (): Promise<User> => {
return new Promise((resolve) => {
setTimeout(() => {
resolve({
name: "小4",
age: 17
})
})
})
}
export const useTestStore = defineStore(Names.TEST, {
state: () => {
return {
// 類(lèi)型斷言
// <type>{} 通常用于類(lèi)型斷言,表示將某個(gè)值強(qiáng)制轉(zhuǎn)換成指定的類(lèi)型。
user: <User>{},
name: 'xiao5'
}
},
// 類(lèi)似于computed可以幫我們修飾我們的值
getters: {
newName(): string {
return `$-${this.name}`
}
},
// 可以操作異步 和 同步
actions: {
// 不能寫(xiě)箭頭函數(shù) 否則this會(huì)指向錯(cuò)誤
// 同步
setUser1() {
this.user = result
this.name = '小8'
},
// 異步
async setUser2() {
const result = await Login()
this.user = result
this.name = '小7'
},
}
})
App.vue
<template>
<div>actions:{{ Test.user }}</div>
<button @click="change1">同步</button>
<button @click="change2">異步</button>
<div>getters:{{ Test.newName }}</div>
</template>
<script setup lang="ts">
import { useTestStore } from "./store";
const Test = useTestStore();
const change1 = () => {
Test.setUser1();
};
const change2 = () => {
Test.setUser2();
};
</script>
<style lang="scss" scoped></style>
結(jié)果展示:
另外,多個(gè)actions可以相互調(diào)用,多個(gè)getters也可以相互調(diào)用。
七、API
1.$reset
重置store到它的初識(shí)狀態(tài)
import { useTestStore } from "./store";
const Test = useTestStore();
const reset = () => {
Test.$reset();
};
2.$subscribe
用于訂閱state
的改變,只要有state的變化就會(huì)觸發(fā)這個(gè)函數(shù)
$subscribe的第一個(gè)參數(shù)是個(gè)回調(diào)函數(shù)
Test.$subscribe((args, state) => {
console.log("======>", args);
console.log("======>", state);
});
返回值args
主要包括effect
、target
、storeId
等信息,state
是state數(shù)據(jù)變化后的狀態(tài)。如下圖
第二個(gè)參數(shù)是是一個(gè)配置對(duì)象
Test.$subscribe(
(args, state) => {
console.log("======>", args);
console.log("======>", state);
},
{
detached: true,
deep: true,
flush: "post",
}
);
這三種配置詳細(xì)如下:
detached(脫離狀態(tài)):指組件從其父級(jí)組件或 DOM 樹(shù)中被移除的狀態(tài)。在這種狀態(tài)下,組件不再接收更新,并且可以被銷(xiāo)毀。Vue 2.x 中通過(guò)調(diào)用 $destroy() 方法來(lái)銷(xiāo)毀組件,Vue 3.x 中則使用 teleport、keepAlive 等組合來(lái)控制組件的生命周期。
deep(深度監(jiān)聽(tīng)):指對(duì)一個(gè)對(duì)象進(jìn)行深度監(jiān)聽(tīng),在該對(duì)象的所有屬性的值發(fā)生改變時(shí),都能夠得到通知。在 Vue.js 中,可以使用 vm.$watch() 方法來(lái)實(shí)現(xiàn)對(duì)數(shù)據(jù)的深度監(jiān)聽(tīng),Vue 3.x 中也提供了相應(yīng)的 API 實(shí)現(xiàn)深度監(jiān)聽(tīng)。
flush(刷新策略):指一種更新數(shù)據(jù)后如何刷新頁(yè)面的策略。在 Vue.js 中,默認(rèn)的刷新策略是異步批處理模式(nextTick 模式),即將所有數(shù)據(jù)的更新操作放入一個(gè)隊(duì)列中,在下一個(gè) tick 執(zhí)行更新操作,以減少不必要的 DOM 操作和提高性能。除此之外,Vue.js 還支持同步刷新(sync)、立即刷新(pre)等刷新策略。
3.$onAction
用于訂閱actions的調(diào)用,只要有actions被調(diào)用就會(huì)觸發(fā)這個(gè)函數(shù)。
Test.$onAction((args) => {
console.log(args);
});
返回值args
主要包括after
回調(diào),args
(actions傳遞的參數(shù)),name
(觸發(fā)的actions名字),onError
(錯(cuò)誤回調(diào)),store
(store實(shí)例)等。如下圖
八、pinia插件
pinia 和 vuex 都有一個(gè)通病 頁(yè)面刷新?tīng)顟B(tài)會(huì)丟失文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-466063.html
我們可以寫(xiě)一個(gè)pinia 插件緩存他的值
參考博客:滿(mǎn)哥牛文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-466063.html
到了這里,關(guān)于Vue——狀態(tài)管理庫(kù)Pinia的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!