Vuex的含義以及作用是什么

本篇內(nèi)容介紹了“Vuex的含義以及作用是什么”的有關(guān)知識,在實(shí)際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

成都創(chuàng)新互聯(lián)長期為千余家客戶提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊(duì)從業(yè)經(jīng)驗(yàn)10年,關(guān)注不同地域、不同群體,并針對不同對象提供差異化的產(chǎn)品和服務(wù);打造開放共贏平臺,與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為宕昌企業(yè)提供專業(yè)的成都網(wǎng)站設(shè)計(jì)、網(wǎng)站制作,宕昌網(wǎng)站改版等技術(shù)服務(wù)。擁有十年豐富建站經(jīng)驗(yàn)和眾多成功案例,為您定制開發(fā)。

一、Vuex是干什么用的?

Vuex是用于對復(fù)雜應(yīng)用進(jìn)行狀態(tài)管理用的(官方說法是它是一種狀態(tài)管理模式)。

“殺雞不用宰牛刀”。對于簡單的項(xiàng)目,根本用不著Vuex這把“宰牛刀”。那簡單的項(xiàng)目用什么呢?用Vue.js官方提供的“事件總線”就可以了。

二、我們import進(jìn)來的Vuex對象都包含些什么呢?

我們使用Vuex的時候怎么用呢?通常都是這樣:

