­

從零開始的vue學習筆記(七)

  • 2019 年 10 月 15 日
  • 筆記

前言

今天花一天時間閱讀完vuex的官方文檔,簡單的做一下總結和記錄

Vuex是什麼

Vuex 是一個專為 Vue.js 應用程序開發的狀態管理模式,以前的符合「單向數據流」理念的示意圖:

它包含三個部分:

  • state,驅動應用的數據源;
  • view,以聲明方式將 state 映射到視圖;
  • actions,響應在 view 上的用戶輸入導致的狀態變化。

當我們的應用遇到多個組件共享狀態時,單向數據流的簡潔性很容易被破壞:

  • 多個視圖依賴於同一狀態。
  • 來自不同視圖的行為需要變更同一狀態。

實際上就是一個組件間通訊的問題,原來是用$ref直接引用子組件,或者多層嵌套組件,或者依賴注入provideinject等暴力方式,在應用和組件複雜的情況下複雜度和可維護性都會成為巨大問題。

所以,vuex就誕生了,vuex的原理圖:

這個圖描述了vuex的數據傳導邏輯,綠色虛線部分為vuex插件本身

  1. 首先,Vuex自身提供了一個store(倉庫),數據結構為樹形的,採用單例設計,裏面用key-value(value可以是string、數字、數組、Object等)的形式包含了一個應用的各種狀態值,並提供了響應式的狀態更新,提供給vue組件Render來渲染。
  2. 傳統的Vue組件接受用戶對界面的操作後,通過分發(Dispatch)這些前端事件或者說響應給Vuex的Action,在Action中可以用來添加自己的業務邏輯,同時可以異步調用一些其他的後端API
  3. Action通過Commit來提交對應的Mutations里的方法,達到調用Mutations里的方法的目的,這個時候可以用Devtools插件來追蹤狀態數據在Mutations里的方法調用前後的數據變化,形成快照等(Mutations里的方法必須同步的)
  4. Mutations里的一些mutation(變異)方法體執行,改變應用的一些State狀態屬性,這些mutation是Vuex改變狀態的唯一途徑,直接修改State狀態值是不允許的(數據不可追蹤),從而形成了單向數據流的完整鏈路,同時狀態是可維護可追蹤響應式可復用

下面就一起來看看Vuex的各個詳細部分:

安裝

  • 直接下載(推薦)或者CDN引入
    從https://unpkg.com/vuex下載下來,然後通過js引入:

      <script src="/path/to/vue.js"></script>    <script src="/path/to/vuex.js"></script>
  • npm/yarn

      //npm    npm install vuex --save    //yarn    yarn add vuex
  • 模塊化的打包系統

    import Vue from 'vue'  import Vuex from 'vuex'  //前面vue基礎部分就有Vue.use()引入插件的用法,  //下面這句在打包系統中是必備的  Vue.use(Vuex)

核心概念

  • State
    首先,Vuex的所有概念都只有一個api:圍繞Vuex.Store(…options) 這個構造器展開,類似Vue的概念都圍繞Vue的構造器展開一樣;State的作用就類似於Vue裏面的data,簡單的new Vuex的例子:

      // 如果在模塊化構建系統中,請確保在開頭調用了 Vue.use(Vuex)      const store = new Vuex.Store({    state: {        count: 0    },    mutations: {        increment (state) {        state.count++        }    }    })

    這樣,在我們的Vue插件里就可以用computed計算屬性來獲取這些state值:

      // 創建一個 Counter 組件    const Counter = {    template: `<div>{{ count }}</div>`,    computed: {        count () {        return store.state.count        }    }    }

    為了簡化寫法(少些代碼),官方提供了一個mapState輔助函數避免寫store.state.count這一長串,其他的輔助函數mapGettersmapActionsmapMutations都是類似的作用,API鏈接

例子:

  // 在單獨構建的版本中輔助函數為 Vuex.mapState  import { mapState } from 'vuex'    export default {    // ...    computed: mapState({      // 箭頭函數可使代碼更簡練      count: state => state.count,        // 傳字符串參數 'count' 等同於 `state => state.count`      countAlias: 'count',        // 為了能夠使用 `this` 獲取局部狀態,必須使用常規函數      countPlusLocalState (state) {        return state.count + this.localCount      }    })  }
  • Getter
    單純的用State里的狀態值還不夠強大,所以Vuex提供了Getter來對State作進一步的複雜邏輯處理,類似於Vue裏面的computed計算屬性對data的進一步處理一樣。
    例子:

      const store = new Vuex.Store({    state: {        todos: [        { id: 1, text: '...', done: true },        { id: 2, text: '...', done: false }        ]    },    getters: {        doneTodos: state => {        return state.todos.filter(todo => todo.done)        }    }    })

    如果對更多的語法細節感興趣,可以閱讀官方鏈接

  • Mutation
    Mutaion中文解釋是變異,用來執行對State狀態改變的同步方法,可以簡單的類比Vue中的methods,只不過Vue中的methods沒有區分同步異步方法,而Vuex中為了追蹤數據狀態,用Mutation執行同步方法,Action直觀性異步方法,做了這種拆分,讓Devtools等工具發揮作用。
    例子:

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

    更多的語法細節參考鏈接

  • Action
    Action 類似於 mutation,不同在於:
    • Action 通過commit提交的是 mutation,而不是直接變更狀態。
    • Action 可以包含任意異步操作
      例子:
      const store = new Vuex.Store({    state: {        count: 0    },    mutations: {        increment (state) {        state.count++        }    },    actions: {        increment (context) {        context.commit('increment')        }    }    })

    組件里通過store.dispatch 來出發actions

      store.dispatch('increment')

    更多語法細節見鏈接

  • Module
    如果只靠一個大的store里的state狀態樹來維護整個應用,當規模巨大,勢必會有問題,所以引入Module做模塊化的拆分,拆成按照命名空間的子狀態樹。每個模塊擁有自己的 state、mutation、action、getter、甚至是嵌套子模塊。
    例子:

      const moduleA = {    state: { ... },    mutations: { ... },    actions: { ... },    getters: { ... }    }      const moduleB = {    state: { ... },    mutations: { ... },    actions: { ... }    }      const store = new Vuex.Store({    modules: {        a: moduleA,        b: moduleB    }    })      store.state.a // -> moduleA 的狀態    store.state.b // -> moduleB 的狀態

    引入namespaced: true確保每個模塊的獨立命名空間,更多語法細節見鏈接

  • 其他
    其他的主題包括:
    • 項目結構
    • 插件
    • 嚴格模式
    • 表單處理
    • 測試
    • 熱重載

這些主題不是核心問題,在需要看的時候或者自己感興趣的再來看,詳見鏈接