1. 程式人生 > 程式設計 >Vuex模組化應用實踐示例

Vuex模組化應用實踐示例

Vuex作為Vue全家桶的成員之一,重要性肯定不用多說,正在做Vue專案的同學,隨著專案需求、功能逐漸增加,用到Vuex也是早晚的事兒,作為一個前端,只能面對現實:學不動也得學!

這篇文章主要介紹Vuex在大型專案中的模組化及持久化應用實踐,下面正文開始

Vuex的應用場景

  • 多個元件檢視共享同一狀態時(如登入狀態等)
  • 多個元件需要改變同一個狀態時
  • 多個元件需要互相傳遞引數且關係較為複雜,正常傳參方式變得難以維護時
  • 持久化儲存某些資料

所以我們把元件共享的狀態抽離出來,不管元件間的關係如何,都通過Vuex來處理

組織store目錄

我們先按模組化的方式組織store目錄,並在Vue根例項中註冊store,Vuex 通過 store 選項,提供了一種機制將狀態從根元件“注入”到每一個子元件中

src
├── ...
├── main.js
├── App.vue
└── store
  ├── index.js     # 我們組裝模組並匯出 store 的地方
  └── modules
    ├── product.js    # 產品模組
    ├── windowInfo.js  # 視窗資訊模組
    └── user.js     # 登入模組

src/store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
import user from './modules/user'
import product from './modules/product'
import windowInfo from './modules/windowInfo'

Vue.use(Vuex)

export default new Vuex.Store({
 modules: {
  // 註冊modules中的模組
  user,product,
  windowInfo
 }
})

src/main.js

import ...
import store from './store' // 新增這行

new Vue({
 el: '#app',router,store,// 注入到根例項
 template: '<App/>',components: { App }
})

store的屬性

state(狀態物件)
state中存放多頁面共享的狀態欄位

getters
相當於當前模組state的計算屬性

mutations
如果想更新state中的欄位,提交mutations中定義的事件是唯一的方式(key為事件名,value是一個函式),但是這個事件函式必須是同步執行的

actions
可以定義非同步函式,並在回撥中提交mutation,就相當於非同步更新了state中的欄位

Vuex模組化應用實踐示例

vuex資料傳遞規則

使用方法

把視窗的高度和寬度存到Vuex中,並且每當視窗被resize,state中的高度和寬度自動更新

src/store/modules/windowInfo.js

import { MU_WIN_RESIZE } from '../../common/constants'
const windowInfo = {
 state: {
  // 初始化
  winHeight: 0,winWidth: 0
 },mutations: {
  // 這裡把事件名統一抽離到constants.js統一管理,方便維護,避免重複。
  // 當然,你也可以不這麼寫。。
  // mutation事件接受的第一個引數是當前模組的state物件
  // 第二個引數是提交事件時傳遞的附加引數
  [MU_WIN_RESIZE] (state,payload) {
   const { winWidth,winHeight } = payload
   state.winWidth = winWidth
   state.winHeight = winHeight
  }
 },actions: {},getters: {}
}

export default windowInfo

src/common/constants.js

export const MU_WIN_RESIZE = 'MU_WIN_RESIZE' // 更新視窗尺寸

下面開啟專案的根元件新增監聽resize事件和提交mutation事件邏輯

src/App.vue

