Vue.js 3.x前端開(kāi)發(fā)技術(shù)與實(shí)戰(zhàn) 課件 -第9章?tīng)顟B(tài)管理Vuex_第1頁(yè)
Vue.js 3.x前端開(kāi)發(fā)技術(shù)與實(shí)戰(zhàn) 課件 -第9章?tīng)顟B(tài)管理Vuex_第2頁(yè)
Vue.js 3.x前端開(kāi)發(fā)技術(shù)與實(shí)戰(zhàn) 課件 -第9章?tīng)顟B(tài)管理Vuex_第3頁(yè)
Vue.js 3.x前端開(kāi)發(fā)技術(shù)與實(shí)戰(zhàn) 課件 -第9章?tīng)顟B(tài)管理Vuex_第4頁(yè)
Vue.js 3.x前端開(kāi)發(fā)技術(shù)與實(shí)戰(zhàn) 課件 -第9章?tīng)顟B(tài)管理Vuex_第5頁(yè)
已閱讀5頁(yè),還剩47頁(yè)未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(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ì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論