VueJS: Mutation

Các khóa học qua video:
Python SQL Server PHP C# Lập trình C Java HTML5-CSS3-JavaScript
Học trên YouTube <76K/tháng. Đăng ký Hội viên
Viết nhanh hơn - Học tốt hơn
Giải phóng thời gian, khai phóng năng lực

Cách duy nhất để thực sự thay đổi state trong một store Vuex là bằng cách thực hiện một mutation. Các mutation Vuex rất giống với các sự kiện: mỗi mutation có một kiểu chuỗi và một trình xử lý. Hàm xử lý là nơi chúng ta thực hiện các sửa đổi state thực tế, và nó sẽ nhận state là đối số đầu tiên:

const store = new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    increment (state) {
      // mutate state
      state.count++
    }
  }
})

Bạn không thể gọi trực tiếp một trình xử lý mutation. Hãy suy nghĩ về nó giống như đăng ký sự kiện: "Khi một mutation với loại increment được kích hoạt, hãy gọi trình xử lý này." Để gọi một trình xử lý mutation, bạn cần phải gọi store.commit bằng loại của nó:

store.commit('increment')

#Commit với Payload

Bạn có thể truyền một đối số bổ sung cho store.commit, được gọi là tải trọng cho mutation:

// ...
mutations: {
  increment (state, n) {
    state.count += n
  }
}
store.commit('increment', 10)

Trong hầu hết các trường hợp, tải trọng phải là một đối tượng để nó có thể chứa nhiều trường và mutation được ghi lại cũng sẽ mang tính mô tả hơn:

// ...
mutations: {
  increment (state, payload) {
    state.count += payload.amount
  }
}
store.commit('increment', {
  amount: 10
})

#Commit kiểu đối tượng

Một cách khác để thực hiện một mutation là sử dụng trực tiếp một đối tượng có thuộc tính type:

store.commit({
  type: 'increment',
  amount: 10
})

Khi sử dụng commit kiểu đối tượng, toàn bộ đối tượng sẽ được truyền dưới dạng tải trọng cho trình xử lý mutation, do đó trình xử lý vẫn giữ nguyên:

mutations: {
  increment (state, payload) {
    state.count += payload.amount
  }
}

#Mutation theo quy tắc phản ứng của Vue

Vì state của store Vuex được tạo ra bởi Vue, khi chúng ta thay đổi state, các component Vue quan sát state sẽ tự động cập nhật. Điều này cũng có nghĩa là các mutation Vuex phải tuân theo các cảnh báo tương tự khi làm việc với Vue đơn giản:

  1. Ưu tiên khởi tạo trạng thái ban đầu của cửa hàng với tất cả các trường mong muốn trả trước.

  2. Khi thêm các thuộc tính mới vào một đối tượng, bạn nên:

  • Sử dụng Vue.set(obj, 'newProp', 123), hoặc

  • Thay thế đối tượng đó bằng một đối tượng mới. Ví dụ, bằng cách sử dụng cú pháp lan truyền đối tượng, chúng ta có thể viết nó như sau:

    state.obj = { ...state.obj, newProp: 123 }
    

#Sử dụng hằng số cho các loại mutation

Nó là một mô hình thường thấy để sử dụng hằng số cho các loại mutation trong các triển khai Flux khác nhau. Điều này cho phép mã tận dụng lợi thế của công cụ như linters và đặt tất cả các hằng số trong một tệp cho phép cộng tác viên của bạn xem nhanh những mutation nào có thể xảy ra trong toàn bộ ứng dụng:

// mutation-types.js
export const SOME_MUTATION = 'SOME_MUTATION'
// store.js
import Vuex from 'vuex'
import { SOME_MUTATION } from './mutation-types'

const store = new Vuex.Store({
  state: { ... },
  mutations: {
    // ta có thể sử dụng thuộc tính computed ES2015 để đặc tả tên
    // để sử dụng một hằng như là tên hàm
    [SOME_MUTATION] (state) {
      // mutate state
    }
  }
})

Việc sử dụng hằng số có phần lớn là sở thích - nó có thể hữu ích trong các dự án lớn với nhiều nhà phát triển, nhưng nó hoàn toàn không bắt buộc nếu bạn không thích chúng.

Các mutation phải đồng bộ

Một quy tắc quan trọng cần nhớ là các hàm xử lý mutation phải đồng bộ . Tại sao? Xem xét ví dụ sau:

mutations: {
  someMutation (state) {
    api.callAsyncMethod(() => {
      state.count++
    })
  }
}

Bây giờ hãy tưởng tượng ta đang gỡ lỗi ứng dụng và xem nhật ký mutation của devtool. Đối với mỗi mutation đăng nhập, các devtool sẽ cần phải nắm bắt một ảnh chụp nhanh "trước" và "sau" của state. Tuy nhiên, callback không đồng bộ bên trong ví dụ mutation trên làm cho điều đó là không thể: callback không được gọi khi mutation được commit, và không có cách nào để devtool biết khi callback thực sự được gọi - bất kỳ state mutation nào được thực hiện trong callback về cơ bản là không thể theo dõi được!

#Commit mutation trong component

Bạn có thể thực hiện các mutation trong các component bằng this.$store.commit('xxx'), hoặc sử dụng helper mapMutations để map các phương thức component thành các lời gọi store.commit (yêu cầu đưa store root ):

import { mapMutations } from 'vuex'

export default {
  // ...
  methods: {
    ...mapMutations([
      'increment', // map `this.increment()` to `this.$store.commit('increment')`

      // `mapMutations` also supports payloads:
      'incrementBy' // map `this.incrementBy(amount)` to `this.$store.commit('incrementBy', amount)`
    ]),
    ...mapMutations({
      add: 'increment' // map `this.add()` to `this.$store.commit('increment')`
    })
  }
}

#Bật hành động

Sự bất đồng bộ kết hợp với state mutation có thể khiến cho chương trình của bạn rất khó giải thích. Ví dụ, khi bạn gọi hai phương thức với cả callback async làm thay đổi state, làm thế nào để bạn biết khi nào chúng được gọi và callback nào được gọi đầu tiên? Đây chính là lý do tại sao ta muốn tách riêng hai khái niệm ra. Trong Vuex, mutation là các giao dịch đồng bộ:

store.commit('increment')
// mỗi khi state thay đổi thì mutation "increment" có thể gây ra
// cần phải được thực hiện tại thời điểm hiện thời

Để xử lý các hoạt động không đồng bộ, ta hãy tìm hiểu Action.

» Tiếp: Action
« Trước: Getter
Các khóa học qua video:
Python SQL Server PHP C# Lập trình C Java HTML5-CSS3-JavaScript
Học trên YouTube <76K/tháng. Đăng ký Hội viên
Viết nhanh hơn - Học tốt hơn
Giải phóng thời gian, khai phóng năng lực
Copied !!!