<!--上面的template我就不往這兒放了-->
<script>
 import { _on,_off,getClientWidth,getClientHeight } from './common/dom'
 import { MU_WIN_RESIZE } from './common/constants'
 import { mapMutations } from 'vuex'

 export default {
  name: 'app',data () {
   return {}
  },mounted () {
   this.handleResize()
   // 這裡對addEventListener方法做了IE相容處理,就不貼出來了,反正事件監聽你們都會
   _on(window,'resize',this.handleResize)
  },beforeDestroy () {
   _off(window,methods: {
   // 物件展開運算子,不熟悉的同學該學學ES6了
   ...mapMutations({
    // 對映 this.winResize 為 this.$store.commit(MU_WIN_RESIZE)
    winResize: MU_WIN_RESIZE
   }),handleResize () {
    const winWidth = getClientWidth()
    const winHeight = getClientHeight()
    this.winResize({ winWidth,winHeight })
   }
  }
 }
</script>

到這一步,在拖動視窗觸發‘resize'事件的時候,就會觸發‘MU_WIN_RESIZE'這個mutation事件並把視窗寬高寫入vuex,下面我們隨便找個頁面看能不能獲取到我們寫入的值

<template>
 <div class="row">視窗高:{{winHeight}} 視窗寬:{{winWidth}}</div>
</template>
<script>
 import { mapState } from 'vuex'
 export default {
  name: 'test',computed: {
   // 把state寫入計算屬性
   // 如果使用mapGetters也是寫入計算屬性
   ...mapState({
    winHeight: state => state.windowInfo.winHeight,winWidth: state => state.windowInfo.winWidth
   })
  },}
</script>

有的時候我們會從後端獲取一些下拉框選項的靜態常量,而且很多頁面都能用到,這個時候用Vuex是比較好的選擇,涉及到後端獲取,就要用到可以使用非同步的actions了

src/store/modules/product.js

import {MU_PRODUCT_UPDATE_CONSTANTS} from '../../common/constants'

const product = {
 state: {
  productConstants: []
 },mutations: {
  [MU_PRODUCT_UPDATE_CONSTANTS] (state,payload) {
   state.productConstants = payload
  }
 },actions: {
  // action函式第一個引數接受一個與 store 例項具有相同方法和屬性的 context 物件,
  // 因此你可以呼叫 context.commit 提交一個 mutation,
  // 或者通過 context.state 和 context.getters 來獲取 state 和 getters
  // 這裡雖然能獲取到state,但是不建議直接修改state中的欄位
  async getProductConstants ({ commit },payload) {
   try {
    // 請求介面,如果需要引數可以通過payload傳遞
    const res = await this.$api.product.getConstants()
    commit(MU_PRODUCT_UPDATE_CONSTANTS,res)
   } catch (e) {
    console.error(e)
   }
  }
 },getters: {}
}

export default product

下面觸發這個getProductConstants事件,觸發這個action事件的位置需要注意一下,假設你有5個元件需要使用這個state,那就應該在這5個元件共同的父元件中呼叫一次action(找不到就在根例項中呼叫),然後在各個子元件中通過mapState或mapGetters獲取state,千萬不要每個元件使用前都呼叫一次action方法!

src/App.vue

<!--為了更直觀的展示action,把之前的程式碼刪掉了-->
<script>
 import { mapActions } from 'vuex' // 注意是mapActions

 export default {
  name: 'app',created () {
   // 觸發請求
   this.getProductConstants()
  }
  methods: {
   ...mapActions([
    // 對映 this.getProductConstants 為 this.$store.dispatch('getProductConstants')
    'getProductConstants'
   ])
  }
 }
</script>

mapGetters,mapMutations,mapActions,這幾個函式可以接受物件也可以接受陣列作為引數,如果你需要在元件中以別的名字呼叫該事件(像上面的mapMutations)就可以傳入物件,key為新命名,value是store中定義的名字;否則的話傳陣列就好了。

那麼問題來了,既然是非同步操作,我想在操作結束後乾點兒別的怎麼做呢?
很簡單,呼叫action中的非同步函式(this.$store.dispatch)返回的是一個Promise,如果你跟我一樣用的是async await:

<!--為了更直觀的展示action,把之前的程式碼刪掉了-->
<script>
 import { mapActions } from 'vuex' // 注意是mapActions

 export default {
  name: 'app',async created () {
   // 觸發請求
   await this.getProductConstants()
   // 接下來執行的操作會等待上面函式完成才會執行
  }
  methods: {
   ...mapActions([
    // 對映 this.getProductConstants 為 this.$store.dispatch('getProductConstants')
    'getProductConstants'
   ])
  }
 }
</script>

如果你用的不是async await那就麻煩一點,在actions中定義事件的時候return一個new Promise,官方文件中有一個例子

表單處理

當你把從state中獲取的欄位填在v-model中時,如果使用者修改表單資料,v-model會嘗試直接修改store中的資料,這樣做會有兩個問題:

  1. 破壞了vuex的資料傳遞規則,如果想修改state中的資料只能通過提交一個mutation
  2. 控制檯報錯:計算屬性沒有setter

官方提供了兩種解決方法,我更傾向於下面這種,給計算屬性新增setter,並在setter中提交mutation修改state:

<template>
 <input v-model="message">
</template>
<script>
  export default {
  name: 'app',computed: {
   message: {
    get () {
     return this.$store.state.test.message
    },set (value) {
     this.$store.commit('updateMessage',value)
    }
   }
  }
  methods: {}
 }
</script>

Vuex持久化

推薦外掛vuex-persist

安裝外掛:

npm install --save vuex-persist

引入、配置、載入外掛:
src/store/persist.js

import VuexPersistence from 'vuex-persist'

const persist = new VuexPersistence({
 // 其他引數看文件
 storage: window.sessionStorage
})
export default persist.plugin

src/store/index.js

import ...
import persist from './persist'

Vue.use(Vuex)

export default new Vuex.Store({
 modules: {
  user,product,windowInfo
 },plugins: [persist]
})

現在重新整理瀏覽器資料也不會重置了!

總結

以上就是vuex比較常規的操作了,第一次看官方文件的我是懵逼的、無助的,但是用了一段時間vuex再重新看文件的時候會有很多收穫。希望對大家的學習有所幫助,也希望大家多多支援我們。