Vue.js 系列教程 4:Vuex


原文:intro-to-vue-4-vuex

譯者:nzbin

這是關於 JavaScript 框架 Vue.js 五個教程的第四部分。在這一部分,我們會學習使用 Vuex 進行狀態管理。這個系列教程並不是一個完整的用戶手冊,而是通過基礎知識讓你快速了解 Vuejs 以及它的用途。

系列文章:

  1. 渲染, 指令, 事件
  2. 組件, Props, Slots
  3. Vue-cli
  4. Vuex (你在這!)
  5. Animations

Vuex

如果你錯過了關於組件及 Vue-cli 的部分,在閱讀本篇文章之前應該先讀讀這幾部分。現在我們已經了解了關於組件、傳遞狀態和 props 的基本知識,接下來討論一下 Vuex,它是狀態管理的好工具。

之前,我們是從頂層組件向下傳遞狀態,而同胞組件之間並沒有分享數據。如果它們需要相互通信,我們要在應用程序中推送狀態。這是可以的!但是一旦你的程序變得復雜,這種方法就沒有意義了。如果你之前用過 Redux,那 Vuex 中所有的概念及實現對你也不陌生。Vuex 是 Vue 中的 Redux。實際上,Redux 也可以用於 Vue,但是,使用專門為 Vue 設計的工具 Vuex 更加有利。

首先,安裝 Vuex:

npm install vuex

或者

yarn add vuex

我這樣設置: 在 `/src` 目錄下,我創建了名為 store 的目錄 ( 這是一種選擇,你也可以在同級目錄創建一個 `store.js` 文件 ),再在其中創建一個名為 `store.js`的文件。`store.js` 中的初始設置如下 ( vstore sublime snippet ):

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

Vue.use(Vuex);

export const store = new Vuex.Store({
  state: {
    key: value
  }
});

key: value 是狀態數據的占位符。在其他例子中,我們已經使用 counter: 0

在 `main.js` 文件中,我們將執行以下更新(加粗顯示更新的行):

import Vue from 'vue';
import App from './App.vue';

import { store } from './store/store'; new Vue({
  el: '#app',
  store: store,
  template: '<App/>',
  components: { App }
});

更新之后,和之前做的組件一樣,我們可以把 data() 作為狀態,然后我們通過以下三種方式使用或者更新狀態:

  • Getters 可以在模板中靜態的顯示數據。換句話說,getters 可以讀取數據,但不能改變狀態。
  • Mutations 允許更新狀態,但永遠是同步的。Mutations 是 store 中改變狀態數據的唯一方式。
  • Actions 允許異步更新狀態,但是需要使用一個已經存在的 mutation 。如果你需要以特定的順序同時執行不同的 mutations 會非常有用。

如果你以前沒有接觸過,也許很難理解為什么會使用異步狀態的變化,所以先看看理論上它會發生什么,然后再開始下一部分。假如你運行 Tumblr。如果頁面中有大量長時間運行的 gif 圖片。你只想每次載入其中一部分,比如當用戶將頁面滾動到底部 200px 時,加載 20 個圖片。

你需要使用 mutation 展示后面的 20 個。但是現在還沒有后面的 20 個,你不知道何時到達頁面底部。因此,在程序中,創建一個事件來監聽滾動的位置然后觸發相應的操作。

然后,該操作將從數據庫中檢索后面 20 個圖像的 URL,並將 20 個圖片的狀態添加到 mutation 中然后顯示。

本質上,Actions 創建一個請求數據的框架。它們使用一致的方法來應用異步方式中的數據。

最基本的抽象例子

在下面的例子中,展示了每個屬性最基本的實現方式,因此你可以了解如何設置及使用。載荷是可選參數,可以通過它定義正在更新的組件的數值。不用擔心,我們隨后將演示一個實際案例,現在最重要的是了解基本概念。

`store.js`:

export const store = new Vuex.Store({
  state: {
    counter: 0
  },
  // 展示內容, 無法改變狀態
  getters: {
    tripleCounter: state => {
      return state.counter * 3;
    }
  },
  // 改變狀態
  //mutations 永遠是同步的
  mutations: {
    // 顯示傳遞的載荷 payload, 用 num 表示
    increment: (state, num) => {
      state.counter += num;
    }
  }, 
  // 提交 mutation, 這是異步的
  actions: {
    // 顯示傳遞的載荷 payload, 用 asynchNum ( 一個對象 )表示
    asyncDecrement: ({ commit }, asyncNum) => {
      setTimeout(() => {
        // asyncNum 對象可以是靜態值
        commit('decrement', asyncNum.by);
      }, asyncNum.duration);
    }
  }
});

