Vuex 快速使用

本文對 Vuex 官方文檔重新組織編排,希望正在學(xué)習(xí) Vue 的同學(xué)們,在閱讀后可快速使用 Vuex。

開始使用 Vuex,把狀態(tài)拿到應(yīng)用外部管理,Vuex 管這個(gè)管理狀態(tài)的玩意叫 Store,一個(gè)完全獨(dú)立的應(yīng)用,他只負(fù)責(zé)狀態(tài)管理。嘗試把 Vuex 應(yīng)用和 Vue 應(yīng)用劃清界限,

  • 一個(gè) Vuex 應(yīng)用,做狀態(tài)管理,可以理解是 Model 層
  • 一個(gè) Vue 應(yīng)用,僅負(fù)責(zé)數(shù)據(jù)展示,純純的 View 層

Vuex 應(yīng)用

所謂狀態(tài)管理,無非就是定義狀態(tài),修改狀態(tài)

定義 state

在 Vuex 里定義狀態(tài),我們需要 new 一個(gè) Store 出來,每一個(gè) Vuex 應(yīng)用的核心就是 store(倉庫)。

// store.js
import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

const store = new Vuex.Store({
  state: {
    count: 0
  }
});

export default store;

以上代碼創(chuàng)建了一個(gè) store,store.state 里定義了狀態(tài)。與在 Vue 里定義 data 沒有任何區(qū)別,

下面修改狀態(tài)。

直接修改

如果你想快點(diǎn)用上 Vuex,你可以在組件里直接修改 store 里的 state,(直接修改的意思就是,用點(diǎn)操作修改)

state.count = 2;

雖然這樣可以正常工作,但在嚴(yán)格模式下會(huì)報(bào)錯(cuò),更改 store 中的狀態(tài)的唯一方法應(yīng)該是提交 mutation

嚴(yán)格模式

開啟嚴(yán)格模式,僅需在創(chuàng)建 store 的時(shí)候傳入 strict: true

const store = new Vuex.Store({
  strict: true
});

在嚴(yán)格模式下,無論何時(shí)發(fā)生了狀態(tài)變更且不是由 mutation 函數(shù)引起的,將會(huì)拋出錯(cuò)誤。

mutation

更改 Vuex 的 store 中的狀態(tài)的唯一方法是提交 mutation。

Vuex.Store的構(gòu)造器選項(xiàng)中,有一個(gè) mutation 選項(xiàng),這個(gè)選項(xiàng)就像是事件注冊:key 是一個(gè)字符串表示 mutation 的類型(type),value 是一個(gè)回調(diào)函數(shù)(handler),

這個(gè)回調(diào)函數(shù)就是我們實(shí)際進(jìn)行狀態(tài)更改的地方,它有兩個(gè)入?yún)ⅲ谝粋€(gè)參數(shù)是 state,就是 store 里的 state;第二個(gè)參數(shù)是 Payload,這是提交 mutation 時(shí)候額外傳入的參數(shù)。

定義 mutation

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

提交 mutation

上面定義了 mutation,要喚醒一個(gè) mutation handler,唯一的接口是store.commit,如果你熟悉事件監(jiān)聽,commit 就類似于 Vue 的$emit,jquery 的trigger。

可想而知,commit 方法傳參必須至少有一個(gè)能區(qū)分 mutation 的唯一標(biāo)識(shí),這個(gè)標(biāo)識(shí)就是 type,

commit 參數(shù)是對象

當(dāng) commit 的參數(shù)是一個(gè)對象的時(shí)候,對象里必須要有 type 字段,除了 type 字段,你還可以添加額外任意字段為載荷(payload)。

// 對象風(fēng)格的提交方式
store.commit({
  type: "increment",
  amount: 10
});

type 做第一參數(shù)

把 type 和 payload 分開也是個(gè)不錯(cuò)的選擇,可以把 type 單獨(dú)拿出來當(dāng)?shù)谝粋€(gè)參數(shù),以commit(type,[payload])的形式提交,官方稱此為以載荷形式提交。

store.commit("increment");
store.commit("increment", 10);
store.commit("increment", { count: 2 });

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

到這里我們已經(jīng)知道如何定義 mutation 和提交 mutation 了,commit 接口很簡單,但在哪里使用呢?有兩個(gè)地方用

  • Vue 應(yīng)用的組件里,在組件里調(diào)用store.commit。
  • 還有 store 的 action 里。

Action

