vue數據管理vuex知識詳解

數據管理

在實際的開發中,經常會遇到多個組件共享一個數據的場景

在這裏插入圖片描述

面對這種場景,會產生至少以下兩個問題:

  1. 多個組件如何共享同一份數據?
  2. 如果某個組件修改了數據,如何讓其他組件知道?

面對這種問題,一個可行的解決辦法,就是讓數據提升

所謂數據提升,就是把數據提升到更加頂層的組件,讓頂層的組件通過屬性下發數據,而當組件想改變數據的時候,又通過事件一層層向上傳遞

在這裏插入圖片描述
使用這種方式,雖然可以解決問題,但是帶來了更多的問題:

  1. 書寫特別繁瑣
  2. 依賴極其混亂:某些組件本來並不需要一些數據,但是由於它的子組件需要,自己也必須要接收
  3. 無謂的重新渲染:如果數據變化了,並不依賴這些數據的組件也會被迫重新渲染

爲了解決這些問題,vuex出現了

vuex專門用於解決共享數據問題,它的思路和上述一致,也是將數據提升到頂層,不過它使用了一些特別的技巧,不僅讓組件的依賴更加清晰,當數據變動時,僅衝渲染依賴該數據的組件

但是要注意,並非所有數據都需要讓vuex管理,通常vuex只管理那些需要被組件共享的數據

在實際的開發中,一些邏輯特別複雜的數據,儘管不共享,也可能提取到vuex中進行管理

安裝

在頁面中引入vuex

該庫提供了一個構造函數Vuex.Store,通過該構造函數,即可創建一個數據倉庫

var store = new Vuex.Store({
  // 倉庫數據配置
})

將得到的對象,配置到vue中,即可在vue中注入vuex的功能

new Vue({
  // 其他配置
  store
})

現在,你的vue應用擁有了使用數據倉庫的能力

初始化狀態

vuex將倉庫中的數據稱之爲state 狀態

vuex的配置中使用屬性state即可配置倉庫中的狀態初始值

var store = new Vuex.Store({
  state: { // 狀態初始值,這些數據可以被所有組件共享
    onlineNumber: 0, // 在線人數
    movies: { // 電影數據
      data: [],
      total: 0,
      page: 1,
      limit: 3,
      isLoading: false
    }
  }
})

在組件中使用該數據非常簡單

vuexvue實例中注入了一個屬性$store,通過該屬性即可得到倉庫中的數據

this.$store.state // 倉庫中的狀態
<h1>
在線人數:{{$store.state.onlineNumber}}
</h1>

雖然這種寫法看上去很方便,但是書寫繁瑣,並且不利於從閱讀上明確依賴關係

更多的時候,我們會將組件對數據的依賴,明確的聲明在組件的computed配置中

var Comp = {
  computed:{
    onlineNumber(){
      return this.$store.state.onlineNumber;
    }
  },
  template: `
    <h1>
    在線人數:{{onlineNumber}}
    </h1>
  `
}

爲了更加方便的讓我們書寫computedvuex提供了一個函數vuex.mapState

var Comp = {
  computed: Vuex.mapState(["onlineNumber"]), // 等同於上面的代碼
  template: `
    <h1>
    在線人數:{{onlineNumber}}
    </h1>
  `
}

vuex.mapState有非常非常多的用法,目的只有一個:更加方便的書寫computed

狀態模塊化

通常情況下,一個vue實例,只有一個數據倉庫

如果倉庫中的所有狀態都放在一起,既不利於管理,也容易產生名稱的衝突

實際開發中,倉庫中的狀態往往是分爲多個模塊的

var movies = { // 電影模塊
  state: { // 電影模塊的初始狀態
      data: [],
      total: 0,
      page: 1,
      limit: 3,
      isLoading: false
  }
}

var online = { // 在線統計模塊
  state: {
    number: 0
  }
}

// 合併狀態模塊

var store = new Vuex.Store({
  modules:{
    movies,
    online
  }
})

