Chủ Nhật, 22/01/2017 | 00:00 GMT+7

Quản lý trạng thái Vue.js với Vuex


Nếu bạn đã thực hiện bất kỳ sự phát triển nghiêm túc nào của các ứng dụng trang đơn quy mô lớn, có lẽ bạn đã quen thuộc với khái niệm quản lý nhà nước, đặc biệt là kiến trúc Flux, được Facebook phổ biến thông qua Redux. Ở đây ta sẽ chỉ cho bạn cách triển khai một mẫu như vậy trong ứng dụng Vue của bạn bằng Vuex .

Sự chuẩn bị

Cài đặt

Vuex , trong khi là một gói chính thức cho Vue, không được tích hợp sẵn. Bạn phải quyết định xem bạn có cần nó trong ứng dụng của bạn hay không, sau đó cài đặt nó cho phù hợp thông qua sợi hoặc npm .

# Yarn
$ yarn add vuex
# NPM
$ npm install vuex --save

Sau đó, trong gói khởi động ứng dụng của bạn, hãy đảm bảo bạn bật plugin Vuex .

main.js
import Vue from 'vue';
import Vuex from 'vuex';
import App from 'App.vue';

Vue.use(Vuex);

new Vue({
  el: '#app',
  render: h => h(App)
});

Tạo một cửa hàng

Để có thể tận dụng các tính năng Vuex cung cấp, trước tiên bạn cần tạo một cửa hàng. Cửa hàng về cơ bản là một đối tượng phản ứng toàn cục tuân theo các mẫu phản ứng Vue bình thường. Nó không thể được truy cập hoặc sửa đổi trực tiếp đảm bảo trạng thái nhất quán và cho phép dễ dàng theo dõi các thay đổi. Đây là cách bạn tạo một cái.

store.js
export const store = new Vuex.Store({
  state: {
    safelyStoredNumber: 0
  }
});

Bây giờ, để truy cập cửa hàng, bạn sẽ phải nhập nó vào tất cả các thành phần của bạn hoặc bạn có thể đưa nó vào version Vue root để nó tự động được đưa vào mọi thành phần khác trong ứng dụng của bạn dưới dạng $ store . Ta sẽ sử dụng phương pháp thứ hai cho phần còn lại của bài viết này.

main.js
import Vue from 'vue';
import Vuex from 'vuex';
import App from 'App.vue';
import { store } from './store.js';

Vue.use(Vuex);

new Vue({
  store,
  el: '#app',
  render: h => h(App)
});

Trạng thái truy cập

Ngay bây giờ bạn không thể thực sự làm bất cứ điều gì với cửa hàng. Đó là một hộp đen riêng biệt cho trạng thái của bạn, ngăn chặn bất kỳ hành động bất ngờ nào để đọc hoặc thao túng nó. Để đọc dữ liệu từ một cửa hàng, bạn cần tạo một getter .

Sử dụng Getters

Getter chỉ đơn giản là một hàm trong cửa hàng của bạn lấy một đối tượng trạng thái và trả về một giá trị từ nó. Trong các thành phần của bạn, nó có thể được truy cập thông qua . $ Store.getters.property dưới dạng một thuộc tính được tính toán, không phải là một hàm. Nếu một getter cần một tham số, nó có thể trả về một hàm thứ hai nhận một tham số.

store.js
export const store = new Vuex.Store({
  state: {
    safelyStoredNumber: 0
  },
  getters: {
    safelyStoredNumber: state => state.safelyStoredNumber,
    storedNumberMatches(state) {
      return matchNumber => {
          return state.safelyStoredNumber === matchNumber;
      }
    }
    // Shorthand:
    // storedNumberMatches: state => matchNumber => state.safelyStoredNumbers === matchNumber
  }
});

Tuy nhiên, cách dễ dàng để truy cập getters trong thành phần của bạn là thông qua phương thức trợ giúp mapGetters của Vuex . Điều này cho phép bạn gắn getters vào các thuộc tính được tính toán cấp cao nhất trong thành phần của bạn.

mapGetters có thể lấy một đối tượng nếu bạn muốn đổi tên getters trong thành phần của bạn .

App.vue
<template>
  <p>The safely stored number: {{safelyStoredNumber}}<p>
</template>

<script>
import { mapGetters } from 'vuex'

export default {
  computed: {
    ...mapGetters([
      // Mounts the "safelyStoredNumber" getter to the scope of your component.
      'safelyStoredNumber'
    ])
  }
}
</script>

Đang sửa đổi trạng thái

Đột biến đồng bộ

Việc sửa đổi trực tiếp trạng thái trong Vuex được thực hiện bằng cách gọi một hàm được gọi là đột biến . Một đột biến được chuyển qua trạng thái hiện tại và một trọng tải tùy chọn. Tải trọng có thể là bất kỳ đối tượng nào. Các đột biến phải đồng bộ và không được trả về giá trị. Chúng được dùng trực tiếp bằng cách chạy tệp này. $ Store.commit ('mutName', payload) .

store.js
export const store = new Vuex.Store({
  state: {
    safelyStoredNumber: 0
  },
  ...
  mutations: {
    incrementStoredNumber(state) {
      state.safelyStoredNumber++;
    },
    setStoredNumber(state, newNumber) {
      // newNumber is the payload passed in.
      state.safelyStoredNumber = newNumber;
    }
  }
});

Như với getters, Vuex cũng có một phương pháp thuận tiện cho các đột biến trong thành phần, phương thức trợ giúp mapMutations . Điều này cho phép bạn gắn các đột biến làm phương thức trong thành phần của bạn .

App.vue
<template>
  <p>The safely stored number: {{safelyStoredNumber}}<p>
</template>

<script>
import { mapMutations } from 'vuex'

export default {
  ...
  methods: {
    ...mapMutations([
      // Mounts the "incrementStoredNumber" mutation to `this.incrementStoredNumber()`.
      'incrementStoredNumber',
      // Mounts the "setStoredNumber" mutation to `this.setStoredNumber(newNumber)`.
      'setStoredNumber'
    ])
  }
}
</script>

Hành động không đồng bộ

Trong các ứng dụng phức tạp hơn, có khả năng bạn cần thực hiện một số hành động không đồng bộ để sửa đổi trạng thái. Vuex xử lý điều này bằng các hành động . Chúng cũng được xác định trên đối tượng trạng thái của bạn và được chuyển toàn bộ ngữ cảnh trạng thái, cho phép chúng truy cập vào getters và thực hiện các đột biến. Chúng được mong đợi (nhưng không bắt buộc) trả về một lời hứa cho biết trạng thái hoàn thành. Sử dụng ES2017 async / await , bạn có thể viết các hành động async rất ngắn gọn nhưng dễ hiểu. Các hành động được sử dụng trong các thành phần trực tiếp với cái này. $ Store.dispatch ('actionName', payload) .then (response => {}) .

Để sửa đổi trạng thái trong một hành động, hãy sử dụng context.commit ('mutName', payload) . Nhiều đột biến được phép bên trong một hành động.

store.js
import myRemoteService from './my-remote-service.js'

export const store = new Vuex.Store({
  state: {
    safelyStoredNumber: 0
  },
  ...
  actions: {
    async setNumberToRemoteValue(context) {
      // Commits the 'setStoredNumber' mutation with the value of whatever myRemoteService.getRemoteValue() resolves through a promise.
      context.commit('setStoredNumber', await myRemoteService.getRemoteValue());
      return Promise.resolve();
    },
  }
});

Nếu bạn không quen với async / await , hãy nghiêm túc đọc về nó. Thật tuyệt vời. Điểm ngắn gọn của nó là, nó tạm dừng việc thực thi chức năng hiện tại cho đến khi lời hứa được chờ đợi giải quyết, cho phép bạn về cơ bản sử dụng độ phân giải của lời hứa làm biến mà không cần tất cả các bảng soạn sẵn bổ sung thông thường.

Phương thức tiện lợi Vuex cho các hành động , (được đặt tên có thể dự đoán là mapActions ) được sử dụng giống như phương pháp cho các đột biến.

App.vue
<template>
  <p>The safely stored number: {{safelyStoredNumber}}<p>
</template>

<script>
import { mapActions } from 'vuex'

export default {
  ...
  methods: {
    ...mapActions([
      // Mounts the "setNumberToRemoteValue" action to `this.setNumberToRemoteValue()`.
      'setNumberToRemoteValue',
    ])
  }
}
</script>

Modularizing

Một cửa hàng duy nhất sẽ ổn nếu bạn chỉ làm việc với một tập hợp dữ liệu nhỏ, nhưng chắc chắn đến một lúc nào đó, bạn cần chia danh sách các hành động, đột biến và getters liên tục phát triển của bạn thành các phần riêng biệt. Rất may, Vuex cũng cung cấp một hệ thống để làm điều này. Mô-đun . Mặc dù có cái tên đáng sợ, một module chỉ đơn giản là một đối tượng bình thường với các thuộc tính trạng thái , nhận dạng , đột biếnhành động . Bạn có thể dễ dàng tạo một cái bằng cách làm như sau:

my-store-module.js
export const myModule = {
  // This makes your getters, mutations, and actions accessed by, eg: 'myModule/myModularizedNumber' instead of mounting getters, mutations, and actions to the root namespace.
  namespaced: true,
  state: {
    myModularizedNumber: 0
  },
  getters: {
    myModularizedNumber: state => state.myModularizedNumber
  },
  mutations: {
    setModularizedNumber(state, newNumber) {
      state.myModularizedNumber = newNumber
    }
  }
}
store.js
import { myModule } from './my-store-module.js';

export const store = new Vuex.Store({
  modules: {
      myModule
  },
  state: {
    safelyStoredNumber: 0
  },
  ...
});

Bạn có thể lồng các module vào các module , tùy ý bạn. Ngoài ra, mapGetters, mapMutations và mapActions đều có thể nhận đối số đầu tiên, là không gian tên module , để giúp bạn không phải viết mã như thế này:

...mapGetters([
  'myModule/nestedModule/subNestedModule/exampleGetter',
  'myModule/nestedModule/subNestedModule/anotherGetter',
])

Thay vào đó, bạn có thể viết nó như thế này:

...mapGetters('myModule/testedModule/subNestedModule', [
  'exampleGetter',
  'anotherGetter'
])

Hy vọng điều đó giúp giải tỏa quản lý nhà nước với Vuex cho bạn!


Tags:

Các tin liên quan