import Vue from 'vue'; import Vuex from 'vuex';  Vue.use(Vuex);  new Vuex.Store({ state: {     //放置state的值   count: 0,   str:"abcd234"   }, getters: {   //放置getters方法     strLen: state => state.str.length }, // mutations只能是同步操作 mutations: {   //放置mutations方法       increment(state, payload) {          //在這里改變state中的數(shù)據(jù)          state.count = payload.number;       } }, // actions可以是異步操作 actions: {      //放置actions方法       actionName({ commit }) {         //dosomething         commit('mutationName')       },       getSong ({commit}, id) {         api.getMusicUrlResource(id).then(res => {           let url = res.data.data[0].url;         })         .catch((error) => {  // 錯誤處理             console.log(error);         });     } } });  new Vue({  el: '#app',  store,  ... });

這里import進(jìn)來的Vuex是個什么東西呢?我們用console.log把它輸出一下:

console.log(Vuex)

通過輸出,我們發(fā)現(xiàn)其結(jié)構(gòu)如下:

Vuex的含義以及作用是什么

可見,import進(jìn)來的Vuex它實(shí)際上是一個對象,里面包含了Store這一構(gòu)造函數(shù),還有幾個mapActions、mapGetters、mapMutations、mapState這幾個輔助方法(后面再講)。

除此之外,還有一個install方法。我們發(fā)現(xiàn),import之后要對其進(jìn)行Vue.use(Vuex)的操作。根據(jù)這兩個線索,我們就明白了,Vuex本質(zhì)上就是一個Vue.js的插件。

三、創(chuàng)建好的store實(shí)例怎么在各個組件中都能引用到?

Vuex 通過 store 選項(xiàng),提供了一種機(jī)制將狀態(tài)從根組件“注入”到每一個子組件中(需調(diào)用 Vue.use(Vuex)):

const app = new Vue({  el: '#app',  // 把 store 對象提供給 “store” 選項(xiàng),這可以把 store 的實(shí)例注入所有的子組件  store,  components: { Counter },  template: `    <div class="app">      <counter></counter>    </div>  ` })

通過在根實(shí)例中注冊 store 選項(xiàng),該 store實(shí)例會注入到根組件下的所有子組件中,且子組件能通過 this.$store 訪問到。

四、Vuex中的幾大核心概念

1. State

這個很好理解,就是狀態(tài)數(shù)據(jù)。Vuex所管理的就是狀態(tài),其它的如Actions、Mutations都是來輔助實(shí)現(xiàn)對狀態(tài)的管理的。Vue組件要有所變化,也是直接受到State的驅(qū)動來變化的。

可以通過this.$store.state來直接獲取狀態(tài),也可以利用vuex提供的mapState輔助函數(shù)將state映射到計(jì)算屬性computed中去。

2. Getters

Getters本質(zhì)上是用來對狀態(tài)進(jìn)行加工處理。Getters與State的關(guān)系,就像Vue.js的computed與data的關(guān)系。  getter的返回值會根據(jù)它的依賴被緩存起來,且只有當(dāng)它的依賴值發(fā)生了改變才會被重新計(jì)算。

可以通過this.$store.getters.valueName對派生出來的狀態(tài)進(jìn)行訪問。或者直接使用輔助函數(shù)mapGetters將其映射到本地計(jì)算屬性中去。

3. Mutations

更改 Vuex的 store中的狀態(tài)的唯一方法是提交 mutation。Vuex中的 mutation非常類似于事件:每個  mutation都有一個字符串的 事件類型 (type) 和 一個 回調(diào)函數(shù) (handler)。這個回調(diào)函數(shù)就是我們實(shí)際進(jìn)行狀態(tài)更改的地方。

你不能直接調(diào)用一個 mutation handler。這個選項(xiàng)更像是事件注冊:“當(dāng)觸發(fā)一個類型為 increment 的  mutation時,調(diào)用此函數(shù)?!币獑拘岩粋€mutation handler,你需要以相應(yīng)的 type 調(diào)用store.commit方法,并且它會接受  state 作為第一個參數(shù),也可以向 store.commit 傳入額外的參數(shù),即 mutation 的 載荷(payload):

const store = new Vuex.Store({  state: {    count: 1 },  mutations: {    increment (state) {      // 變更狀態(tài)      state.count++   } } }) store.commit('increment')
// ... mutations: {  increment (state, n) {    state.count += n } } store.commit('increment', 10)

在大多數(shù)情況下,載荷應(yīng)該是一個對象,這樣可以包含多個字段并且記錄的 mutation 會更易讀:

// ... mutations: {  increment (state, payload) {    state.count += payload.amount } } store.commit('increment', {  amount: 10 }) // ***或者以對象風(fēng)格提交*** mutations: {  increment (state, payload) {    state.count += payload.amount } } store.commit({  type: 'increment',  amount: 10 })

Mutation需遵守 Vue的響應(yīng)規(guī)則

既然 Vuex的 store中的狀態(tài)是響應(yīng)式的,那么當(dāng)我們變更狀態(tài)時,監(jiān)視狀態(tài)的 Vue組件也會自動更新。這也意味著 Vuex中的  mutation也需要與使用 Vue一樣遵守一些注意事項(xiàng):

  • 最好提前在你的 store中初始化好所有所需屬性。

  • 當(dāng)需要在對象上添加新屬性時,你應(yīng)該:

  • 使用 Vue.set(obj, 'newProp', 123), 或者

  • 以新對象替換老對象。例如,利用對象展開運(yùn)算符 我們可以這樣寫:

state.obj = { ...state.obj, newProp: 123 }

Mutation必須是同步函數(shù)

一條重要的原則就是要記住 mutation必須是同步函數(shù)。為什么?請參考下面的例子:

mutations: {  someMutation (state) {    api.callAsyncMethod(() => {      state.count++   }) } }

現(xiàn)在想象,我們正在 debug一個 app并且觀察 devtool中的 mutation日志。每一條  mutation被記錄,devtools都需要捕捉到前一狀態(tài)和后一狀態(tài)的快照。然而,在上面的例子中 mutation中的異步函數(shù)中的回調(diào)讓這不可能完成:因?yàn)楫?dāng)  mutation觸發(fā)的時候,回調(diào)函數(shù)還沒有被調(diào)用,devtools不知道什么時候回調(diào)函數(shù)實(shí)際上被調(diào)用&mdash;&mdash;實(shí)質(zhì)上任何在回調(diào)函數(shù)中進(jìn)行的狀態(tài)的改變都是不可追蹤的。

在組件中提交 Mutation

除了這種使用 this.$store.commit('xxx') 提交 mutation的方式之外,還有一種方式,即使用 mapMutations  輔助函數(shù)將組件中的 methods映射為 this.$store.commit。例如:

import { mapMutations } from 'vuex'  export default {  // ...  methods: {    ...mapMutations([      'increment', // 將 `this.increment()` 映射為 `this.$store.commit('increment')`       // `mapMutations` 也支持載荷:      'incrementBy' // 將 `this.incrementBy(amount)` 映射為 `this.$store.commit('incrementBy', amount)`   ]),    ...mapMutations({      add: 'increment' // 將 `this.add()` 映射為 `this.$store.commit('increment')`   }) } }

經(jīng)過這樣的映射之后,就可以通過調(diào)用方法的方式來觸發(fā)其對應(yīng)的(所映射到的)mutation  commit了,比如,上例中調(diào)用add()方法,就相當(dāng)于執(zhí)行了this.$store.commit('increment')了。

4. Actions

Action類似于 mutation,不同在于:

  • Action提交的是 mutation,而不是直接變更狀態(tài)。

  • Action可以包含任意異步操作。

Action函數(shù)接受一個與 store實(shí)例具有相同方法和屬性的 context對象,因此你可以調(diào)用 context.commit 提交一個  mutation,或者通過 context.state 和 context.getters 來獲取 state和 getters。

分發(fā) Action

Action 通過 store.dispatch 方法觸發(fā):

store.dispatch('increment')

Actions 支持同樣的載荷方式和對象方式進(jìn)行分發(fā):

// 以載荷形式分發(fā) store.dispatch('incrementAsync', {  amount: 10 })  // 以對象形式分發(fā) store.dispatch({  type: 'incrementAsync',  amount: 10 })

另外,你需要知道, this.$store.dispatch 可以處理被觸發(fā)的 action 的處理函數(shù)返回的 Promise,并且  this.$store.dispatch 仍舊返回 Promise。

actions: {  actionA ({ commit }) {    return new Promise((resolve, reject) => {      setTimeout(() => {        commit('someMutation')        resolve()     }, 1000)   }) } }  store.dispatch('actionA').then(() => {  // ... })

5. Module

Module是什么概念呢?它實(shí)際上是對于store的一種切割。由于Vuex使用的是單一狀態(tài)樹,這樣整個應(yīng)用的所有狀態(tài)都會集中到一個比較大的對象上面,那么,當(dāng)應(yīng)用變得非常復(fù)雜時,store對象就很可能變得相當(dāng)臃腫!Vuex允許我們將  store分割成一個個的模塊(module)。每個模塊擁有自己的  state、mutation、action、getter、甚至是嵌套子模塊&mdash;&mdash;從上至下進(jìn)行同樣方式的分割。

(1)模塊的局部狀態(tài)

對于每個模塊內(nèi)部的 mutation 和 getter,接收的第一個參數(shù)就是模塊的局部狀態(tài)對象,對于模塊內(nèi)部的  getter,根節(jié)點(diǎn)狀態(tài)會作為第三個參數(shù)暴露出來。同樣,對于模塊內(nèi)部的 action,局部狀態(tài)通過 context.state 暴露出來,根節(jié)點(diǎn)狀態(tài)則為  context.rootState:

const moduleA = {  state: () => ({    count: 0 }),  mutations: {    increment (state) {      // 這里的 `state` 對象是模塊的局部狀態(tài)      state.count++   } },   getters: {    doubleCount (state) {      return state.count * 2   },    sumWithRootCount (state, getters, rootState) {      return state.count + rootState.count   } },   actions: {    incrementIfOddOnRootSum ({ state, commit, rootState }) {      if ((state.count + rootState.count) % 2 === 1) {        commit('increment')     }   } } }

(2)命名空間

默認(rèn)情況下,模塊內(nèi)部的 action、mutation和 getter是注冊在全局命名空間的&mdash;&mdash;這樣使得多個模塊能夠?qū)ν?mutation或  action作出響應(yīng)。

如果希望你的模塊具有更高的封裝度和復(fù)用性,你可以通過添加 namespaced: true 的方式使其成為帶命名空間的模塊。當(dāng)模塊被注冊后,它的所有  getter、action及 mutation都會自動根據(jù)模塊注冊的路徑調(diào)整命名。例如:

const store = new Vuex.Store({  modules: {    account: {      namespaced: true,       // 模塊內(nèi)容(module assets)      state: { ... }, // 模塊內(nèi)的狀態(tài)已經(jīng)是嵌套的了,使用 `namespaced` 屬性不會對其產(chǎn)生影響      getters: {        isAdmin () { ... } // -> getters['account/isAdmin']     },      actions: {        login () { ... } // -> dispatch('account/login')     },      mutations: {        login () { ... } // -> commit('account/login')     },       // 嵌套模塊      modules: {        // 繼承父模塊的命名空間        myPage: {          state: { ... },          getters: {            profile () { ... } // -> getters['account/profile']         }       },         // 進(jìn)一步嵌套命名空間        posts: {          namespaced: true,           state: { ... },          getters: {            popular () { ... } // -> getters['account/posts/popular']         }       }     }   } } })

啟用了命名空間的 getter 和 action 會收到局部化的 getter,dispatch 和 commit。換言之,你在使用模塊內(nèi)容(module  assets)時不需要在同一模塊內(nèi)額外添加空間名前綴。更改 namespaced 屬性后不需要修改模塊內(nèi)的代碼。

(3)在帶命名空間的模塊內(nèi)訪問全局內(nèi)容(Global Assets)

如果你希望使用全局 state和 getter,rootState 和 rootGetters 會作為第三和第四參數(shù)傳入 getter,也會通過  context 對象的屬性傳入 action。

若需要在全局命名空間內(nèi)分發(fā) action或提交 mutation,將 { root: true } 作為第三參數(shù)傳給 dispatch 或 commit  即可。

modules: {  foo: {    namespaced: true,     getters: {      // 在這個模塊的 getter 中,`getters` 被局部化了      // 你可以使用 getter 的第四個參數(shù)來調(diào)用 `rootGetters`      someGetter (state, getters, rootState, rootGetters) {        getters.someOtherGetter // -> 'foo/someOtherGetter'        rootGetters.someOtherGetter // -> 'someOtherGetter'     },      someOtherGetter: state => { ... }   },     actions: {      // 在這個模塊中, dispatch 和 commit 也被局部化了      // 他們可以接受 `root` 屬性以訪問根 dispatch 或 commit      someAction ({ dispatch, commit, getters, rootGetters }) {        getters.someGetter // -> 'foo/someGetter'        rootGetters.someGetter // -> 'someGetter'         dispatch('someOtherAction') // -> 'foo/someOtherAction'        dispatch('someOtherAction', null, { root: true }) // -> 'someOtherAction'         commit('someMutation') // -> 'foo/someMutation'        commit('someMutation', null, { root: true }) // -> 'someMutation'     },      someOtherAction (ctx, payload) { ... }   } } }

(4)在帶命名空間的模塊注冊全局 action

若需要在帶命名空間的模塊注冊全局 action,你可添加 root: true,并將這個 action 的定義放在函數(shù) handler 中。例如:

{  actions: {    someOtherAction ({dispatch}) {      dispatch('someAction')   } },  modules: {    foo: {      namespaced: true,       actions: {        someAction: {          root: true,          handler (namespacedContext, payload) { ... } // -> 'someAction'       }     }   } } }

(5)帶命名空間綁定函數(shù)

當(dāng)使用 mapState, mapGetters, mapActions 和 mapMutations  這些函數(shù)來綁定帶命名空間的模塊時,寫起來可能比較繁瑣:

computed: {  ...mapState({    a: state => state.some.nested.module.a,    b: state => state.some.nested.module.b }) }, methods: {  ...mapActions([    'some/nested/module/foo', // -> this['some/nested/module/foo']()    'some/nested/module/bar' // -> this['some/nested/module/bar']() ]) }

對于這種情況,你可以將模塊的空間名稱字符串作為第一個參數(shù)傳遞給上述函數(shù),這樣所有綁定都會自動將該模塊作為上下文。于是上面的例子可以簡化為:

computed: {  ...mapState('some/nested/module', {    a: state => state.a,    b: state => state.b }) }, methods: {  ...mapActions('some/nested/module', [    'foo', // -> this.foo()    'bar' // -> this.bar() ]) }

(6)模塊動態(tài)注冊

在 store創(chuàng)建之后,你可以使用 store.registerModule 方法注冊模塊:

import Vuex from 'vuex'  const store = new Vuex.Store({ /* 選項(xiàng) */ })  // 注冊模塊 `myModule` store.registerModule('myModule', {  // ... }) // 注冊嵌套模塊 `nested/myModule` store.registerModule(['nested', 'myModule'], {  // ... })

之后就可以通過 store.state.myModule 和 store.state.nested.myModule 訪問模塊的狀態(tài)。

模塊動態(tài)注冊功能使得其他 Vue 插件可以通過在 store 中附加新模塊的方式來使用 Vuex  管理狀態(tài)。例如,vuex-router-sync插件就是通過動態(tài)注冊模塊將vue-router 和 vuex結(jié)合在一起,實(shí)現(xiàn)應(yīng)用的路由狀態(tài)管理。

你也可以使用 store.unregisterModule(moduleName) 來動態(tài)卸載模塊。注意,你不能使用此方法卸載靜態(tài)模塊(即創(chuàng)建  store時聲明的模塊)。

注意,你可以通過 store.hasModule(moduleName) 方法檢查該模塊是否已經(jīng)被注冊到 store。

保留 state

在注冊一個新 module時,你很有可能想保留過去的 state,例如從一個服務(wù)端渲染的應(yīng)用保留 state。你可以通過 preserveState  選項(xiàng)將其歸檔:store.registerModule('a', module, { preserveState: true })。

當(dāng)你設(shè)置 preserveState: true 時,該模塊會被注冊,action、mutation和 getter會被添加到 store中,但是  state不會。這里假設(shè) store的 state已經(jīng)包含了這個 module的 state并且你不希望將其覆寫。

(7)模塊重用

有時我們可能需要創(chuàng)建一個模塊的多個實(shí)例,例如:

  • 創(chuàng)建多個 store,他們共用同一個模塊 (例如當(dāng) runInNewContext 選項(xiàng)是 false 或 'once'  時,為了在服務(wù)端渲染中避免有狀態(tài)的單例 (opens new window))

  • 在一個 store中多次注冊同一個模塊

如果我們使用一個純對象來聲明模塊的狀態(tài),那么這個狀態(tài)對象會通過引用被共享,導(dǎo)致狀態(tài)對象被修改時 store 或模塊間數(shù)據(jù)互相污染的問題。

實(shí)際上這和 Vue組件內(nèi)的 data 是同樣的問題。因此解決辦法也是相同的&mdash;&mdash;使用一個函數(shù)來聲明模塊狀態(tài)(僅 2.3.0+ 支持):

const MyReusableModule = {  state: () => ({    foo: 'bar' }),  // mutation, action 和 getter 等等... }

五、Vuex中的表單處理

當(dāng)在嚴(yán)格模式中使用 Vuex時,在屬于 Vuex的 state上使用 v-model 會比較棘手:

<input v-model="obj.message">

假設(shè)這里的 obj 是在計(jì)算屬性中返回的一個屬于 Vuex store 的對象,在用戶輸入時,v-model 會試圖直接修改  obj.message。在嚴(yán)格模式中,由于這個修改不是在 mutation 函數(shù)中執(zhí)行的, 這里會拋出一個錯誤。

用“Vuex 的思維”去解決這個問題的方法是:給 <input> 中綁定 value,然后偵聽 input 或者 change 事件,在事件回調(diào)中調(diào)用一個方法:

<input :value="message" @input="updateMessage"> computed: {  ...mapState({    message: state => state.obj.message }) }, methods: {  updateMessage (e) {    this.$store.commit('updateMessage', e.target.value) } }

下面是 mutation函數(shù):

// ... mutations: {  updateMessage (state, message) {    state.obj.message = message } }

必須承認(rèn),這樣做比簡單地使用“v-model + 局部狀態(tài)”要啰嗦得多,并且也損失了一些 v-model 中很有用的特性。另一個方法是使用帶有  setter 的雙向綁定計(jì)算屬性:

<input v-model="message"> computed: {  message: {    get () {      return this.$store.state.obj.message   },    set (value) {      this.$store.commit('updateMessage', value)   } } }

“Vuex的含義以及作用是什么”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

網(wǎng)站名稱:Vuex的含義以及作用是什么
文章起源:http://muchs.cn/article26/iejcjg.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供手機(jī)網(wǎng)站建設(shè)App開發(fā)、移動網(wǎng)站建設(shè)、網(wǎng)站設(shè)計(jì)、虛擬主機(jī)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

小程序開發(fā)