一個很好的功能是我們可以在 mutations 中返回整個狀態對象,但是不必這樣做,我們只使用我們需要的。時間穿梭測試(進入 mutations 中尋找錯誤)仍然可以工作。

在組件中,我們將對 getters 使用 computed (這很重要,因為 value 值已經計算好了),在 methods 中使用 dispatch 來訪問 mutations 和 actions

`app.vue`:

computed: {
  value() {
    return this.$store.getters.value;
  }
},
methods: {
  increment() {
    this.$store.dispatch('increment', 2)
  }
}

或者,您可以使用擴展運算符。我發現如果需要大量 mutations/actions 的時候是非常有用的:

export default {
  // ...
  methods: {
    ...mapActions([
      'increment', // 將 this.increment() 映射到 this.$store.commit('increment')
      'decrement',
      'asyncIncrement'
    ])
  }
}

簡單的實例

讓我們再看一看天氣通知的程序, 在它的 Vuex store 中有少量且簡單的狀態。這是示例代碼的倉庫.

See the Pen Vue Weather Notifier by Sarah Drasner (@sdras) on CodePen.

`store.js`:

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

Vue.use(Vuex);

export const store = new Vuex.Store({
  state: {
    showWeather: false,
    template: 0
  },
    mutations: {
      toggle: state => state.showWeather = !state.showWeather,
      updateTemplate: (state) => {
        state.showWeather = !state.showWeather;
        state.template = (state.template + 1) % 4;
      }
  }
});

我們在這里設置了 showWeather 的狀態,它的初始值為 false ,因為我們不希望任何動畫立即執行,只有當用戶點擊按鈕時才會執行。在 mutations 中,我們可以切換 showWeather 的狀態。

我們也將狀態中的 template 設置為 0 。我們會在每個天氣組件中循環使用這個數字。所以在 mutations 中,我們創建了一個名為 updateTemplate 的方法。它會同時切換 showWeather 的狀態並且更新 template 加 1 后的數值,但是值為 4 時再點擊會變成 0 。

App.vue:

<template>
  <div id="app">
    ...
    <g id="phonebutton" @click="updateTemplate" v-if="!showWeather">
       ...
    </g>

    <transition 
        @leave="leaveDroparea"
        :css="false">
      <g v-if="showWeather">
        <app-droparea v-if="template === 1"></app-droparea>
        <app-windarea v-else-if="template === 2"></app-windarea>
        <app-rainbowarea v-else-if="template === 3"></app-rainbowarea>
        <app-tornadoarea v-else></app-tornadoarea>
      </g>
    </transition>
    ...

  </div>
</template>
<script>
  import Dialog from './components/Dialog.vue';
  ...
  export default {
    computed: {
      showWeather() {
        return this.$store.state.showWeather;
      },
      template() {
        return this.$store.state.template;
      }
    },
    methods: {
      updateTemplate() {
        this.$store.commit('updateTemplate');
      }
    },
    ...
    components: {
      appDialog: Dialog,
      ...
    }
}
</script>

`dialog.vue`:

<script>
export default {
  computed: {
    template() {
      return this.$store.state.template;
    }
  },
  methods: {
    toggle() {
      this.$store.commit('toggle');
    }
  },
  mounted () {
      //enter weather
      const tl = new TimelineMax();
    ...
  }
}
</script>

在上面的代碼中,App 組件使用了 showWeather 依次展示模板, 而 Dialog 組件只切換組件的可見性。在 App.vue 中,我們根據 App <template> 中的模板數值,通過第一章學過的的條件渲染來展示以及隱藏不同的子組件。在 App 中, 我們通過 computed 數值監聽 store 中狀態的變化,使用 methods 中的 toggle()updateTemplate() 提交 store 的 mutations 。

這是一個基本示例,但是你可以了解如何處理有大量狀態的復雜程序,這有利於在在一個地方管理所有的狀態,而不是上下移動組件。尤其當同胞組件之間通信的時候。

如果你想深入了解 Vuex , 可以看這篇 文檔 。你肯能注意到我們在最后一個例子中使用了 <transition> 組件,還有大量動畫。我們會在下一部分展開討論。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM