版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
教學(xué)目標(biāo)掌握Vuex的工作原理。掌握Vuex五個(gè)核心概念。掌握Vuex中mutation和action的定義與使用方法。熟悉多模塊的應(yīng)用場(chǎng)景。學(xué)會(huì)在Vue3.x中引入相關(guān)VuexAPI、使用store對(duì)象。1第9章-狀態(tài)管理模式Vuex(4學(xué)時(shí))29.1Vuex概述
1.狀態(tài)管理模式Vuex是一個(gè)專為Vue.js應(yīng)用程序開(kāi)發(fā)的狀態(tài)管理模式。它采用集中式存儲(chǔ)管理應(yīng)用的所有組件的State狀態(tài),并以相應(yīng)的規(guī)則保證狀態(tài)以一種可預(yù)測(cè)的方式發(fā)生變化。
Vuex也集成到Vue的官方調(diào)試工具devtoolsextension,提供了諸如零配置的time-travel調(diào)試、狀態(tài)快照導(dǎo)入導(dǎo)出等高級(jí)調(diào)試功能。
狀態(tài)自管理應(yīng)用通常包含state、view和actions三個(gè)部分,如圖所示。它們作用分別如下:state(狀態(tài)):驅(qū)動(dòng)應(yīng)用的數(shù)據(jù)源。數(shù)據(jù)源就是組件里面的data。view(視圖):以聲明方式將state映射到視圖。{{count}}即為聲明方式,數(shù)據(jù)就可以顯示出來(lái)。actions(行為):響應(yīng)在view上的用戶輸入導(dǎo)致的狀態(tài)變化。actions其實(shí)就是多個(gè)函數(shù)。9.1.1Vuex定義【例9-1】簡(jiǎn)易投票的單向數(shù)據(jù)流應(yīng)用實(shí)戰(zhàn)。39.1.1Vuex定義
第16~22行定義template屬性,使用反單引號(hào)定義div標(biāo)記,該標(biāo)記包含文本插值和一個(gè)按鈕,用于觸發(fā)遞增行為來(lái)改變count的值,并顯示在視圖上。
第24行定義methods屬性,其中定義vote()方法實(shí)現(xiàn)tickets增1。view初始讀取data中的tickets,顯示為0。通過(guò)事件觸發(fā)調(diào)用actions里面的vote()方法,然后actions去更新state的狀態(tài)數(shù)據(jù)。state更新之后,view的界面也會(huì)隨之改變。42.Vuex數(shù)據(jù)流向及適用場(chǎng)景
在實(shí)際工程應(yīng)用中經(jīng)常會(huì)遇到多組件間共享狀態(tài),此時(shí)單向數(shù)據(jù)流的簡(jiǎn)潔性很容易被破壞。主要來(lái)源于兩種應(yīng)用場(chǎng)合:使用數(shù)據(jù):多個(gè)視圖依賴于同一狀態(tài)。
在多層嵌套的組件中采用參數(shù)傳遞的方法將會(huì)非常繁瑣,尤其是兄弟組件間的狀態(tài)傳遞更顯得無(wú)能為力。更新數(shù)據(jù):來(lái)自不同視圖的行為需要變更同一狀態(tài)。9.1.1Vuex定義
經(jīng)常會(huì)采用父子組件直接引用或者通過(guò)事件來(lái)變更和同步狀態(tài)的多份拷貝。以上的這些模式非常脆弱,通常會(huì)導(dǎo)致代碼無(wú)法維護(hù)。
如果不打算開(kāi)發(fā)大型單頁(yè)應(yīng)用,就沒(méi)有必要使用Vuex。一個(gè)簡(jiǎn)單的store模式就足夠滿足需求了。但是,如果需要構(gòu)建一個(gè)中大型單頁(yè)應(yīng)用,此時(shí)很可能會(huì)考慮如何更好地在組件外部管理狀態(tài),Vuex自然而然將會(huì)成為最好的選擇。5
若有一個(gè)狀態(tài)需要被多個(gè)實(shí)例共享,可以簡(jiǎn)單地通過(guò)維護(hù)一份數(shù)據(jù)來(lái)實(shí)現(xiàn)共享,這就是store模式。
【例9-2】store模式應(yīng)用實(shí)戰(zhàn)--共享狀態(tài)。設(shè)計(jì)要求:設(shè)置兩個(gè)組件分別共享store中state狀態(tài),同時(shí)管理自身的私有數(shù)據(jù),通過(guò)全局聲明一個(gè)store對(duì)象變量,封裝一個(gè)state屬性(定義為reactive()響應(yīng)式對(duì)象)和setMessageAction(newValue)、clearMessageAction()等兩個(gè)方法。再定義兩個(gè)組件,分別為App1、App2,并給每個(gè)實(shí)例分別定義data、methods等屬性。data屬性中分別定義私有數(shù)據(jù)屬性privateState和共享數(shù)據(jù)屬性sharedState,并將sharedState屬性的值設(shè)置為store.state。9.1.2簡(jiǎn)單狀態(tài)管理-store模式69.1.2簡(jiǎn)單狀態(tài)管理-store模式
代碼中第27~40行定義1個(gè)對(duì)象變量store,并給其定義一個(gè)state屬性(在其中定義message屬性,作為狀態(tài)數(shù)據(jù))和setMessageAction(newValue)(功能為改變狀態(tài))、clearMessageAction()(功能為清空狀態(tài))等兩個(gè)方法。
兩個(gè)Vue實(shí)例中data屬性中均定義了privateState(私有數(shù)據(jù))、sharedState(共享數(shù)據(jù))。7
store模式應(yīng)用-單擊“改變共享信息”按鈕頁(yè)面store模式應(yīng)用-單擊“清空享信息”按鈕頁(yè)面9.1.2簡(jiǎn)單狀態(tài)管理-store模式注意:所有store中state的改變,都放置在store自身的action中去管理。這種集中式狀態(tài)管理能夠被更容易地理解哪種類型的mutation將會(huì)發(fā)生,以及它們是如何被觸發(fā)。當(dāng)出現(xiàn)錯(cuò)誤時(shí),現(xiàn)在也會(huì)有一個(gè)log記錄bug之前發(fā)生了什么。此外,每個(gè)實(shí)例/組件仍然可以擁有和管理自己的私有狀態(tài)(數(shù)據(jù))。89.2Vuex基本使用
在項(xiàng)目中需要使用Vuex時(shí),必須將vue.js和vuex.js引用到項(xiàng)目中??梢酝ㄟ^(guò)CDN或者script腳本引用(
/vuex@4.1.0/dist/vuex.global.js),然后在項(xiàng)目中安裝vuex模塊,并在項(xiàng)目主文件中導(dǎo)入vue和vuex,再顯式地使用app.use(store)即可。具體的操作步驟如下:1.直接下載/CDN引用。HTML中使用script標(biāo)簽引入。<scriptsrc="vue.global.js"></script><scriptsrc="vuex.global.js"></script>CDN引用。<scriptsrc="/vuex@4.1.0/dist/vuex.global.js"></script>
2.項(xiàng)目目錄下載安裝模塊。npminstallvuex--save-dev|-D
3.導(dǎo)入并顯式地使用Vuex。//index.jsimportVuefrom'vue'importVuexfrom'vuex'Vue.use(Vuex)//Vue2.x中顯式地通過(guò)
Vue.use()
來(lái)使用Vuex//在Vue3.x中使用方式import{createApp}from'vue'importAppfrom'./App.vue'importstorefrom'./store'Vue.createApp(App).use(store).mount('#app')99.2Vuex基本使用4.Vue3.x中的Vuex
在Vue3.x中使用Vuex的版本是v4.0.2以上,在src/store/index.js文件中需要導(dǎo)入createStoreAPI,然后定義state、mutations、actions、modules等核心屬性。部分參考代碼如下:import
{
createStore
}
from
'vuex'
export
default
createStore({
state:
{
user:
{name:
'李小明',sex:
'男'},
title:
'Vuex狀態(tài)管理'
},
mutations:
{
setUserName(state,
value)
{
=
value
}
},
actions:
{},
modules:
{}
})
在Vue3.x中,組件內(nèi)使用Vuex,setup函數(shù)中使用有所不同。部分參考代碼如下:import
{
toRefs,reactive}
from
"vue";
import
{
useStore}
from
"vuex";
export
default
{
setup()
{
const
state
=
reactive({
name:
'',
title:''
})
const
store
=
useStore()
=
return
{
...toRefs(state)
...toRefs(store.state.user)
}
}
};
在Vue3.x中使用Vuex時(shí)需要顯式導(dǎo)入useStore,然后在setup函數(shù)內(nèi)通過(guò)useStore()來(lái)創(chuàng)建store(如第9行所示),并在其中定義相關(guān)方法去調(diào)用相關(guān)的mutations和actions,最后將store.state中的相關(guān)屬性通過(guò)toRefs()轉(zhuǎn)換為普通的響應(yīng)式數(shù)據(jù)(如第13行所示),供模板使用。109.3Vuex核心概念
Vuex應(yīng)用的核心就是store(倉(cāng)庫(kù))。store就是一個(gè)容器,它包含著用戶應(yīng)用中大部分的狀態(tài)(數(shù)據(jù)),如右圖所示。但Vuex和單純的全局對(duì)象是不同。
主要有兩點(diǎn)區(qū)別:
Vuex的狀態(tài)存儲(chǔ)是響應(yīng)式的。
當(dāng)Vue實(shí)例/組件從store中讀取狀態(tài)的時(shí)候,若store中的狀態(tài)發(fā)生變化,那么相應(yīng)的實(shí)例/組件也會(huì)相應(yīng)地得到高效更新。
用戶不能直接改變store中的狀態(tài)。
改變store中的狀態(tài)的唯一途徑就是顯式地提交mutation。
這樣使得可以方便地跟蹤每一個(gè)狀態(tài)的變化,從而可以通過(guò)一些工具幫助用戶更好地了解自己的應(yīng)用。119.3.1一個(gè)完整的store結(jié)構(gòu)
一個(gè)完整的store包含state、getters、mutations、actions、modules等五大組成部分。精簡(jiǎn)的代碼如下所示。//Vue3.x中定義方式import
{
createStore
}
from
'vuex'
export
default
createStore({
state:
{
//
存放狀態(tài)
},
getters:
{
//
state的計(jì)算屬性
},
mutations:
{
//
更改state中狀態(tài)的邏輯,同步操作
}
actions:
{
//
提交mutation,異步操作
}
//
如果將store分成一個(gè)個(gè)的模塊的話,則需要用到modules。
//
然后在每一個(gè)module中寫state,
getters,
mutations,
actions等。
modules:
{
a:
moduleA,
b:
moduleB,
//
...
})
需要使用exportdefaultcreateStore({})將store導(dǎo)出,在main.js文件可以導(dǎo)入,并掛載到Vue根實(shí)例中,其它子組件即可以使用store中的state狀態(tài)。129.3.2最簡(jiǎn)單的store
安裝Vuex之后,就可以來(lái)創(chuàng)建一個(gè)store。創(chuàng)建過(guò)程比較簡(jiǎn)單,僅需要提供一個(gè)初始state對(duì)象和一些mutation。部分代碼如下:import
{createStore}
from
'vuex'
exportdefaultcreateStore({
state:
{
count:
0
},
mutations:
{
increment
(state)
{
state.count++
}
}
})
接下來(lái)就可以通過(guò)store.state來(lái)獲取狀態(tài)對(duì)象,以及通過(guò)mit方法觸發(fā)狀態(tài)變更,并通過(guò)控制臺(tái)輸出狀態(tài)數(shù)據(jù)。代碼如下:mit('increment')//increment觸發(fā)mutationconsole.log(store.state.count)//count值為1注意:通過(guò)提交mutation的方式,而非直接改變store.state.count,是因?yàn)橄胍鞔_地追蹤到狀態(tài)的變化。這樣可使用戶的意圖更加明顯,在閱讀代碼的時(shí)候能更容易地解讀應(yīng)用內(nèi)部的狀態(tài)改變。由于store中的狀態(tài)是響應(yīng)式的,在組件中調(diào)用store中的狀態(tài)簡(jiǎn)單到僅需要在計(jì)算屬性中返回即可。觸發(fā)變化也僅僅是在組件的methods中提交mutation。139.3.2最簡(jiǎn)單的storeVuex是解決Vue組件和組件間相互通信而存在的。Vuex理解起來(lái)稍微復(fù)雜,可以通過(guò)以下五個(gè)核心概念來(lái)了解并學(xué)會(huì)使用。它們分別是:state:定義狀態(tài)(變量),輔助函數(shù)mapState。getter:獲取狀態(tài)(變量的值),同時(shí)可以對(duì)狀態(tài)進(jìn)行處理,輔助函數(shù)mapGetters。mutation:修改狀態(tài)(修改變量的值),輔助函數(shù)mapMutations。action:觸發(fā)mutation函數(shù),從而修改狀態(tài),支持異步,輔助函數(shù)mapActions。module:在狀態(tài)很多時(shí),把狀態(tài)分開(kāi)來(lái)管理。149.3.3Vuex中stateVuex使用單一狀態(tài)樹(shù),即用一個(gè)對(duì)象包含了全部的應(yīng)用層級(jí)狀態(tài),它作為一個(gè)“唯一數(shù)據(jù)源”而存在。
單一狀態(tài)樹(shù)讓用戶能夠直接地定位任一特定的狀態(tài)片段,在調(diào)試的過(guò)程中也能輕易地取得整個(gè)當(dāng)前應(yīng)用狀態(tài)的快照。
存儲(chǔ)在Vuex中的數(shù)據(jù)和Vue實(shí)例中的data遵循相同的規(guī)則,都是純粹的對(duì)象(有零個(gè)或多個(gè)的key/value對(duì))。
1.在Vue組件中通過(guò)computed計(jì)算屬性獲得Vuex狀態(tài)
在Vue組件中如何展示狀態(tài)呢?由于Vuex的狀態(tài)存儲(chǔ)是響應(yīng)式的,從store實(shí)例中讀取狀態(tài)最簡(jiǎn)單的方法就是在計(jì)算屬性computed中返回某個(gè)狀態(tài)。
部分代碼如下://
創(chuàng)建一個(gè)
Counter
組件
const
Counter
=
{
template:
`<div>{{
count
}}</div>`,
computed:
{
count
()
{
return
store.state.count
}
}
}
每當(dāng)store.state.count變化的時(shí)候,都會(huì)重新求取計(jì)算屬性,并且觸發(fā)更新相關(guān)聯(lián)的DOM。然而,這種模式導(dǎo)致組件依賴全局狀態(tài)單例。在模塊化的構(gòu)建系統(tǒng)中,在每個(gè)需要使用state的組件中需要頻繁地導(dǎo)入,并且在測(cè)試組件時(shí)需要模擬狀態(tài)。15Vuex通過(guò)main.js文件中的createApp(App).use(store)為每一個(gè)子組件提供Store。部分代碼如下:import
{
createApp
}
from
'vue'
import
App
from
'./App.vue'
import
store
from
'./store'
createApp(App).use(store).mount('#app')
單個(gè)文件組件中通過(guò)computed()獲取狀態(tài)數(shù)據(jù)。代碼如下:<template><div>{{
count
}}</div></template>
<scriptsetup>import{computed}from'vue'import{useStore}from'vuex'conststore=useStore()
constcount=computed(()=>store.state.count
)</script>
【引入問(wèn)題】
當(dāng)一個(gè)組件需要獲取多個(gè)狀態(tài)的時(shí)候,將這些狀態(tài)都聲明為計(jì)算屬性會(huì)有些重復(fù)和冗余。怎么辦呢?【解決辦法】Vuex通過(guò)使用mapState()輔助函數(shù)幫助生成計(jì)算屬性,減少用戶按鍵的次數(shù)。2.在Vue組件中通過(guò)mapState()輔助函數(shù)獲得Vuex狀態(tài)。mapState()函數(shù)返回的是一個(gè)對(duì)象,用來(lái)獲取多個(gè)狀態(tài)。mapState()函數(shù)可以接受對(duì)象{}或數(shù)組[]作為參數(shù)。{}為鍵值對(duì)形式,key:value,key為計(jì)算屬性,value為函數(shù),參數(shù)為store.state,返回需要的state;當(dāng)映射的計(jì)算屬性的名稱與state的子節(jié)點(diǎn)名稱相同時(shí),可以給mapState傳一個(gè)字符串?dāng)?shù)組。9.3.3Vuex中state169.3.3Vuex中state//
1.在單獨(dú)構(gòu)建的版本中輔助函數(shù)為
Vuex.mapState
computed:
mapState({
//
箭頭函數(shù)可使代碼更簡(jiǎn)練
count:
state
=>
state.count,
//
傳字符串參數(shù)
'count'
等同于
`state
=>
state.count`
countAlias:
'count',
//
為了能夠使用
`this`
獲取局部狀態(tài),必須使用常規(guī)函數(shù)
countPlusLocalState
(state)
{
return
state.count
+
this.localCount
}
})
//
2.當(dāng)映射的計(jì)算屬性的名稱與
state
的子節(jié)點(diǎn)名稱相同時(shí)
computed:
mapState([
//
映射
this.count
為
store.state.count
'count'
//可以有多個(gè)state對(duì)象中屬性(key),用逗號(hào)分隔])
【引入問(wèn)題】如何將Vuex狀態(tài)與局部計(jì)算屬性混合使用呢?
【解決辦法】展開(kāi)運(yùn)算符(...)將多個(gè)對(duì)象合并為一個(gè),再傳給computed屬性。
3.對(duì)象展開(kāi)運(yùn)算符部分代碼如下:computed:
{
localComputed
()
{
/*
...
*/
},
//
使用對(duì)象展開(kāi)運(yùn)算符將此對(duì)象混入到外部對(duì)象中
...mapState({
//
...
})
}
4.組件自有局部狀態(tài)
使用Vuex并不意味著需要將所有的狀態(tài)均放入Vuex。
如果有些狀態(tài)嚴(yán)格屬于單個(gè)組件,最好還是作為組件的局部狀態(tài)??梢愿鶕?jù)具體應(yīng)用開(kāi)發(fā)需要進(jìn)行權(quán)衡和確定。179.3.3Vuex中state
【例9-3】Vuex核心概念實(shí)戰(zhàn)--state的應(yīng)用(項(xiàng)目vuex-state-1)
1.在當(dāng)前目錄下,通過(guò)vuecreate命令創(chuàng)建項(xiàng)目,選擇babel+vuex+eslint+Vue3.x,完成后進(jìn)入項(xiàng)目文件夾,啟動(dòng)本地開(kāi)發(fā)服務(wù)。命令如下,執(zhí)行結(jié)果如下圖所示。vuecreatevuex-state-1cdvuex-state-1npmrunserve2.在瀏覽器中打開(kāi)http://localhost:8080,查看頁(yè)面。
3.看到下圖這個(gè)界面說(shuō)明項(xiàng)目啟動(dòng)成功,然后在項(xiàng)目的src目錄下新建一個(gè)目錄store,在該目錄下新建一個(gè)index.js文件,使用createStore({})創(chuàng)建store,并使用exportdefault導(dǎo)出store。具體代碼如下:import
{
createStore
}
from
'vuex'
export
default
createStore({
state:
{
bookname:
'Vue.js前端框架技術(shù)與實(shí)戰(zhàn)',
price:
69.80,
press:
'清華大學(xué)出版社'
},
getters:
{},
mutations:
{},
actions:
{},
modules:
{}
})
18【例9-3】Vuex核心概念實(shí)戰(zhàn)--state4.修改src/App.vue文件。文件包含template、script、style等3個(gè)部分,完成HelloWorld組件導(dǎo)入和使用。代碼如下:<template>
<div>
<img
alt="logo"
src="/logo.png"
/>
<HelloWorld
msg="歡迎使用清華社圖書"
/>
</div>
</template>
<script
setup>
import
HelloWorld
from
"./components/HelloWorld.vue";
</script>
<style>
#app
{text-align:
center;
color:
#2c3e50;
margin-top:
60px;}
</style>
5.修改components/HelloWorld.vue文件。代碼如下:<template>
<div
class="hello">
<h3>{{
msg
}}</h3>
<p>圖書名稱:{{
bookname1
}}</p>
<p>定價(jià):{{
price1
}}</p>
<p>出版社:{{
press1
}}</p>
<h3>使用mapState()</h3>
<p>圖書名稱:{{
storeState.bookname}}</p>
<p>定價(jià):{{
storeState.price
}}</p>
<p>出版社:{{
storeState.press
}}</p>
<!--
1-1.使用toRefs()獲取狀態(tài)
-->
<!--
<p>圖書名稱:{{
bookname}}</p>
<p>定價(jià):{{
price
}}</p>
<p>出版社:{{
press
}}</p>
-->
</div>
</template>
<script
setup>
//
import
{
toRefs
}
from
'vue'
1-2.導(dǎo)入toRefs()
import
{
computed,
defineProps
}
from
'vue'
import
{
useStore,
mapState
}
from
'vuex'
defineProps({
msg:
String
})
const
store
=
useStore()
19【例9-3】Vuex核心概念實(shí)戰(zhàn)--state6.在src子文件夾下創(chuàng)建main.js。代碼如下:import
{
createApp
}
from
'vue'
import
App
from
'./App.vue'
import
store
from
'./store'
createApp(App).use(store).mount('#app')7.切換到瀏覽器界面,并刷新頁(yè)面,效果如下圖所示。
//
單個(gè)獲取store中的狀態(tài)數(shù)據(jù),使用計(jì)算函數(shù)
const
bookname1
=
computed(()
=>
store.state.bookname)
const
price1
=
computed(()
=>
store.state.price)
const
press1
=
computed(()
=>
store.state.press)
//
使用輔助函數(shù)一次性獲取所有狀態(tài)數(shù)據(jù),使用mapState()
const
storeStateFns
=
mapState(['bookname',
'price',
'press'])
//返回函數(shù)對(duì)象console.log(storeStateFns)
const
storeState
=
{}
//
對(duì)storeStateFns進(jìn)行Object.keys(storeStateFns)=[bookname,price,press]
Object.keys(storeStateFns).forEach(fnkey
=>
{
const
fn
=
storeStateFns[fnkey].bind({
$store:
store
})
storeState[fnkey]
=
computed(fn)
})
//
const
{
bookname,
price,
press
}
=
toRefs(store.state)
1-3.使用toRefs()
</script>
<style
scoped>
h3
{margin:
5px
auto;}
</style>
209.3.4Vuex中g(shù)etter1.Vuex中g(shù)etter的需求背景【引入問(wèn)題】工程項(xiàng)目中有時(shí)候需要從store.state中派生出一些狀態(tài),例如對(duì)列表進(jìn)行過(guò)濾并計(jì)數(shù),可以使用計(jì)算屬性來(lái)實(shí)現(xiàn)。實(shí)現(xiàn)的代碼段如下:computed:{doneTodosCount(){//統(tǒng)計(jì)待辦項(xiàng)目中已經(jīng)完成的項(xiàng)目數(shù)returnthis.$store.state.todos.filter(todo
=>todo.done).length}}
如果有多個(gè)組件需要用到此屬性,要么復(fù)制這個(gè)函數(shù),要么抽取到一個(gè)共享函數(shù),然后在多處導(dǎo)入它,但無(wú)論哪種方式使用起來(lái)均不是很理想?!窘鉀Q辦法】Vuex允許在store中定義getter(可以認(rèn)為是store的計(jì)算屬性)。getter的返回值會(huì)根據(jù)它的依賴被緩存起來(lái),且只有當(dāng)它的依賴值發(fā)生了改變才會(huì)被重新計(jì)算。getter可以接受第為一個(gè)參數(shù)為state。部分代碼如下:import{createStore}from'vuex'const
store
=
createStore({
state:
{
todos:
[
{
id:
1,
text:
'...',
done:
true
},
{
id:
2,
text:
'...',
done:
false
}
]
},
getters:
{
doneTodos:
state
=>
{
return
state.todos.filter(todo
=>
todo.done)
}
}
})
212.getter使用方法。常用的方法有通過(guò)屬性、方法和mapGetters()輔助函數(shù)來(lái)訪問(wèn)。
通過(guò)屬性訪問(wèn)。getter會(huì)暴露為store.getters對(duì)象,可通過(guò)屬性的形式訪問(wèn)這些值:store.getters.doneTodos//返回已完成項(xiàng)目[{id:1,text:'...',done:true}]getter可以接受將其他getter作為第二個(gè)參數(shù)。代碼如下:getters:
{
//
...
doneTodosCount:
(state,
getters)
=>
{
return
getters.doneTodos.length
}
}
//使用doneTodosCountstore.getters.doneTodosCount
//返回19.3.4Vuex中g(shù)etter
在其它組件中可以很容易地使用它。代碼如下:constdoneTodosCount=computed(()=>store.getters.doneTodosCount)
注意:通過(guò)屬性訪問(wèn)時(shí),getter作為Vue的響應(yīng)式系統(tǒng)的一部分緩存在其中。通過(guò)方法訪問(wèn)。getters:
{
//
...
getTodoById:
(state)
=>
(id)
=>
{
return
state.todos.find(todo
=>
todo.id
===
id)
}
}
//使用方法
store.getters.getTodoById(2)
//
返回
{
id:
2,
text:
'...',
done:
false
}
229.3.4Vuex中g(shù)etter在OptionsAPI編程中,通過(guò)mapGetters()輔助函數(shù)來(lái)訪問(wèn)。代碼如下:import
{
mapGetters
}
from
'vuex'
export
default
{
//
...
computed:
{
//
使用對(duì)象展開(kāi)運(yùn)算符將
getter
混入
computed
對(duì)象中
...mapGetters([
'doneTodosCount',
'anotherGetter',
//
...
])
}
}
如果想將一個(gè)getter屬性另取一個(gè)名字,使用對(duì)象形式來(lái)定義。代碼如下:mapGetters({
//
把
`this.doneCount`
映射為
`this.$store.getters.doneTodosCount`
doneCount:
'doneTodosCount'
})
【例9-4】Vuex核心概念實(shí)戰(zhàn)之二--getter的使用(項(xiàng)目vuex-getter-1)。步驟如下:1.在當(dāng)前目錄下,新建vuex-getter-1項(xiàng)目,依次執(zhí)行下列指令,完成項(xiàng)目創(chuàng)建與配置工作。vuecreatevuex-getter-1cdvuex-getter-1npmrunserve
2.進(jìn)入vuex-getter-1文件夾,刪除components/HelloWorld.vue組件,然后依次修改App.vue、src/store/index.js等文件。各文件具體內(nèi)容如下:23【例9-4】Vuex核心概念實(shí)戰(zhàn)之二編輯main.js文件。import
{
createApp
}
from
'vue'
import
App
from
'./App.vue'
import
store
from
'./store'
createApp(App).use(store).mount('#app')
編輯index.js文件。import
{
createStore
}
from
'vuex'
export
default
createStore({
state:
{
bookname:
'Vue.js前端框架技術(shù)與實(shí)戰(zhàn)',
price:
69.8,
press:
'清華大學(xué)出版社',
total:
0.0,
count:
2
},
getters:
{
getPrice
(state)
{
return
state.price
},
getCount
(state)
{
return
state.count
},
getTotal
(state,
getters)
{
return
getters.getPrice
*
getters.getCount
}
},
mutations:
{},
actions:
{},
modules:
{}
})
編輯App.vue文件。<template>
<div>
<img
alt="Vue
logo"
src="/vuebook.jpg"
/>
<h3>{{
store.state.bookname
}}</h3>
<p>圖書定價(jià):{{
getPrice
}}</p>
<p>圖書數(shù)量:{{
getCount
}}</p>
<p>購(gòu)買總價(jià):{{
getTotal
}}</p>
</div>
</template>
<scriptsetup>import{toRefs}from'vue'import{useStore}from'vuex'conststore=useStore()const{getPrice,getCount,getTotal}=toRefs(store.getters)</script>
<style>#app{
text-align:center;
color:#2c3e50;
margin-top:60px;}</style>3.切換到瀏覽器界面,刷新頁(yè)面,效果如圖9-11所示249.3.5Vuex中mutation
更改Vuex的store中的狀態(tài)的唯一方法是提交mutation(突變、變異、改變)。
Vuex中的mutation非常類似于事件:每個(gè)mutation都有一個(gè)字符串的事件類型(type)和一個(gè)回調(diào)函數(shù)(handler)-就是實(shí)際進(jìn)行狀態(tài)更改的地方,并且它會(huì)接受state作為第一個(gè)參數(shù)。import{createStore}from'vuex'exportdefaultcreateStore({
state:
{
count:
1
},
mutations:
{
increment
(state)
{
//increment為事件類型type,state為參數(shù)
state.count++
//
變更狀態(tài)
}
}
})
用戶不能直接調(diào)用一個(gè)mutationhandler。這個(gè)選項(xiàng)更像是事件注冊(cè):“當(dāng)觸發(fā)一個(gè)類型為increment的mutation時(shí),調(diào)用此函數(shù)”。要喚醒一個(gè)mutationhandler,需要以相應(yīng)的type調(diào)用mit()方法。代碼如下:mit('increment')
提交載荷(payload)
可以向mit()傳入額外的參數(shù),即mutation的載荷。部分代碼如下://
...
mutations:
{
increment
(state,
n)
{
state.count
+=
n
}
}
259.3.5Vuex中mutation
喚醒這樣mutationhandler同樣需要以相應(yīng)的type調(diào)用mit()方法。mit(type,[payload])//[]表示可選參數(shù)mit('increment',
10)
載荷應(yīng)該是一個(gè)對(duì)象,這樣可以包含多個(gè)字段并且記錄的mutation會(huì)更易讀。部分代碼如下://
...
mit(type,[payload])//[]可選mit('increment',
10)
在多數(shù)情況下,載荷應(yīng)該是一個(gè)對(duì)象,這樣可以包含多個(gè)字段并且記錄的mutation會(huì)更易讀。部分代碼如下:mutations:
{
increment
(state,
payload)
{
state.count
+=
payload.amount
//累加,payload是對(duì)象,amount是其一個(gè)屬性
}
}
相應(yīng)的喚醒方法如下://把payload和type分開(kāi)提交mit('increment',
{
amount:
10
})
對(duì)象風(fēng)格的提交方式
提交mutation的另一種方式是直接使用包含
type
屬性的對(duì)象{}。//整個(gè)對(duì)象都作為載荷傳給mutation函數(shù)mit({
type:
'increment',
amount:
10
})
修改state對(duì)象的方法
有時(shí)會(huì)動(dòng)態(tài)修改state對(duì)象,如增加對(duì)象的屬性,如何才能正確地實(shí)施呢?
可以修改state對(duì)象中的屬性方法如下:Vue.set(obj,'newProp',value)//Vue.set()方法
state.obj={...state.obj,newProp:value}//以新對(duì)象替換老對(duì)象269.3.5Vuex中mutation
例如,state對(duì)象中的student對(duì)象原來(lái)有name、sex兩個(gè)屬性,現(xiàn)在需要增加age屬性。正確的添加方法如下:import{createStore}from'vuex'exportdefaultcreateStore({
state:
{
student:
{
name:
'張?zhí)m英',
sex:
'女'
}
}
})
//以下給state添加一個(gè)age屬性
mutations:
{
addAge
(state)
{
Vue.set(state.student,
'age',
20)
//這是第一種方法,新增屬性需要使用引號(hào)
//
state.student
=
{
...state.student,
age:
18
}
//這是第二種方法
}
}
使用常量替代Mutation事件類型(選講)
在工程項(xiàng)目中,可以使用常量替代mutation事件類型。通常將這些常量放在單獨(dú)的文件中,可以讓項(xiàng)目中所包含的mutation一目了然,方便項(xiàng)目合作者查看使用。具體代碼如下:新建mutation-types.js文件//
mutation-types.js
export
const
SOME_MUTATION
=
'SOME_MUTATION'
27新建store.js文件//
store.js
import
{createStore}
from
'vuex'
import
{
SOME_MUTATION
}
from
'./mutation-types'
exportdefaultcreateStore({
state:
{
...
},
mutations:
{
//
使用
ES2015
風(fēng)格的計(jì)算屬性命名功能來(lái)使用一個(gè)常量作為函數(shù)名
[SOME_MUTATION]
(state)
{
//
mutation
state
}
}
})
注意:函數(shù)名必須是帶[]的類型常量(如[SOME_MUTATION])。建議多人合作的大項(xiàng)目最好用常量的形式來(lái)處理mutation。小項(xiàng)目不需要這樣做。9.3.5Vuex中mutationmutation必須是同步函數(shù)
一條重要的原則就是要記住mutation必須是同步函數(shù)。為什么?要通過(guò)提交mutation的方式來(lái)改變狀態(tài)數(shù)據(jù),才能更明確地追蹤到狀態(tài)的變化。在組件中提交mutation
在OptionAPI編程模式下,組件中可以使用this.$mit(‘xxx')提交mutation,或者使用mapMutations輔助函數(shù)將組件中的methods映射為mit()調(diào)用(需要在根節(jié)點(diǎn)注入store)。具體實(shí)現(xiàn)的部分代碼如下:28組件中script標(biāo)記中導(dǎo)入import
{
mapMutations
}
from
'vuex'
export
default
{
//
...
methods:
{
...mapMutations([
'increment',
//
將
`this.increment()`
映射為
`this.$mit('increment')`
//
`mapMutations`
也支持載荷:
'incrementBy'
//
將
`this.incrementBy(amount)`
映射為
`this.$mit('incrementBy',
amount)`
]),
...mapMutations({
add:
'increment'
//
將
`this.add()`
映射為
`this.$mit('increment')`
})
}
}
9.3.5Vuex中mutation299.3.5Vuex中mutation
在CompositionAPI編程模式下,組件的<script
setup>內(nèi)可以在functionfunName()內(nèi)使用mit("mutationName",[payload])來(lái)提交mutation。具體實(shí)現(xiàn)的部分代碼如下:<!--
采用Vue3.2
新增<script
setup>
-->
<script
setup>
import
{
useStore
}
from
'vuex';
const
store
=
useStore();
//狀態(tài)foods、total、orderSumfunction
add(n)
{
mit("addOrderAmount",
n);
mit("addTotal");
mit("totalSum");
}
function
reduce(n)
{
mit("reduceOrderAmount",
n);
mit("reduceTotal");
mit("totalSum");
}
</script>
309.3.6Vuex中action
action類似于mutation,又不同在于mutation。具體有以下兩點(diǎn):action提交的是mutation,而不是直接變更狀態(tài)。action可以包含任意異步操作。
?actions對(duì)象里的方法需要使用store.dispatch調(diào)用。action函數(shù)接受一個(gè)與store實(shí)例具有相同方法和屬性的context對(duì)象,因此可以調(diào)用mit提交一個(gè)mutation,或者通過(guò)context.state和context.getters來(lái)獲取state和getters。
以下來(lái)注冊(cè)一個(gè)簡(jiǎn)單的action。import{createStore}form'vuex'exportdefaultcreateStore({
state:
{
count:
0
},
mutations:
{
increment
(state)
{
state.count++
}
},
actions:
{
increment
(context)
{
mit('increment')
}
}
})
在項(xiàng)目實(shí)踐中,也可以使用ES2015的參數(shù)解構(gòu)來(lái)簡(jiǎn)化代碼(特別是需要多次調(diào)用commit時(shí)候)。簡(jiǎn)化格式如下:
31actions:
{
increment
({commit
})
{
//{commit}相當(dāng)于{commit:mit}
commit('increment')
//由原來(lái)mit簡(jiǎn)化為commit
}
}
分發(fā)ActionAction通過(guò)store.dispatch方法觸發(fā)Mutations。代碼如下:store.dispatch('increment')
由于mutation必須同步執(zhí)行,但action就沒(méi)有這個(gè)約束??梢栽赼ction內(nèi)部執(zhí)行異步操作。部分代碼如下:actions:
{
incrementAsync
({
commit
})
{
setTimeout(()
=>
{
//=>表示箭頭函數(shù)
commit('increment')
},
1000)
//1000毫秒后執(zhí)行
}
}
9.3.6Vuex中action329.3.6Vuex中actionActions支持同樣的載荷方式和對(duì)象方式進(jìn)行分發(fā)://
以載荷形式分發(fā)
store.dispatch('incrementAsync',
{
amount:
10
})
//
以對(duì)象形式分發(fā)
store.dispatch({
type:
'incrementAsync',
amount:
10
})
在組件中分發(fā)Action
組件中使用this.$store.dispatch(‘xxx’)分發(fā)action,或者使用mapActions輔助函數(shù)將組件的methods映射為store.dispatch()調(diào)用(需要先在根節(jié)點(diǎn)注入store)。
在OptionsAPI編程模式下,Vue2.6.x中,組件中使用this.$store.dispatch(‘xxx')分發(fā)action,或者使用mapActions輔助函數(shù)將組件的methods映射為store.dispatch()調(diào)用(需要先在根節(jié)點(diǎn)注入store)。import
{
mapActions
}
from
'vuex'
export
default
{
//
...
methods:
{
...mapActions([
'increment',
//
將
`this.increment()`
映射為
`this.$store.dispatch('increment')`
//
`mapActions`
也支持載荷:
'incrementBy'
//
將
`this.incrementBy(amount)`
映射為
`this.$store.dispatch('incrementBy',
amount)`
]),
...mapActions({
add:
'increment'
//
將
`this.add()`
映射為
`this.$store.dispatch('increment')`
})
}
}
339.3.6Vuex中action(帶載荷)
在CompositionAPI編程模式下,組件的<scriptsetup>標(biāo)記中可以在functionfunName()內(nèi)使用dispatch("mutationName",[payload])來(lái)提交mutation。具體實(shí)現(xiàn)的部分代碼如下:<script
setup>
import
{useStore}
from
'vuex'
const
{state,commit,dispatch}
=
useStore()
const
add
=
()
=>
{
commit('increment')
}
const
asyncIncrement
=
()
=>
{
dispatch('incrementAsync',3)
}
</script>
Vuex核心概念實(shí)戰(zhàn)之三--muatations和actions的使用(項(xiàng)目vuex-mutation-action-1)。使用store的mutations和actions完成周薪調(diào)節(jié)功能。代碼如下,頁(yè)面效果如圖9-12~圖9-14所示。具體實(shí)現(xiàn)的步驟如下:1.在當(dāng)前目錄下,新建vuex-3-mutation-action項(xiàng)目,依次執(zhí)行下列指令,完成項(xiàng)目創(chuàng)建與配置工作。vuecreatevuex-mutation-action-1cdvuex-mutation-action-1npmrunserve2.修改App.vue、src/store/index.js等文件,在components子文件夾下新建addWeeklyPay.vue、reduceWeek.vue兩個(gè)組件,刪除HelloWorld.vue組件。。349.3.6Vuex中action修改src/store/index.js文件。內(nèi)容如下://
vuex-mutation-action-1
index.js
import
{
createStore
}
from
'vuex'
export
default
createStore({
state:
{
name:
'張成長(zhǎng)',
weeklyPay:
5000,
week:
6
},
mutations:
{
add
(state)
{
state.weeklyPay
=
state.weeklyPay
+
100
},
addNum
(state,
num)
{
//
帶第二個(gè)參數(shù)num(幅度)
state.weeklyPay
=
state.weeklyPay
+
num
},
reduce
(state)
{
state.weeklyPay
=
state.weeklyPay
-
100
},
reduceNum
(state,
num)
{
//
帶第二個(gè)參數(shù)num(幅度)
state.weeklyPay
=
state.weeklyPay
-
num
}
},
修改App.vue文件。內(nèi)容如下:<!--
vuex-mutation-action-1
App.vue
-->
<template>
<div
id="app">
<h2>使用mutation調(diào)增周薪</h2>
<add></add>
<hr
/>
<h2>使用action調(diào)減周薪</h2>
<reduce></reduce>
</div>
</template>
<script
setup>
import
add
from
'./components/addWeeklyPay.vue'
import
reduce
from
'./components/reduceWeeklyPay.vue'
</script>
<style
scoped>
#app
{
margin-top:
10px;
padding:
10px;
border:
1px
dashed
#112233;}
</style>
359.3.6Vuex中action編輯main.js文件import
{
createApp
}
from
'vue'
import
App
from
'./App.vue'
import
store
from
'./store'
createApp(App).use(store).mount('#app')
創(chuàng)建addWeeklyPay.vue組件<!--
addWeeklyPay.vue
-->
<template>
<div>
<p
v-once>
姓名:{{
$
}},第{{
$store.state.week
}}周,周薪:{{
$store.state.weeklyPay
}}元
</p>
<p>姓名:{{
name
}},第{{
week
}}周,周薪:{{
weeklyPay
}}元</p>
<button
@click="addWeeklyPay">增薪(100元)</button>
<button
@click="addWeeklyPayNum">增薪(Num元)</button>
</div>
</template>
actions:
{
addWeeklyPay
(context)
{
mit('add')
},
reduce
(context)
{
mit('reduce')
//
同步減少
},
reduceNum
(context,
num)
{
mit('reduceNum',
num)
//
同步減少
},
reduceAsync
(context)
{
setTimeout(()
=>
{
//
異步
mit('reduce')
},
1000)
},
reduceNumAsync
(context,
step)
{
//
異步帶參數(shù)
setTimeout(()
=>
{
mit('reduceNum',
step)
},
1000)
}
}
})
36<script
setup>
import
{
useStore
}
from
'vuex'
import
{
toRefs
}
from
'vue'
const
{
state,
commit
}
=
useStore()
//解構(gòu)賦值const
addWeeklyPay
=
()
=>
{
commit('add')
}
const
addWeeklyPayNum
=
()
=>
{
commit('addNum',
150)
}
const
{
week,
name,
weeklyPay
}
=
toRefs(state)
//解構(gòu)賦值</script>
<style
scoped="scoped">
button
{
border-radius:
4px
4px;
border:
1px
solid
#1f1202;
height:
28px;
background-color:
#F1F2F3;
}
</style>
創(chuàng)建reduceWeek.vue組件文件。內(nèi)容如下:<!--
reduceWeek.vue
-->
<template>
<div>
<p
v-once>
姓名:{{
$
}},第{{
$store.state.week
}}周,
周薪:{{
$store.state.weeklyPay}}元
</p>
<p>姓名:{{
name
}},第{{
week
}}周,周薪:{{
weeklyPay
}}元</p>
<button
@click="reduceWeeklyPay">降薪(100元)</button>
<button
@click="reduceWeeklyPayNum">降薪(Num元)</button>
<button
@click="reduceAsync">異步Actions降薪(100元)</button>
<button
@click="reduceNumAsync(300)">異步Actions降薪(Num元)</button>
</div>
</template>
9.3.6Vuex中action37<style
scoped>
button
{
border:
1px
solid
#774477;
border-radius:
4px
4px;
height:
28px;
background-color:
#ebebeb;
}
</style>
3.刷新瀏覽器頁(yè)面,效果如圖9-12所示。兩個(gè)子組件中定義的按鈕樣式是略有差異。然后依次單擊各個(gè)組件中的各個(gè)按鈕,頁(yè)面效果如圖9-13所示。9.3.6Vuex中action<script
setup>
import
{
useStore
}
from
'vuex'
import
{
toRefs
}
from
'vue'
const
{
state,
dispatch
}
=
useStore()
const
{
name,
week,
weeklyPay
}
=
toRefs(state)
const
reduceWeeklyPay
=
()
=>
{
dispatch('reduce')
}
const
reduceWeeklyPayNum
=
()
=>
{
dispatch('reduceNum',
300)
}
const
reduceAsync
=
()
=>
{
dispatch('reduceAsync')
}
const
reduceNumAsync
=
(num)
=>
{
dispatch('reduceNumAsync',
num)
}
</script>
389.3.7Vuex中module
由于使用單一狀態(tài)樹(shù),應(yīng)用的所有狀態(tài)會(huì)集中在一個(gè)比較大的對(duì)象中。在工程應(yīng)用變得非常復(fù)雜時(shí),store對(duì)象就有可能變得相當(dāng)臃腫。為了解決以上問(wèn)題,Vuex允許將store分割成模塊(module)。每個(gè)模塊擁有自己的state、getter、mutation、action,甚至是嵌套子模塊--從上至下進(jìn)行同樣方式的分割。多模塊定義方法。以下來(lái)定義兩個(gè)模塊,并注冊(cè)到Vuex中store中。部分代碼參考如下:const
module1
=
{
state:
{
...
},
mutations:
{
...
},
actions:
{
...
},
getters:
{
...
}
}
const
module2
=
{
state:
{
...
},
mutations:
{
...
},
actions:
{
...
}
}
exportdefaultcreateStore({
modules:
{
m1:
module1,
m2:
module2
}
})
store.state.m1//調(diào)用module1的狀態(tài)store.state.m2
//調(diào)用module2的狀態(tài)2.模塊的局部狀態(tài)及使用
對(duì)于模塊內(nèi)部
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 家具導(dǎo)購(gòu)實(shí)戰(zhàn)訓(xùn)練絕對(duì)成交吳飛彤
- 2024至2030年中國(guó)彈力羅緞面料行業(yè)投資前景及策略咨詢研究報(bào)告
- 制造業(yè)主要經(jīng)濟(jì)業(yè)務(wù)的核算
- 2024至2030年中國(guó)分布移動(dòng)式切割機(jī)數(shù)據(jù)監(jiān)測(cè)研究報(bào)告
- 2024年中國(guó)防滑劑市場(chǎng)調(diào)查研究報(bào)告
- 2024年中國(guó)豪華型易拉寶市場(chǎng)調(diào)查研究報(bào)告
- 2024年中國(guó)耐溫耐堿消泡劑市場(chǎng)調(diào)查研究報(bào)告
- 2024年中國(guó)塑膠五金制品市場(chǎng)調(diào)查研究報(bào)告
- 高中數(shù)學(xué)總復(fù)習(xí)系列之集合
- 大學(xué)三年??茖I疽?guī)劃計(jì)劃書
- 風(fēng)景園林工程師答辯(中級(jí))文字版
- 公務(wù)員考試行測(cè)模擬試題及答案解析3
- 2023-2024學(xué)年四川省成都市青羊區(qū)樹(shù)德實(shí)驗(yàn)中學(xué)八年級(jí)上冊(cè)12月月考數(shù)學(xué)試題(含解析)
- 外研社(一年級(jí)起點(diǎn))小學(xué)英語(yǔ)四年級(jí)上冊(cè)單詞(帶音標(biāo)、詞性)
- 電力設(shè)備行業(yè)背景分析報(bào)告
- 基于大數(shù)據(jù)技術(shù)的老年人慢性病風(fēng)險(xiǎn)預(yù)測(cè)模型構(gòu)建與應(yīng)用
- JCT478.2-2013 建筑石灰試驗(yàn)方法 第2部分 化學(xué)分析方法
- 膽囊切除術(shù)術(shù)后健康飲食宣教
- LASI-領(lǐng)導(dǎo)風(fēng)格測(cè)評(píng)試題與答案
- 難治性抑郁癥的治療及護(hù)理
- 小學(xué)一二三年級(jí)勞動(dòng)與技術(shù)《整理書包》課件
評(píng)論
0/150
提交評(píng)論