天天看點

一文教會你認識Vuex狀态機

摘要:簡單來說,Vuex就是實作元件全局狀态(資料)管理的一種機制,可以友善的實作元件之間資料的共享。

本文分享自華為雲社群《Vuex狀态機快速了解與應用》,原文作者:北極光之夜。

一. 速識概念:

1. 元件之間共享資料的方式:

通常有以下幾種方式:

  1. 父向子傳值:v-bind 屬性綁定;
  2. 子向父傳值:v-on 事件綁定;
  3. 兄弟元件之間共享資料:EventBus;

2. vuex是什麼:

  1. 按照官方的話來說,Vuex 是一個專為 Vue.js 應用程式開發的狀态管理模式。它采用集中式存儲管理應用的所有元件的狀态,并以相應的規則保證狀态以一種可預測的方式發生變化。Vuex 也內建到 Vue 的官方調試工具 devtools extension (opens new window),提供了諸如零配置的 time-travel 調試、狀态快照導入導出等進階調試功能。
  2. 簡單來說,Vuex就是實作元件全局狀态(資料)管理的一種機制,可以友善的實作元件之間資料的共享。

3.使用vuex優點:

  1. 能夠在vuex中集中管理共享的資料,易于開發和後期維護。
  2. 能夠高效地實作元件之間的資料共享, 提高開發效率。
  3. 存儲在vuex中的資料都是響應式的,能夠實時保持資料與頁面的同步。
  4. 解決了非父子元件的消息傳遞(将資料存放在state中)。
  5. 減少了AJAX請求次數,有些情景可以直接從記憶體中的state擷取。

一般情況下,隻有元件之間共享的資料,才有必要存儲到vuex中。而對于元件中的私有資料,就沒必要了,依舊存儲在元件自身的data中即可。當然,如果你想要都存在vuex中也是可以的。

二. 基本使用:

1.安裝依賴包:

npm install vuex --save      

2.導入依賴包:

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)      

3.建立store對象:

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const store = new Vuex.Store({
//state中存放的就是全局共享的資料
  state: {
    count: 0
  }
})      

4. 将store對象挂載到vue執行個體中:

new Vue({
  el: '#app',
  store
})      

此時所有元件就可以從store中擷取資料了。

三.建立項目:

下面為建立一個vue項目流程,後面會有案例:

(1)打開cmd視窗輸入 vue ui 打開vue的可視化面闆:

一文教會你認識Vuex狀态機

(2)選擇建立項目路徑:

一文教會你認識Vuex狀态機

(3)命名:

一文教會你認識Vuex狀态機

(4)手動選擇配置,注意用的是vue2版本:

一文教會你認識Vuex狀态機
一文教會你認識Vuex狀态機

(5)建立:

一文教會你認識Vuex狀态機

(6)下一步:

一文教會你認識Vuex狀态機

(7)建立成功,到對應目錄打開vscode開始程式設計:

一文教會你認識Vuex狀态機

(8)運作項目:

一文教會你認識Vuex狀态機

四. 講解前提:

前提(注意):

寫一個計數器小案例,從案例中配合概念能更快上手vuex。是以下面核心概念中的代碼部分是基于這個小案例來示範的。目标:寫兩個子元件,有一個公共count值,在父元件中,其中一個元件實作點選後count值減1,一個元件實作點選後count值增1。

父元件 App.vue 初始代碼:

<template>
  <div id="app">
       <my-add></my-add>
       <p>--------------------</p>
       <my-reduce></my-reduce>
  </div>
</template>

<script>
// 引入元件
import Add from './components/Add.vue'
import Reduce from './components/Reduce.vue'
export default {
  name: 'App',
  data() {
    return {
      
    }
  },
  components: {
    'my-add': Add,
    'my-reduce': Reduce
  }

}
</script>      

子元件Add.vue初始代碼:

<template>
    <div>
        <p>count值為:</p>
           <button>+1</button>
    </div>
 
