1、為什么要用狀態(tài)管理庫?
多組件的狀態(tài)共享問題: 當(dāng)多個(gè)組件需要訪問和修改相同的數(shù)據(jù)時(shí),我們需要在組件之間傳遞 props或者使用事件總線。當(dāng),應(yīng)用就會(huì)變得難以維護(hù)和調(diào)試。
多組件狀態(tài)同步問題: 當(dāng)一個(gè)組件修改了狀態(tài),其他組件可能無法立即得知該變化。
狀態(tài)變更的追蹤問題: 無法追蹤到狀態(tài)的變化是由何處引起的,使得調(diào)試和維護(hù)變得困難。
2、Vuex
2.1、核心概念
2.1.1、State:用于存儲(chǔ)應(yīng)用程序的狀態(tài)數(shù)據(jù)
當(dāng)你需要在多個(gè)組件之間共享數(shù)據(jù)時(shí),可以將這些數(shù)據(jù)放入state
中。
例如,保存用戶登錄狀態(tài)、購(gòu)物車中的商品列表等。
可以通過在組件中使用store.state.xxx
或計(jì)算屬性來獲取狀態(tài)數(shù)據(jù)。
2.1.2、Mutation:用于修改狀態(tài)的方法,必須是同步函數(shù)。
什么時(shí)候用? 當(dāng)你需要修改狀態(tài)時(shí)。讓所有的狀態(tài)變更都經(jīng)過 mutation
可以保證狀態(tài)的變更是可追蹤的。
通常,一個(gè) mutation
對(duì)應(yīng)一個(gè)狀態(tài)變更操作。例如,修改用戶登錄狀態(tài)、添加商品到購(gòu)物車等。
2.1.3、Action:用于處理異步邏輯或提交多個(gè) mutation。
什么時(shí)候用? 當(dāng)你需要處理異步操作(例如發(fā)起網(wǎng)絡(luò)請(qǐng)求)或需要在一個(gè) action
中提交多個(gè) mutation
時(shí)。
Action
可以包含任意的異步操作,并可以通過提交 mutation
來修改狀態(tài)。
例如,獲取用戶信息的異步請(qǐng)求、添加多個(gè)商品到購(gòu)物車等。
2.1.4、Getter:用于從狀態(tài)中獲取派生數(shù)據(jù)的方法。
什么時(shí)候用? 當(dāng)你需要根據(jù)狀態(tài)state.xxx
計(jì)算出一些數(shù)據(jù)時(shí)。
Getter 可以將一些復(fù)雜的數(shù)據(jù)計(jì)算邏輯封裝起來,并在組件中使用 store.getters
來獲取計(jì)算后的數(shù)據(jù)。
例如,基于購(gòu)物車商品列表計(jì)算購(gòu)物車總價(jià)、根據(jù)用戶權(quán)限判斷是否具有管理員角色等。
2.2、原理(v4.0.2)
2.2.1、vuex如何掛載到vue實(shí)例的
install (app, injectKey) {
// 使用`vue.provide()`將`vuex`提供給整個(gè)應(yīng)用
app.provide(injectKey || storeKey, this)
// 將vuex實(shí)例賦值給vue.$store;
// 在項(xiàng)目的非setup中可以使用this.$store.state.xxx取值就是這樣來的
app.config.globalProperties.$store = this
}
2.2.2、useStore
源碼
import { inject } from 'vue'
export const storeKey = 'store'
export function useStore (key = null) {
return inject(key !== null ? key : storeKey)
}
commit
源碼
commit (_type, _payload, _options) {
const { type, payload, options } = unifyObjectStyle(_type, _payload, _options)
const mutation = { type, payload }
// 查找該類型對(duì)應(yīng)的 mutations
const entry = this._mutations[type]
if (!entry) {
return
}
// 執(zhí)行mutations對(duì)應(yīng)的處理函數(shù)
this._withCommit(() => {
entry.forEach(function commitIterator (handler) {
handler(payload)
})
})
// 通知訂閱者
this._subscribers
.slice() // shallow copy to prevent iterator invalidation if subscriber synchronously calls unsubscribe
.forEach(sub => sub(mutation, this.state))
}
3、Pinia
相比vuex的優(yōu)勢(shì):
- 可通過devtools追蹤數(shù)據(jù)變化,無需通過
commit
提交Mutation
- 支持TS,提供代碼自動(dòng)補(bǔ)全,源碼為TS編寫;vuex是用JS編寫的,vuex要支持TS需要安裝插件
- pinia更輕,大小只有 1kb 左右
改變狀態(tài)的方法
- 直接修改變量
- 調(diào)用action
- 調(diào)用patch
3.1原理
install 原理與vuex一致文章來源:http://www.zghlxwxcb.cn/news/detail-805212.html
let toBeInstalled: PiniaPlugin[] = []
install(app: App) {
if (!isVue2) {
pinia._a = app
// 暴露pinia,組件通過inject注入pinia實(shí)例
app.provide(piniaSymbol, pinia)
// 模版中可通過$pinia訪問
app.config.globalProperties.$pinia = pinia
// 將pinia的plugin 存到插件列表
toBeInstalled.forEach((plugin) => _p.push(plugin))
toBeInstalled = []
}
},
pinia的plugin實(shí)現(xiàn)原理
1、在調(diào)用vue.use(pinia)
之前注入插件的情況,會(huì)將plugin存放到toBeInstalled列表,
2、調(diào)用vue.use(pinia)
之后,會(huì)將toBeInstalled的插件存到pinia實(shí)例的_p中
3、調(diào)用useStore時(shí)將plugin注入每個(gè)Store實(shí)例文章來源地址http://www.zghlxwxcb.cn/news/detail-805212.html
pinia.use(plugin) {
if (!this._a && !isVue2) {
toBeInstalled.push(plugin)
} else {
_p.push(plugin)
}
return this
}
useStore(pinia) {
if (!pinia._s.has(id)) {
// creating the store registers it in `pinia._s`
if (isSetupStore) {
createSetupStore(id, setup, options, pinia)
} else {
createOptionsStore(id, options as any, pinia)
}
/* istanbul ignore else */
if (__DEV__) {
// @ts-expect-error: not the right inferred type
useStore._pinia = pinia
}
}
}
createSetupStore () {
pinia._p.forEach((extender) => {
assign(
store,
scope.run(() =>
extender({
store: store as Store,
app: pinia._a,
pinia,
options: optionsForPlugin,
})
)!
)
})
}
到了這里,關(guān)于vue:狀態(tài)管理庫及其部分原理(Vuex、Pinia)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!