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

1. Cây trạng thái duy nhất

Vuex sử dụng cây trạng thái duy nhất - đúng vậy, chỉ với một đối tượng duy nhất chứa toàn bộ trạng thái của ứng dụng ở mọi cấp độ. Từ đây, nó hoạt động như một "nguồn dữ liệu duy nhất (SSOT)". Điều này cũng có nghĩa là mỗi ứng dụng sẽ chỉ chứa một instance của store. Cây trạng thái duy nhất cho phép chúng ta xác định trực tiếp bất kỳ đoạn trạng thái cụ thể nào, và trong quá trình gỡ lỗi, chúng ta cũng có thể dễ dàng lấy ảnh chụp toàn bộ trạng thái hiện tại của ứng dụng.

Cây trạng thái duy nhất không mâu thuẫn với việc module hóa - trong các chương sau, chúng ta sẽ thảo luận về cách phân phối trạng thái và các sự kiện thay đổi trạng thái đến các module con.

Dữ liệu được lưu trữ trong Vuex và dữ liệu trong instance Vue tuân theo các quy tắc tương tự, ví dụ đối tượng trạng thái phải là đối tượng thuần túy (plain).

Truy cập trạng thái Vuex trong các component Vue

Làm thế nào để chúng ta hiển thị trạng thái trong các component Vue? Vì trạng thái lưu trữ trong Vuex là phản ứng (reactive), cách đơn giản nhất để đọc trạng thái từ instance store là trả về trạng thái đó trong một thuộc tính tính toán (computed property):

// Tạo một component DemoSo
const DemoSo = {
  template: `<div>{{ soDem }}</div>`,
  computed: {
    soDem() {
      return khoTrangThai.state.soDem
    }
  }
}

Mỗi khi khoTrangThai.state.soDem thay đổi, thuộc tính tính toán sẽ được tính toán lại và kích hoạt cập nhật DOM liên quan.

Tuy nhiên, mô hình này khiến các component phụ thuộc vào singleton trạng thái toàn cục. Trong hệ thống xây dựng module hóa, cần phải nhập khẩu thường xuyên trong mỗi component cần sử dụng state, và khi kiểm thử component, cần phải mô phỏng trạng thái.

Vuex cung cấp một cơ chế thông qua tùy chọn store để "tiêm" trạng thái từ component gốc vào mọi component con (cần gọi Vue.use(Vuex)):

const ungDung = new Vue({
  el: '#app',
  // Cung cấp đối tượng khoTrangThai cho tùy chọn "store", điều này có thể tiêm instance store vào tất cả các component con
  khoTrangThai,
  components: { DemoSo },
  template: `
    <div class="ung-dung">
      <demo-so></demo-so>
    </div>
  `
})

Bằng cách đăng ký tùy chọn store trong instance gốc, instance store sẽ được tiêm vào tất cả các component con dưới component gốc, và các component con có thể truy cập thông qua this.$khoTrangThai. Hãy cập nhật thực hiện của DemoSo:

const DemoSo = {
  template: `<div>{{ soDem }}</div>`,
  computed: {
    soDem() {
      return this.$khoTrangThai.state.soDem
    }
  }
}

Hàm trợ giúp mapTrangThai

Khi một component cần lấy nhiều trạng thái, việc khai báo tất cả các trạng thái này như các thuộc tính tính toán sẽ có sự lặp lại và thừa thãi. Để giải quyết vấn đề này, chúng ta có thể sử dụng hàm trợ giúp mapTrangThai để tạo ra các thuộc tính tính toán giúp chúng ta gõ ít hơn:

// Trong phiên bản xây dựng riêng, hàm trợ giúp là Vuex.mapTrangThai
import { mapTrangThai } from 'vuex'

export default {
  // ...
  computed: mapTrangThai({
    // Hàm mũi tên làm cho mã ngắn gọn hơn
    soDem: state => state.soDem,
    
    // Truyền tham số chuỗi 'soDem' tương đương với `state => state.soDem`
    tenKhac: 'soDem',
    
    // Để có thể sử dụng `this` để lấy trạng thái cục bộ, phải sử dụng hàm thông thường
    soCongThem(state) {
      return state.soDem + this.soCucBo
    }
  })
}

Khi tên của thuộc tính tính toán được ánh xạ trùng với tên nút con của state, chúng ta cũng có thể truyền một mảng chuỗi cho mapTrangThai:

computed: mapTrangThai([
  // Ánh xạ this.soDem thành khoTrangThai.state.soDem
  'soDem'
])

Toán tử trải rộng đối tượng

Hàm mapTrangThai trả về một đối tượng. Làm thế nào để trộn nó với các thuộc tính tính toán cục bộ? Thông thường, chúng ta cần sử dụng một hàm tiện ích để hợp nhất nhiều đối tượng thành một, để chúng ta có thể chuyển đối tượng cuối cùng cho thuộc tính computed. Nhưng kể từ khi có toán tử trải rộng đối tượng, chúng ta có thể đơn giản hóa cách viết rất nhiều:

computed: {
  tinhToanCucBo() { /* ... */ },
  // Sử dụng toán tử trải rộng đối tượng để trộn đối tượng này vào đối tượng bên ngoài
  ...mapTrangThai({
    // ...
  })
}

Component vẫn giữ trạng thái cục bộ

Sử dụng Vuex không có nghĩa là bạn cần đặt tất cả trạng thái vào Vuex. Mặc dù việc đặt tất cả trạng thái vào Vuex sẽ làm cho sự thay đổi trạng thái trở nên rõ ràng và dễ gỡ lỗi hơn, nhưng nó cũng làm cho mã trở nên dài dòng và không trực quan. Nếu một số trạng thái hoàn toàn thuộc về một component duy nhất, tốt nhất vẫn nên coi đó là trạng thái cục bộ của component. Bạn nên cân nhắc và quyết định dựa trên nhu cầu phát triển ứng dụng của mình.

Thẻ: Vuex Vue.js quản-lý-trạng-thái reactivity JavaScript

Đăng vào ngày 23 tháng 5 lúc 20:36