從此,store中的狀態如下:

{
  online:{
    number:0
  },
  movies:{
    data: [],
    total: 0,
    page: 1,
    limit: 3,
    isLoading: false
  }
}

在使用時,可以通過下面的方式映射到computed

var Comp = {
  computed: Vuex.mapState("online", ["number"]), // 第一個參數是模塊名稱(命名空間名)
  template: `
    <h1>
    在線人數:{{number}}
    </h1>
  `
}

數據變化

數據不可能永遠不變,但數據也不會無緣無故的變化

每一次數據變化都有原因,在某種原因的驅使下,數據從一種狀態變化到另一種狀態

vuex把數據的變化過程,稱之爲mutation

mutation在代碼中表現爲一個函數,配置在mutations

var movies = { // 電影模塊
  state: { // 電影模塊的初始狀態
      data: [],
      total: 0,
      page: 1,
      limit: 3,
      isLoading: false
  },
  mutations:{
    /**
     * setPage: mutation的名稱
     * oldState: 原來的狀態
     * payload: 載荷。爲了變化新狀態,需要的額外信息
     */ 
    setPage(oldState, payload){
      oldState.page = payload.page;
    },
    setResp(oldState, payload){
      oldState.data = payload.data;
      oldState.total = payload.total;
    }
  }
}

看上去,mutation似乎非常簡單,僅僅是完成一個賦值即可

但它的意義是非凡的

它至少向外表達了以下信息:

  • 我的數據可以發生哪些變化
  • 除了這些變化之外,不可能再發生任何其他變化
  • 這些變化是原子性的,不可分割的。比如setResp,必須同時變化datatotal,不可能分割

mutation的存在,讓狀態變化變得統一、可控

通過mutation觸發數據的變化,稱之爲commit 提交

提交mutation,是數據發生變化的唯一原因

在這裏插入圖片描述

在代碼層面,提交mutation通過下面的api完成

// 觸發setPage運行,payload爲2
this.$store.commit("setPage", {page:2});

this.$store.commit({
  type: "setPage",
  page: 2
})

當倉庫中的狀態變化時,所有依賴該狀態的組件都會自動重新渲染

追蹤數據變化

mutation讓追蹤數據變化成爲了可能,這非常有利於調試

chrome擴展程序vue-devtools

同時,這也要求我們在編寫mutation時,要注意:

mutation 中不要出現異步代碼,否則會讓狀態的變化難以追蹤

啓用命名空間

由於數據模塊後,非常容易出現mutation重名

啓用命名空間即可解決該問題

var movies = { // 電影模塊
  namespaced: true, // 啓用命名空間
  state: { 
      data: [],
      total: 0,
      page: 1,
      limit: 3,
      isLoading: false
  },
  mutations:{
    setPage(oldState, payload){
      oldState.page = payload.page;
    },
    setResp(oldState, payload){
      oldState.data = payload.data;
      oldState.total = payload.total;
    }
  }
}

命名空間啓用後,commit時,mutation的名稱前需要加上命名空間

this.$store.commit("movies/setPage", { page: 1 })

異步處理

由於mutation不允許使用異步代碼,所以異步代碼需要單獨處理

vuex把異步處理稱之爲action

action中不允許直接更改狀態,但允許提交mutation

var online = {
  namespaced: true,
  state:{
    number: 0
  },
  mutations:{
    add(state){
      state.number++;
    }
  },
  actions:{ //處理異步
    asyncAdd(context){
      // 可以把context當作是store對象
      setTimeout(function(){
        context.commit("add")
      }, 1000)
    }
  }
}

action中,你可以通過參數context參數,獲取store實例

但值得注意的是,如果開啓了namespaced,則contextstore有以下不同:

  • context.state是當前模塊的狀態,而不是整個倉庫的狀態
  • context.rootState纔是整個倉庫的狀態
  • context.commit可省略當前命名空間

如果要觸發一個action,需要使用下面的API

this.$store.dispatch("online/asyncAdd");
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章