狀態(tài)管理不過就是定義 state 和修改 state,mutation 已經(jīng)可以修改 state 了,為什么還需要 action?

同步和異步

在 Vuex 中,mutation 都是同步事務(wù),在 mutation 中混合異步調(diào)用會(huì)導(dǎo)致你的程序很難調(diào)試。

例如,當(dāng)你調(diào)用了兩個(gè)包含異步回調(diào)的 mutation 來改變狀態(tài),你怎么知道什么時(shí)候回調(diào)和哪個(gè)先回調(diào)呢?這就是為什么我們要區(qū)分這兩個(gè)概念。

Action 確實(shí)和 mutation 很類似,不同在于:

  • Action 提交的是 mutation,而不是直接變更狀態(tài)。
  • Action 可以包含任意異步操作。

注冊 action:

注冊 action 就跟定義 mutation 一樣,除了 handler 的入?yún)⒉煌?/p>

Action 函數(shù)的入?yún)⑹且粋€(gè)與 store 實(shí)例具有相同方法和屬性的 context 對象。因此可以

  • context.commit 提交一個(gè) mutation,
  • context.state獲取 state。
  • context.getters 獲取 getters。
const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment(state) {
      state.count++;
    }
  },
  actions: {
    increment(context) {
      context.commit("increment");
    }
  }
});

分發(fā) Action

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

// 以載荷形式分發(fā)
store.dispatch("incrementAsync", {
  amount: 10
});

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

組合 Action

store.dispatch 可以處理被觸發(fā)的 action 的處理函數(shù)返回的 Promise,并且 store.dispatch 仍舊返回 Promise,因此,通過 async / await,很方便控制流程

actions: {
  async actionA ({ commit }) {
    commit('gotData', await getData())
  },
  async actionB ({ dispatch, commit }) {
    await dispatch('actionA') // 等待 actionA 完成
    commit('gotOtherData', await getOtherData())
  }
}

表單處理

表單的問題在于使用v-model時(shí)候,v-model會(huì)試圖直接修改 state,而 Vuex 在嚴(yán)格模式下是不允許直接修改 state 的。

很容易解決,只要我們把修改 state 的行為按 Vuex 的要求以commit mutation 方式修改即可。

有兩種方式。

用“Vuex 的思維”解決

拋棄v-model指令,自己去實(shí)現(xiàn)v-model雙向綁定,非常簡單,

  • input標(biāo)簽的value屬性綁定對應(yīng)從 store 中映射來的計(jì)算屬性,
  • 監(jiān)聽 input 事件,用 commit mutation 的方式,修改 store 里的 state。
<input :value="message" @input="updateMessage" />
computed: {
  ...mapState({
    message: state => state.obj.message
  })
},

methods: {
  updateMessage (e) {
    this.$store.commit('updateMessage', e.target.value)
  }
}

store 中的 mutation 函數(shù):

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

用 Vue 計(jì)算屬性解決

如果堅(jiān)持使用v-model,可以在 Vue 里對v-model綁定的計(jì)算屬性設(shè)置 set 行為。

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

使用 Vuex 進(jìn)行狀態(tài)管理,到此結(jié)束。

從 flux 架構(gòu)來看,三個(gè)核心,StateMutation,Action,已經(jīng)足夠了,總結(jié)一下其實(shí)很簡單,同步 commit mutation,異步 dispatch action。

核心原則

  • 應(yīng)用層級(jí)的狀態(tài)應(yīng)該集中到單個(gè) store 對象中。
  • 提交 mutation 是更改狀態(tài)的唯一方法,并且這個(gè)過程是同步的。
  • 異步邏輯都應(yīng)該封裝到 action 里面。

記住這些原則,開始動(dòng)手把 Vuex 集成到 Vue 項(xiàng)目中吧,至于一些更多的概念,都是些錦上添花的東西。

Vue 應(yīng)用使用 Vuex

Vuex 的常用 api 上面都涉及到了,使用思路就是

  • 定義狀態(tài):通過new Vuex.Store({})創(chuàng)建一個(gè) store 實(shí)例,定義 state,getters,actions,mutations。
  • 改變狀態(tài):使用store.commit提交 mutation,使用store.dispatch分發(fā)actions。

現(xiàn)在狀態(tài)管理的部分已經(jīng)全部由 store 實(shí)例去管理了,如何在 Vue 組件中使用 store,非常簡單,一點(diǎn)也不神奇。