</template>
<script>
  export default{
      data() {
          return {
              
          }
      },
  }
</script>      

子元件Reduce.vue初始代碼:

<template>
    <div>
         <p>count值為:</p>
           <button>-1</button>
    </div>
</template>
<script>
  export default{
      data() {
          return {
              
          }
      },
  }
</script>      

store對象初始代碼為:

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    count: 0
  }
})      

初始效果:

一文教會你認識Vuex狀态機

五.核心概念:

1.state:

按照官方的話來說,如下:Vuex 使用單一狀态樹——是的,用一個對象就包含了全部的應用層級狀态。至此它便作為一個“唯一資料源 (SSOT)”而存在。這也意味着,每個應用将僅僅包含一個 store 執行個體。

簡單來說,就是State提供唯一的公共資料源, 所有共享的資料都要統一放到Store的State中進行存儲。

1.1 元件中通路state的第一種方式:

元件中直接輸入以下指令:

如在Add.vue子元件中引用:

<template>
    <div>
        <p>count值為:{{this.$store.state.count}}</p>
           <button>+1</button>
    </div> 
</template>
//下面部分代碼跟前面一樣無改變,是以省略了      

看效果,顯示了count的值為0:

一文教會你認識Vuex狀态機

1.2 元件中通路state的第二種方式:

(1)從 vuex 中按需導入 mapState 函數

import { mapState } from 'vuex'      

(2)通過剛才導入的mapState函數,将目前元件需要的全局資料,映射為目前元件的computed計算屬性:

computed: {
   ...mapState([count])
}      

小知識:computed用來監控自己定義的變量,該變量不在data裡面聲明,直接在computed裡面定義,然後就可以在頁面上進行雙向資料綁定展示出結果或者用作其他處理;

如在Reduce.vue子元件中引用:

<template>
    <div>
         <p>count值為:{{count}}</p>
           <button>-1</button>
    </div>
</template>
<script>
import {mapState} from 'vuex'
  export default{
      data() {
          return {
              
          }
      },
      computed: {
         ...mapState(['count'])
      }
  }
</script>      

看效果,同樣顯示了count的值為0:

一文教會你認識Vuex狀态機

2. mutation:

按照官方的話來說,更改 Vuex 的 store 中的狀态的唯一方法是送出 mutation。Vuex 中的 mutation 非常類似于事件:每個 mutation 都有一個字元串的 事件類型 (type) 和 一個 回調函數 (handler)。這個回調函數就是我們實際進行狀态更改的地方,并且它會接受 state 作為第一個參數。

簡單來說就是Mutation用于變更Store中的資料。

①隻能通過mutation變更Store資料,不可以直接操作Store中的資料。

②通過這種方式雖然操作起來稍微繁瑣一些,但是可以集中監控所有資料的變化。

比如,要實作count值自增加1的操作,那就在先motations裡定義一個自增加1的函數。然後對應子元件想用,該元件就直接引入mutation并調用對應的函數就好。

如下,Add.vue子元件要實作自增加1功能:先在狀态機裡的mutations裡定義一個能實作自增的函數add:

export default new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    //自增加1函數
    add(state){
      state.count++
    }
  }
})      

2.1 觸發mutation的第一種方式:

Add.vue子元件裡給按鈕綁定點選事件,并觸發mutation:

<template>
    <div>
        <p>count值為:{{this.$store.state.count}}</p>
           <button @click="btnAdd">+1</button>
    </div>
 
</template>
<script>
  export default{
      data() {
          return {
              
          }
      },
      methods: {
          btnAdd() {
              // 第一種引入mutation的方式,觸發add函數
              this.$store.commit('add')
          }
      }
  }
</script>      

看效果實作了點選自增:

一文教會你認識Vuex狀态機

2.2 觸發mutation并傳參數:

當然,當元件裡調用mutation裡函數時,也是可以傳參數的。比如,有一個自增函數,但增多少看調用時傳入的參數:

export default new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    // 傳入參數,第一個一定是state,第二個為傳入的參數
    //自增加 n 的函數
    addN(state,n){
      state.count+= n
    }
  }
})      

對應元件調用時要傳入參數:

methods: {
          btnAdd2() {
              // 引入mutation的方式,觸發addN函數
              // 并傳參,自增加6吧
              this.$store.commit('addN',6)
          }
      }      

2.3 觸發mutation的第二種方式:

(1)從 vuex 中按需導入 mapMutations 函數

import { mapMutations } from 'vuex'      

(2)通過剛才導入的mapMutations函數,将需要的mutations函數,映射為目前元件的methods方法:

methods: {
   ...mapMutations(['add','addN'])
}      

實戰,實作Reduce.vue元件的點選自減1的功能要求:

狀态機添加自減函數:

export default new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    //自增加1函數
    add(state){
      state.count++
    },
    // 自減1的函數
    sub(state){
      state.count--
    }
  }
})      

Reduce.vue元件點選按鈕實作自減1:

<template>
    <div>
         <p>count值為:{{count}}</p>
           <button @click="btnSub">-1</button>
    </div>
</template>
<script>
//導入
import {mapState,mapMutations} from 'vuex'
  export default{
      data() {
          return {
              
          }
      },
      computed: {
         ...mapState(['count'])
      },
      methods: {
          // 映射mutation裡的sub函數
          ...mapMutations(['sub']),
          // 要自減,調用sub函數
          btnSub(){
             this.sub()
          }
      }
  }
</script>      

看效果:

一文教會你認識Vuex狀态機

3.Action:

至此,第四大點裡的案例已經完成,已經實作了自增和自減,現在對案例做改進,要我們點選按鈕一秒後再自增和自減,該怎麼實作?可以在狀态機裡的mutation裡的函數是加一個1秒定時器嗎,這肯定是不行的,因為mutation裡不支援異步操作,那咋辦,當當當,Action閃亮登場。

Action 可以包含任意異步操作,是以它用來處理異步任務。

Action 送出的是 mutation,而不是直接變更狀态。記住它并不能直接修改state裡的資料,隻有mutation能修改。就是說,如果通過異步操作變更資料,必須通過Action,而不能使用Mutation,但是在Action中還是要通過觸發Mutation的方式間接變更資料。

先在狀态機裡定義Action:

export default new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    //自增加1函數
    add(state){
      state.count++
    },
    // 自減1的函數
    sub(state){
      state.count--
    }
  },
  // 定義action,裡面的addAsync函數實作1秒後執行mutation裡的add函數
   actions: {
    addAsync(context) {
      setTimeout(()=>{
      // 必須通過context.commit()觸發mutation才行
         context.commit('add')
    },1000)
  }  
 }
})      

Action 函數接受一個與 store 執行個體具有相同方法和屬性的 context 對象,是以你可以調用 context.commit 送出一個 mutation。

3.1 觸發Action的第一種方式:

更改元件Add.vue代碼,引入Action,實作異步自增操作。

<template>
    <div>
        <p>count值為:{{this.$store.state.count}}</p>
           <button @click="btnAdd">+1</button>
    </div>
 
</template>
<script>
  export default{
      data() {
          return {
              
          }
      },
      methods: {
          btnAdd() {
              // 第一種引入Action的方式,觸發addAsync函數
              // 這裡的dispatch專門用來調用action函數
              this.$store.dispatch('addAsync')
          }
      }
  }
</script>      

看效果,實作1秒後自增:

一文教會你認識Vuex狀态機

3.2 觸發Action異步任務并傳參數:

當然,當元件裡調用action裡函數時,也是可以傳參數的。比如,有一個點選1秒後才執行的自增函數,但增多少看調用時傳入的參數:

定義:

export default new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
   // 傳入參數,第一個一定是state,第二個為傳入的參數
    //自增加 n 的函數
    addN(state,n){
      state.count+= n
    }
  },
   actions: {
    // 有參數 n,這個n又傳給了mutation裡的addN函數
    addNAsync(context,n) {
      setTimeout(()=>{
         context.commit('addN',n)
    },1000)
  }  
 }
})      
methods: {
          btnAdd2() {
              // 調用dispatch函數
              // 觸發action時傳參數,為 6 吧,表示自增6
              this.$store.dispatch('addNAsync',6)
          }
      }      

3.3 觸發Action的第二種方式:

(1)從 vuex 中按需導入 mapActions 函數

import { mapActions } from 'vuex'      

(2)通過剛才導入的mapActions函數,将需要的actions函數,映射為目前元件的methods方法:

methods: {
   ...mapActions(['add','addN'])
}      

實戰,實作Reduce.vue元件的點選一秒後自減1的功能要求:

定義actions裡的subAsync為一秒後自減函數:

export default new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    //自增加1函數
    add(state){
      state.count++
    },
    // 自減1的函數
    sub(state){
      state.count--
    }
  },
   actions: {
    addAsync(context) {
      setTimeout(()=>{
         context.commit('add')
    },1000)
  },
   subAsync(context) {
      setTimeout(()=>{
         context.commit('sub')
    },1000)
  }    
 }
})      

更改Reduce.vue代碼,實作功能:

<template>
    <div>
         <p>count值為:{{count}}</p>
           <button @click="btnSub">-1</button>
    </div>
</template>
<script>
//導入
import {mapState,mapActions} from 'vuex'
  export default{
      data() {
          return {
              
          }
      },
      computed: {
         ...mapState(['count'])
      },
      methods: {
          // 映射Action裡的函數
          ...mapActions(['subAsync']),
          // 要自減,調用subAsync函數
          btnSub(){
             this.subAsync()
          }
      }
  }
</script>      
一文教會你認識Vuex狀态機

4. Getter:

Getter用于對Store中的資料進行加工處理形成新的資料。且要注意的是它并不會修改state中的資料。

①Getter 可以對Store中已有的資料加工處理之後形成新的資料,類似Vue的計算屬性。

②Store 中資料發生變化,Getter 的資料也會跟着變化。

如,有一個傳回目前count+1的getter函數:

4.1 觸發getters的第一種方式:

this.$store.getters.名稱      

在App.vue元件中顯示:

<template>
  <div id="app">
       <my-add></my-add>
       <p>--------------------</p>
       <my-reduce></my-reduce>
       <p>--------------------</p>
       <h3>{{this.$store.getters.showNum}}</h3>
  </div>
</template>      

效果:

一文教會你認識Vuex狀态機

4.2觸發getters的第二種方式:

(1)從 vuex 中按需導入 mapGetters 函數

import { mapGetters } from 'vuex'      

(2)通過剛才導入的mapGetters函數,将目前元件需要的全局資料,映射為目前元件的computed計算屬性:

computed: {
   ...mapGetters(['showNum'])
}      

還是在App.vue中使用把:

<template>
  <div id="app">
       <my-add></my-add>
       <p>--------------------</p>
       <my-reduce></my-reduce>
       <p>--------------------</p>
       <h3>{{showNum}}</h3>
  </div>
</template>

<script>
// 引入元件
import Add from './components/Add.vue'
import Reduce from './components/Reduce.vue'
// 導入 mapGetters函數
import {mapGetters} from 'vuex'
export default {
  name: 'App',
  data() {
    return {
      
    }
  },
  components: {
    'my-add': Add,
    'my-reduce': Reduce
  },
  // 引入 getter
  computed: {
    ...mapGetters(['showNum'])
  }

}
</script>      

看,一樣的效果:

一文教會你認識Vuex狀态機

點選關注,第一時間了解華為雲新鮮技術~

繼續閱讀