Tôi tin rằng việc hiểu được các động lực đằng sau Đột biến và Hành động cho phép người ta đánh giá tốt hơn khi nào nên sử dụng cái nào và như thế nào. Nó cũng giải phóng lập trình viên khỏi gánh nặng của sự không chắc chắn trong các tình huống mà "quy tắc" trở nên mờ nhạt. Sau khi suy luận một chút về mục đích tương ứng của họ, tôi đã đi đến kết luận rằng mặc dù có thể có những cách sai để sử dụng Hành động và Đột biến, tôi không nghĩ rằng có một cách tiếp cận kinh điển.
Trước tiên chúng ta hãy cố gắng hiểu lý do tại sao chúng ta thậm chí trải qua cả Đột biến hoặc Hành động.
Tại sao đi qua lò hơi ở nơi đầu tiên? Tại sao không thay đổi trạng thái trực tiếp trong các thành phần?
Nói đúng ra bạn có thể thay đổi state
trực tiếp từ các thành phần của mình. Đây state
chỉ là một đối tượng JavaScript và không có gì kỳ diệu sẽ hoàn nguyên các thay đổi mà bạn thực hiện đối với nó.
// Yes, you can!
this.$store.state['products'].push(product)
Tuy nhiên, bằng cách này, bạn đang phân tán các đột biến trạng thái của mình ở mọi nơi. Bạn mất khả năng chỉ đơn giản là mở một mô-đun duy nhất chứa trạng thái và trong nháy mắt xem loại hoạt động nào có thể được áp dụng cho nó. Có đột biến tập trung giải quyết điều này, mặc dù với chi phí của một số nồi hơi.
// so we go from this
this.$store.state['products'].push(product)
// to this
this.$store.commit('addProduct', {product})
...
// and in store
addProduct(state, {product}){
state.products.push(product)
}
...
Tôi nghĩ rằng nếu bạn thay thế một cái gì đó ngắn bằng nồi hơi, bạn sẽ muốn nồi hơi cũng nhỏ. Do đó, tôi cho rằng đột biến có nghĩa là các trình bao bọc rất mỏng xung quanh các hoạt động tự nhiên trên tiểu bang, gần như không có logic kinh doanh. Nói cách khác, đột biến có nghĩa là chủ yếu được sử dụng như setters.
Bây giờ bạn đã tập trung các đột biến của mình, bạn có cái nhìn tổng quan hơn về các thay đổi trạng thái của mình và vì công cụ của bạn (vue-devtools) cũng nhận thức được vị trí đó nên việc gỡ lỗi dễ dàng hơn. Cũng đáng lưu ý rằng nhiều plugin của Vuex không theo dõi trực tiếp trạng thái để theo dõi các thay đổi, họ thay vào đó dựa vào các đột biến cho điều đó. Do đó, những thay đổi "ngoài giới hạn" đối với nhà nước là vô hình đối với họ.
Vì vậy mutations
, actions
sự khác biệt là gì?
Các hành động, như đột biến, cũng nằm trong mô-đun của cửa hàng và có thể nhận state
đối tượng. Điều đó ngụ ý rằng họ cũng có thể đột biến nó trực tiếp. Vậy quan điểm của việc có cả hai là gì? Nếu chúng ta lý do rằng các đột biến phải được giữ nhỏ và đơn giản, điều đó có nghĩa là chúng ta cần một phương tiện thay thế để chứa logic kinh doanh phức tạp hơn. Hành động là phương tiện để làm điều này. Và vì như chúng tôi đã thiết lập trước đó, vue-devtools và plugin nhận thức được các thay đổi thông qua Đột biến, để duy trì sự nhất quán, chúng tôi nên tiếp tục sử dụng Đột biến từ hành động của mình. Hơn nữa, vì các hành động có nghĩa là bao gồm tất cả và logic mà chúng gói gọn có thể không đồng bộ, nên có nghĩa là Hành động cũng sẽ đơn giản được thực hiện không đồng bộ ngay từ đầu.
Người ta thường nhấn mạnh rằng các hành động có thể không đồng bộ, trong khi các đột biến thường không xảy ra. Bạn có thể quyết định xem sự khác biệt như một dấu hiệu cho thấy các đột biến nên được sử dụng cho bất kỳ điều gì đồng bộ (và hành động cho bất kỳ điều gì không đồng bộ); tuy nhiên, bạn sẽ gặp một số khó khăn nếu bạn cần phải thực hiện nhiều hơn một đột biến (đồng bộ) hoặc nếu bạn cần làm việc với Getter từ các đột biến của mình, vì các hàm đột biến không nhận được Getters cũng như Đột biến làm đối số ...
... dẫn đến một câu hỏi thú vị.
Tại sao Đột biến không nhận được Getters?
Tôi chưa tìm thấy câu trả lời thỏa đáng cho câu hỏi này. Tôi đã thấy một số lời giải thích của nhóm nòng cốt mà tôi thấy tốt nhất. Nếu tôi tóm tắt cách sử dụng của chúng, Getters có nghĩa là các phần mở rộng được tính toán (và thường được lưu trong bộ nhớ cache) về trạng thái. Nói cách khác, về cơ bản chúng vẫn là trạng thái, mặc dù đòi hỏi một số tính toán trả trước và chúng thường chỉ đọc. Đó ít nhất là cách họ được khuyến khích sử dụng.
Do đó, ngăn chặn Đột biến truy cập trực tiếp vào Getters có nghĩa là một trong ba điều cần thiết bây giờ, nếu chúng ta cần truy cập từ một số chức năng trước đây được cung cấp bởi sau: (1) các tính toán trạng thái do Getter cung cấp được sao chép ở đâu đó có thể truy cập được đối với Đột biến (mùi hôi) hoặc (2) giá trị được tính toán (hoặc chính Getter có liên quan) được truyền lại dưới dạng đối số rõ ràng đối với Đột biến (funky) hoặc (3) chính logic của Getter được sao chép trực tiếp trong Đột biến , không có thêm lợi ích của bộ nhớ đệm như được cung cấp bởi Getter (mùi hôi thối).
Sau đây là một ví dụ về (2), trong hầu hết các kịch bản mà tôi gặp phải có vẻ là tùy chọn "ít tệ nhất".
state:{
shoppingCart: {
products: []
}
},
getters:{
hasProduct(state){
return function(product) { ... }
}
}
actions: {
addProduct({state, getters, commit, dispatch}, {product}){
// all kinds of business logic goes here
// then pull out some computed state
const hasProduct = getters.hasProduct(product)
// and pass it to the mutation
commit('addProduct', {product, hasProduct})
}
}
mutations: {
addProduct(state, {product, hasProduct}){
if (hasProduct){
// mutate the state one way
} else {
// mutate the state another way
}
}
}
Đối với tôi, những điều trên dường như không chỉ hơi phức tạp, mà còn hơi "rò rỉ", vì một số mã có trong Hành động rõ ràng được tiết lộ từ logic bên trong của Đột biến.
Theo tôi, đây là một dấu hiệu của sự thỏa hiệp. Tôi tin rằng việc cho phép Đột biến tự động nhận Getters đưa ra một số thách thức. Nó có thể là do thiết kế của Vuex, hoặc dụng cụ (vue-devtools et al), hoặc để duy trì một số khả năng tương thích ngược, hoặc kết hợp tất cả các khả năng đã nêu.
Điều tôi không tin là việc tự mình chuyển Getters cho Đột biến của bạn nhất thiết là một dấu hiệu cho thấy bạn đang làm gì đó sai. Tôi thấy nó chỉ đơn giản là "vá" một trong những thiếu sót của khung.