store.js文件里我們創(chuàng)建了 store 實(shí)例并將其導(dǎo)出了(export),按照 js 模塊化的知識(shí),我們只要在需要的地方導(dǎo)入它就可以直接使用了。

組件引入 store

可以在需要的組件里直接引入,就像引入一個(gè)普通的 js 一樣。

import store from "path/to/store.js";

組件中引入了 store,就可以直接通過store.state引用 state,以及直接使用 store 實(shí)例簡潔的 api,真的就是這么簡單。

store.state.count = 2; // 直接修改,并不建議
store.commit(); // 在 store 中調(diào)用 mutation
store.dispatch("increment"); // 在 store 中分發(fā) action

我們往往需要在 Vue 組件的template中展示狀態(tài),由于 Vuex 的狀態(tài)存儲(chǔ)是響應(yīng)式的,從 store 實(shí)例中讀取狀態(tài)最簡單的方法就是在計(jì)算屬性中返回某個(gè)狀態(tài):

import store from "path/to/store.js";
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。

其實(shí)到現(xiàn)在為止,對于在 Vue 中使用 Vuex 就已經(jīng)足夠了。下面的東西其實(shí)可以不用看了,但 Vuex 還是提供了一些方便我們使用的方式,錦上添花。

組件中引入的方式,缺點(diǎn):這種模式導(dǎo)致組件依賴全局狀態(tài)單例。在模塊化的構(gòu)建系統(tǒng)中,在每個(gè)需要使用 state 的組件中需要頻繁地導(dǎo)入。

全局引入 store

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

// app.js
import Vue from "vue";

import store from "path/to/store.js";

const app = new Vue({
  store // 把 store 對象提供給 “store” 選項(xiàng),這可以把 store 的實(shí)例注入所有的子組件
});

通過在根實(shí)例中注冊 store 選項(xiàng),該 store 實(shí)例會(huì)注入到根組件下的所有子組件中,且子組件能通過 this.$store 訪問到。這樣我們就不用每個(gè)組件單獨(dú)引入了。

this.$store.state.count = 2; // 直接修改,并不建議
this.$store.commit("", {}); // 直接在組件中提交 mutation
this.$store.dispatch("increment"); // 在組件中分發(fā) action

組件綁定的輔助函數(shù)

  • mapState
  • mapGetters
  • mapMutations
  • mapActions

既然是輔助,就不是必須的東西,輔助函數(shù)可以方便的把 Vuex 中的 state,getter,mutation,action 映射到組件,方便調(diào)用。

更多概念

Getter

如果你很喜歡 Vue 提供的計(jì)算屬性(computed),Vuex 允許在 store 中定義“getter”(可以認(rèn)為是 store 的計(jì)算屬性)。

但對狀態(tài)管理來說,Getter 其實(shí)并不是必須的,如果你需要的話,可以查看文檔使用。

Module

模塊化并不是狀態(tài)管理的概念,也不是必須的,但如果應(yīng)用十分復(fù)雜,將 store 分割成模塊(module)會(huì)是一個(gè)必經(jīng)之路,Vuex 提供了模塊化選項(xiàng),需要可查看文檔。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 安裝 npm npm install vuex --save 在一個(gè)模塊化的打包系統(tǒng)中,您必須顯式地通過Vue.u...
    蕭玄辭閱讀 3,040評論 0 7
  • Vuex 概念篇 Vuex 是什么? Vuex 是一個(gè)專為 Vue.js 應(yīng)用程序開發(fā)的狀態(tài)管理模式。它采用集中式...
    Junting閱讀 3,176評論 0 43
  • State 單一狀態(tài)樹 Vuex使用單一狀態(tài)樹——用一個(gè)對象就包含了全部的應(yīng)用層級(jí)狀態(tài)。至此它便作為一個(gè)“唯一數(shù)據(jù)...
    oWSQo閱讀 1,174評論 0 0
  • Vuex是什么? Vuex 是一個(gè)專為 Vue.js應(yīng)用程序開發(fā)的狀態(tài)管理模式。它采用集中式存儲(chǔ)管理應(yīng)用的所有組件...
    蕭玄辭閱讀 3,233評論 0 6
  • 上一章總結(jié)了 Vuex 的框架原理,這一章我們將從 Vuex 的入口文件開始,分步驟閱讀和解析源碼。由于 Vuex...
    你的肖同學(xué)閱讀 1,890評論 3 16

友情鏈接更多精彩內(nèi)容