Vuex là gì?
Theo như định nghĩa của trang chủ thì nguyên văn nó như thế này :
Vuex is a state management pattern + library for Vue.js applications. It serves as a centralized store for all the components in an application, with rules ensuring that the state can only be mutated in a predictable fashion. It also integrates with Vue’s official devtools extension to provide advanced features such as zero-config time-travel debugging and state snapshot export / import
Định nghĩ thì có vẻ khó hiểu như vậy nhưng ta có thể hiểu một cách nôm na Vuex là một pattern + library của Vuejs, nó có chức năng như một cái kho chứa tập trung các state của các component trong ứng dụng. Khi chúng ta cần thay đổi gì chỉ cần tương tác trực tiếp với thằng state trên store của Vuex, mà không cần phải thông qua quan hệ giữa các component.
Để hiểu hơn là tại sao phải cần đến Vuex ta sẽ qua một ví dụ sau:
Chúng ta có 2 component là Counter
chứa 2 chức năng là increment
– decrement
và component Result
có chức năng in ra kết quả
- Với trường hợp không sử dụng Vuex thì chúng ta sẽ cần truyền sự kiện
increment
hoặc decrement
từ Counter
lên cho App
và sau đó App
sẽ cập nhật và truyền kết quả xuống cho thằng Result
Đây là trường hợp mới chỉ có một cấp. Vậy nếu cây phân cấp components của ứng dụng là rất nhiều thì điều gì sẽ xảy ra. Trông nó sẽ như thế này :
Sẽ rất là rối và khó quản lý thì ý tưởng của thằng này cũng tương tự như Redux nếu anh em nào đã từng học qua Redux. Thì Vuex cũng vậy, nó sẽ tạo ra một strore chung cho các state để dễ dàng quản lý và thao tác khi có thay đổi:
Cài Đặt
CDN
Nếu bạn đang dùng Vuejs dạng CDN như Jquery :
https://unpkg.com/vuex
nhớ tải cả Vuejs nhá Vuejs
<span class="token tag"><span class="token punctuation"><</span>script <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/path/to/vue.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token punctuation"></</span>script<span class="token punctuation">></span></span>
<span class="token tag"><span class="token punctuation"><</span>script <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/path/to/vuex.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token punctuation"></</span>script<span class="token punctuation">></span></span>
NPM
<span class="token function">npm</span> <span class="token function">install</span> vuex --save
Yarn
Sau khi kiểm tra trong package.json
đã cài đặt thành công Vuex ta tạo 1 folder store
và tạo file store.js
<span class="token keyword">import</span> Vue <span class="token keyword">from</span> <span class="token string">'vue'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> Vuex <span class="token keyword">from</span> <span class="token string">'vuex'</span><span class="token punctuation">;</span>
Vue<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span>Vuex<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">export</span> <span class="token keyword">const</span> store <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Vuex<span class="token punctuation">.</span>Store</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
state<span class="token punctuation">:</span> <span class="token punctuation">{</span>
result<span class="token punctuation">:</span> <span class="token number">0</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
mutations<span class="token punctuation">:</span> <span class="token punctuation">{</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
getters<span class="token punctuation">:</span> <span class="token punctuation">{</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
actions<span class="token punctuation">:</span> <span class="token punctuation">{</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
modules<span class="token punctuation">:</span> <span class="token punctuation">{</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
Vậy là đã có 1 cái store tập trung của Vuex rồi.
Các thành phần và cách sử dụng của chúng
1. State
- Giống như ở mỗi component chúng ta thường có 1 đối tượng
data
chứa các biến của componet thì state ở đây cũng có thể hiểu chính là data của cả ứng dụng. Sử dụng một state duy nhất như thế này sẽ giúp ta đồng bộ được dữ liệu giữa các componet một cách nhanh chóng và chính xác.
state<span class="token punctuation">:</span> <span class="token punctuation">{</span>
result<span class="token punctuation">:</span> <span class="token number">0</span>
<span class="token punctuation">}</span>
Lấy ra giá trị của một biến trong state, thì cũng giống như cách lấy ta giá trị của một attribute trong đối tượng vậy.
<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span>
computed<span class="token punctuation">:</span> <span class="token punctuation">{</span>
<span class="token function">result</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>$store<span class="token punctuation">.</span>state<span class="token punctuation">.</span>result<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
Nếu trong state của chúng ta có nhiều biến và ta chỉ muốn lấy ra một số các biến nhưng lại không muốn gọi từng thứ một như thế kia, thì đừng lo đã có cách đó là sử dụng một helper tên là mapState. Nó sẽ sử dụng toán thử Spread (...Array
) cú pháp này chỉ áp dụng được trong các phiên bản javascript ES6 trở lên thôi nhe.
state
state<span class="token punctuation">:</span> <span class="token punctuation">{</span>
result<span class="token punctuation">:</span> <span class="token number">0</span><span class="token punctuation">,</span>
value<span class="token punctuation">:</span> <span class="token string">'aaa'</span>
<span class="token punctuation">}</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> mapState <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"vuex"</span><span class="token punctuation">;</span>
<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span>
computed<span class="token punctuation">:</span> <span class="token punctuation">{</span>
<span class="token function">localComputed</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/* ... */</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token comment">// mix this into the outer object with the object spread operator
<span class="token operator">...</span><span class="token function">mapState</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string">"result"</span><span class="token punctuation">,</span><span class="token string">"value"</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
c
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token tag"><span class="token punctuation"><</span>template<span class="token punctuation">></span></span>
<span class="token tag"><span class="token punctuation"><</span>div<span class="token punctuation">></span></span>
<span class="token tag"><span class="token punctuation"><</span>p<span class="token punctuation">></span></span>this is Result: {{result}}<span class="token tag"><span class="token punctuation"></</span>p<span class="token punctuation">></span></span>
<span class="token tag"><span class="token punctuation"><</span>p<span class="token punctuation">></span></span>value: {{value}}<span class="token tag"><span class="token punctuation"></</span>p<span class="token punctuation">></span></span>
<span class="token tag"><span class="token punctuation"></</span>div<span class="token punctuation">></span></span>
<span class="token tag"><span class="token punctuation"></</span>template<span class="token punctuation">></span></span>
Vậy là giờ ta có các giá trị result
và value
đã có thể lấy ra sử dụng mà không cần phải lấy từng giá trị một nữa. Đừng quên import mapState không lại bảo sao không chạy.
Sử dụng mapState thì có thể lấy ra giá trị nhưng không thể update được đâu, Docs thì không thấy nói update bằng cách này, nhưng mình thấy từ map
mình cứ nghĩ là nó binding 2 chiều nên mình đã thử update state bằng cách này và không thấy được nên chắc nó chỉ để get state thôi.
2. Getters
- Đôi khi chúng ta có một hàm cần tính toán dựa trên biến trong state mà cái hàm này lại xuất hiện ở nhiều component. Bây giờ chả nhẽ ở mỗi component ta lại lôi cái biến đó ra và tạo hàm tính toán lại ví dụ hàm lọc các công việc phải làm và đếm chúng:
computed<span class="token punctuation">:</span> <span class="token punctuation">{</span>
<span class="token function">doneTodosCount</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>$store<span class="token punctuation">.</span>state<span class="token punctuation">.</span>todos<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span>todo <span class="token operator">=></span> todo<span class="token punctuation">.</span>done<span class="token punctuation">)</span><span class="token punctuation">.</span>length
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
Thì Vuex nó cho phép ta định nghĩa các hàm như thế này trong getters
-
<span class="token keyword">const</span> store <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Vuex<span class="token punctuation">.</span>Store</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
state<span class="token punctuation">:</span> <span class="token punctuation">{</span>
todos<span class="token punctuation">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{</span> id<span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">,</span> text<span class="token punctuation">:</span> <span class="token string">'...'</span><span class="token punctuation">,</span> done<span class="token punctuation">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">{</span> id<span class="token punctuation">:</span> <span class="token number">2</span><span class="token punctuation">,</span> text<span class="token punctuation">:</span> <span class="token string">'...'</span><span class="token punctuation">,</span> done<span class="token punctuation">:</span> <span class="token boolean">false</span> <span class="token punctuation">}</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
getters<span class="token punctuation">:</span> <span class="token punctuation">{</span>
doneTodos<span class="token punctuation">:</span> state <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> state<span class="token punctuation">.</span>todos<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span>todo <span class="token operator">=></span> todo<span class="token punctuation">.</span>done<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
Và lấy cũng đơn giản thôi :
-
store<span class="token punctuation">.</span>getters<span class="token punctuation">.</span>doneTodos <span class="token comment">// -> [{ id: 1, text: '...', done: true }]
Ta có thể sử dụng các hàm trong cùnggetters
với nhau:
-
getters<span class="token punctuation">:</span> <span class="token punctuation">{</span>
doneTodos<span class="token punctuation">:</span> state <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> state<span class="token punctuation">.</span>todos<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span>todo <span class="token operator">=></span> todo<span class="token punctuation">.</span>done<span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
doneTodosCount<span class="token punctuation">:</span> <span class="token punctuation">(</span>state<span class="token punctuation">,</span> getters<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> getters<span class="token punctuation">.</span>doneTodos<span class="token punctuation">.</span>length
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
store<span class="token punctuation">.</span>getters<span class="token punctuation">.</span>doneTodosCount <span class="token comment">// -> 1
Còn ở trong các component khác thì cũng đơn giản không kém
-
computed<span class="token punctuation">:</span> <span class="token punctuation">{</span>
<span class="token function">doneTodosCount</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>$store<span class="token punctuation">.</span>getters<span class="token punctuation">.</span>doneTodosCount
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
Đã có mapSate thì cũng có mapGetters và cách dùng cũng tương tự:
-
<span class="token keyword">import</span> <span class="token punctuation">{</span> mapGetters <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'vuex'</span>
<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span>
<span class="token comment">// ...
computed<span class="token punctuation">:</span> <span class="token punctuation">{</span>
<span class="token comment">// mix the getters into computed with object spread operator
<span class="token operator">...</span><span class="token function">mapGetters</span><span class="token punctuation">(</span><span class="token punctuation">[</span>
<span class="token string">'doneTodosCount'</span><span class="token punctuation">,</span>
<span class="token string">'anotherGetter'</span><span class="token punctuation">,</span>
<span class="token comment">// ...
<span class="token punctuation">]</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
Hoặc là lúc định nghĩ thì một tên nhưng lúc sử dụng ta muốn dùng tên khác cũng chẳng sao đổi được ý mà :
-
<span class="token operator">...</span><span class="token function">mapGetters</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
<span class="token comment">// map `this.doneCount` to `this.$store.getters.doneTodosCount`
doneCount<span class="token punctuation">:</span> <span class="token string">'doneTodosCount'</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
3. Mutations
Theo như Docs thì mutations là cách duy nhất mà ta có thể thay đổi thực sự state trong store. Và cách để kích hoạt một mutations đó là ta sẽ commit một chuỗi String
chính là tên của hàm mà ta muốn gọi trong mutations, nó sẽ nhận state của store làm tham số đầu tiên:
<span class="token keyword">const</span> store <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Vuex<span class="token punctuation">.</span>Store</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
state<span class="token punctuation">:</span> <span class="token punctuation">{</span>
count<span class="token punctuation">:</span> <span class="token number">1</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
mutations<span class="token punctuation">:</span> <span class="token punctuation">{</span>
<span class="token function">increment</span> <span class="token punctuation">(</span>state<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token comment">// mutate state
state<span class="token punctuation">.</span>count<span class="token operator">++</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
store<span class="token punctuation">.</span><span class="token function">commit</span><span class="token punctuation">(</span><span class="token string">'increment'</span><span class="token punctuation">)</span>
Thấy nhà phát triển nói là cách duy nhất để thay đổi thực sự nhưng mình đã thử một cách và vẫn thấy nó thay đổi được đó là :
<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span>
name<span class="token punctuation">:</span> <span class="token string">"counter"</span><span class="token punctuation">,</span>
methods<span class="token punctuation">:</span> <span class="token punctuation">{</span>
<span class="token function">increment</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">this</span><span class="token punctuation">.</span>$store<span class="token punctuation">.</span>state<span class="token punctuation">.</span>count<span class="token operator">++</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
Mình vẫn thấy nó hoạt động nhưng không thấy Docs nói đến kiểu này hay là nó không chuẩn chỉ và có thể gây lỗi hay như thế nào. Nhưng hoi Docs đã viết cách này dùng để thay đổi, nên mình sẽ dùng cách này cho chắc không lại đến lúc có lỗi thì vỡ mồm.
- Ngoài commit mỗi tên của hàm thì bạn cũng có thể truyền thêm một tham số bổ sung, nếu như hàm của bạn có định nghĩa nhiều hơn 1 tham số đầu vào là state
<span class="token comment">// ...
mutations<span class="token punctuation">:</span> <span class="token punctuation">{</span>
<span class="token function">increment</span> <span class="token punctuation">(</span>state<span class="token punctuation">,</span> n<span class="token punctuation">)</span> <span class="token punctuation">{</span>
state<span class="token punctuation">.</span>count <span class="token operator">+=</span> n
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
store<span class="token punctuation">.</span><span class="token function">commit</span><span class="token punctuation">(</span><span class="token string">'increment'</span><span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">)</span>
thường thì người ta sẽ gom các đối số thành một Object để có thể chứa được nhiều biến cần truyền vào hơn
<span class="token comment">// ...
mutations<span class="token punctuation">:</span> <span class="token punctuation">{</span>
<span class="token function">increment</span> <span class="token punctuation">(</span>state<span class="token punctuation">,</span> payload<span class="token punctuation">)</span> <span class="token punctuation">{</span>
state<span class="token punctuation">.</span>count <span class="token operator">+=</span> payload<span class="token punctuation">.</span>amount
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
store<span class="token punctuation">.</span><span class="token function">commit</span><span class="token punctuation">(</span><span class="token string">'increment'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> amount<span class="token punctuation">:</span> <span class="token number">10</span><span class="token punctuation">,</span> total<span class="token punctuation">:</span><span class="token number">50</span> <span class="token punctuation">}</span><span class="token punctuation">)</span>
Còn một cách nữa đó là gom cả tên hàm cần gọi và biến cần truyền vào 1 Object với tên hàm để là type
vậy là mutations sẽ hiểu và thực hiện mà không cần thay đổi số tham số của hàm
store<span class="token punctuation">.</span><span class="token function">commit</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
type<span class="token punctuation">:</span> <span class="token string">'increment'</span><span class="token punctuation">,</span>
amount<span class="token punctuation">:</span> <span class="token number">10</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
mutations<span class="token punctuation">:</span> <span class="token punctuation">{</span>
<span class="token function">increment</span> <span class="token punctuation">(</span>state<span class="token punctuation">,</span> payload<span class="token punctuation">)</span> <span class="token punctuation">{</span>
state<span class="token punctuation">.</span>count <span class="token operator">+=</span> payload<span class="token punctuation">.</span>amount
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
Muntations thì cũng tuân theo Reactivity Rules của Vue. Nên nếu sau khi state đã được khởi tạo ta muốn thêm một biến mới vào trong state, thì ta cần khai báo cho Vue biết rằng ta có một mới muốn thêm vào hoặc là thay thế toàn bộ
Vue<span class="token punctuation">.</span><span class="token keyword">set</span><span class="token punctuation">(</span>obj<span class="token punctuation">,</span> <span class="token string">'newProp'</span><span class="token punctuation">,</span> <span class="token number">123</span><span class="token punctuation">)</span>
<span class="token constant">OR</span>
state<span class="token punctuation">.</span>obj <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token operator">...</span>state<span class="token punctuation">.</span>obj<span class="token punctuation">,</span> newProp<span class="token punctuation">:</span> <span class="token number">123</span> <span class="token punctuation">}</span>
- Ta có thể dùng các mutations với dạng các hằng số. Điều này sẽ rất giúp ích cho việc đồng bộ tên hàm cũng như phù hợp cho các dự án lớn với nhiều bên tham gia
<span class="token comment">// mutation-types.js
<span class="token keyword">export</span> <span class="token keyword">const</span> <span class="token constant">SOME_MUTATION</span> <span class="token operator">=</span> <span class="token string">'SOME_MUTATION'</span>
<span class="token comment">// store.js
<span class="token keyword">import</span> Vuex <span class="token keyword">from</span> <span class="token string">'vuex'</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> <span class="token constant">SOME_MUTATION</span> <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'./mutation-types'</span>
<span class="token keyword">const</span> store <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Vuex<span class="token punctuation">.</span>Store</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
state<span class="token punctuation">:</span> <span class="token punctuation">{</span> <span class="token operator">...</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
mutations<span class="token punctuation">:</span> <span class="token punctuation">{</span>
<span class="token comment">// we can use the ES2015 computed property name feature
<span class="token comment">// to use a constant as the function name
<span class="token punctuation">[</span><span class="token constant">SOME_MUTATION</span><span class="token punctuation">]</span> <span class="token punctuation">(</span>state<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token comment">// mutate state
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
nhưng nó chỉ là một tùy chọn thôi nha bạn không nhất thiết cứ phải sử dụng nó đâu
=> Một điều cần nhớ đó là thằng mutations này sẽ chạy đồng bộ nên bạn cần cẩn thận khi sử dụng nó, không lại dối tung lên khi kết hợp nó với các hàm bất đồng bộ và không hiểu sao nó lại không chạy.
- Giống như 2 thằng trên thì mutations cũng có helper đó là mapMutations
<span class="token keyword">import</span> <span class="token punctuation">{</span> mapMutations <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'vuex'</span>
<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span>
<span class="token comment">// ...
methods<span class="token punctuation">:</span> <span class="token punctuation">{</span>
<span class="token operator">...</span><span class="token function">mapMutations</span><span class="token punctuation">(</span><span class="token punctuation">[</span>
<span class="token string">'increment'</span><span class="token punctuation">,</span> <span class="token comment">// map `this.increment()` to `this.$store.commit('increment')`
<span class="token comment">// `mapMutations` also supports payloads:
<span class="token string">'incrementBy'</span> <span class="token comment">// map `this.incrementBy(amount)` to `this.$store.commit('incrementBy', amount)`
<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token operator">...</span><span class="token function">mapMutations</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
add<span class="token punctuation">:</span> <span class="token string">'increment'</span> <span class="token comment">// map `this.add()` to `this.$store.commit('increment')`
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
4. Actions
- Actions cũng giống như mutations nhưng nó khác ở hai điểm:
Ví dụ đơn giản
- Actions không trực tiếp thay đổi state trong store mà nó sẽ thông qua mutations để thay đổi
- Nó có thể chứa các hàm bất đồng bộ
<span class="token keyword">const</span> store <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Vuex<span class="token punctuation">.</span>Store</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
state<span class="token punctuation">:</span> <span class="token punctuation">{</span>
count<span class="token punctuation">:</span> <span class="token number">0</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
mutations<span class="token punctuation">:</span> <span class="token punctuation">{</span>
<span class="token function">increment</span> <span class="token punctuation">(</span>state<span class="token punctuation">)</span> <span class="token punctuation">{</span>
state<span class="token punctuation">.</span>count<span class="token operator">++</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
actions<span class="token punctuation">:</span> <span class="token punctuation">{</span>
<span class="token function">increment</span> <span class="token punctuation">(</span>context<span class="token punctuation">)</span> <span class="token punctuation">{</span>
context<span class="token punctuation">.</span><span class="token function">commit</span><span class="token punctuation">(</span><span class="token string">'increment'</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
Có thể sử dụng hàm argument destructuring để tạo
actions<span class="token punctuation">:</span> <span class="token punctuation">{</span>
<span class="token function">increment</span> <span class="token punctuation">(</span><span class="token punctuation">{</span> commit <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token function">commit</span><span class="token punctuation">(</span><span class="token string">'increment'</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
- Cách kích hoạt 1 actions khi ở component khác
store<span class="token punctuation">.</span><span class="token function">dispatch</span><span class="token punctuation">(</span><span class="token string">'increment'</span><span class="token punctuation">)</span>
chúng ta cũng có thể truyền thêm một tham số
<span class="token comment">// dispatch with a payload
store<span class="token punctuation">.</span><span class="token function">dispatch</span><span class="token punctuation">(</span><span class="token string">'incrementAsync'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
amount<span class="token punctuation">:</span> <span class="token number">10</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token comment">// dispatch with an object
store<span class="token punctuation">.</span><span class="token function">dispatch</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
type<span class="token punctuation">:</span> <span class="token string">'incrementAsync'</span><span class="token punctuation">,</span>
amount<span class="token punctuation">:</span> <span class="token number">10</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
Và lại thêm một cái helper nữa đó là mapActions
<span class="token keyword">import</span> <span class="token punctuation">{</span> mapActions <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'vuex'</span>
<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span>
<span class="token comment">// ...
methods<span class="token punctuation">:</span> <span class="token punctuation">{</span>
<span class="token operator">...</span><span class="token function">mapActions</span><span class="token punctuation">(</span><span class="token punctuation">[</span>
<span class="token string">'increment'</span><span class="token punctuation">,</span> <span class="token comment">// map `this.increment()` to `this.$store.dispatch('increment')`
<span class="token comment">// mapActions` also supports payloads:
<span class="token string">'incrementBy'</span> <span class="token comment">// map `this.incrementBy(amount)` to `this.$store.dispatch('incrementBy', amount)`
<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token operator">...</span><span class="token function">mapActions</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
add<span class="token punctuation">:</span> <span class="token string">'increment'</span> <span class="token comment">// map `this.add()` to `this.$store.dispatch('increment')`
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
5. Modules
- Bây giờ mới có vài hàm thì nhét hết vào file
store.js
được chứ về sau mỗi biến trong state lại có hàng tá hàm thì rất rối. Thì Vuex đã hỗ trợ một tùy chỉnh đó là modules, ta có thể tách các hàm có chung mục đích ra một file như sau:
<span class="token keyword">const</span> moduleA <span class="token operator">=</span> <span class="token punctuation">{</span>
state<span class="token punctuation">:</span> <span class="token punctuation">{</span> <span class="token operator">...</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
mutations<span class="token punctuation">:</span> <span class="token punctuation">{</span> <span class="token operator">...</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
actions<span class="token punctuation">:</span> <span class="token punctuation">{</span> <span class="token operator">...</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
getters<span class="token punctuation">:</span> <span class="token punctuation">{</span> <span class="token operator">...</span> <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token keyword">const</span> moduleB <span class="token operator">=</span> <span class="token punctuation">{</span>
state<span class="token punctuation">:</span> <span class="token punctuation">{</span> <span class="token operator">...</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
mutations<span class="token punctuation">:</span> <span class="token punctuation">{</span> <span class="token operator">...</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
actions<span class="token punctuation">:</span> <span class="token punctuation">{</span> <span class="token operator">...</span> <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token keyword">const</span> store <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Vuex<span class="token punctuation">.</span>Store</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
modules<span class="token punctuation">:</span> <span class="token punctuation">{</span>
a<span class="token punctuation">:</span> moduleA<span class="token punctuation">,</span>
b<span class="token punctuation">:</span> moduleB
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
store<span class="token punctuation">.</span>state<span class="token punctuation">.</span>a <span class="token comment">// -> `moduleA`'s state
store<span class="token punctuation">.</span>state<span class="token punctuation">.</span>b <span class="token comment">// -> `moduleB`'s state
- Nếu bạn muốn nhóm các kiểu mutation/action có chung mục đích sử dụng lại với nhau ta có thể sử dụng đinh nghĩa Namespacing với thuộc tính
namespaced: true
<span class="token keyword">const</span> store <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Vuex<span class="token punctuation">.</span>Store</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
modules<span class="token punctuation">:</span> <span class="token punctuation">{</span>
account<span class="token punctuation">:</span> <span class="token punctuation">{</span>
namespaced<span class="token punctuation">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
<span class="token comment">// module assets
state<span class="token punctuation">:</span> <span class="token punctuation">{</span> <span class="token operator">...</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token comment">// module state is already nested and not affected by namespace option
getters<span class="token punctuation">:</span> <span class="token punctuation">{</span>
<span class="token function">isAdmin</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token operator">...</span> <span class="token punctuation">}</span> <span class="token comment">// -> getters['account/isAdmin']
<span class="token punctuation">}</span><span class="token punctuation">,</span>
actions<span class="token punctuation">:</span> <span class="token punctuation">{</span>
<span class="token function">login</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token operator">...</span> <span class="token punctuation">}</span> <span class="token comment">// -> dispatch('account/login')
<span class="token punctuation">}</span><span class="token punctuation">,</span>
mutations<span class="token punctuation">:</span> <span class="token punctuation">{</span>
<span class="token function">login</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token operator">...</span> <span class="token punctuation">}</span> <span class="token comment">// -> commit('account/login')
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token comment">// nested modules
modules<span class="token punctuation">:</span> <span class="token punctuation">{</span>
<span class="token comment">// inherits the namespace from parent module
myPage<span class="token punctuation">:</span> <span class="token punctuation">{</span>
state<span class="token punctuation">:</span> <span class="token punctuation">{</span> <span class="token operator">...</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
getters<span class="token punctuation">:</span> <span class="token punctuation">{</span>
<span class="token function">profile</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token operator">...</span> <span class="token punctuation">}</span> <span class="token comment">// -> getters['account/profile']
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token comment">// further nest the namespace
posts<span class="token punctuation">:</span> <span class="token punctuation">{</span>
namespaced<span class="token punctuation">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
state<span class="token punctuation">:</span> <span class="token punctuation">{</span> <span class="token operator">...</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
getters<span class="token punctuation">:</span> <span class="token punctuation">{</span>
<span class="token function">popular</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token operator">...</span> <span class="token punctuation">}</span> <span class="token comment">// -> getters['account/posts/popular']
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
Và khi binding với các helpers mà có sử dụng namespace trông nó sẽ như thế này :
computed<span class="token punctuation">:</span> <span class="token punctuation">{</span>
<span class="token operator">...</span><span class="token function">mapState</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
a<span class="token punctuation">:</span> state <span class="token operator">=></span> state<span class="token punctuation">.</span>some<span class="token punctuation">.</span>nested<span class="token punctuation">.</span>module<span class="token punctuation">.</span>a<span class="token punctuation">,</span>
b<span class="token punctuation">:</span> state <span class="token operator">=></span> state<span class="token punctuation">.</span>some<span class="token punctuation">.</span>nested<span class="token punctuation">.</span>module<span class="token punctuation">.</span>b
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
methods<span class="token punctuation">:</span> <span class="token punctuation">{</span>
<span class="token operator">...</span><span class="token function">mapActions</span><span class="token punctuation">(</span><span class="token punctuation">[</span>
<span class="token string">'some/nested/module/foo'</span><span class="token punctuation">,</span> <span class="token comment">// -> this['some/nested/module/foo']()
<span class="token string">'some/nested/module/bar'</span> <span class="token comment">// -> this['some/nested/module/bar']()
<span class="token punctuation">]</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
Hoặc là đưa phần string namespace vào làm đối số đầu tiên
computed<span class="token punctuation">:</span> <span class="token punctuation">{</span>
<span class="token operator">...</span><span class="token function">mapState</span><span class="token punctuation">(</span><span class="token string">'some/nested/module'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
a<span class="token punctuation">:</span> state <span class="token operator">=></span> state<span class="token punctuation">.</span>a<span class="token punctuation">,</span>
b<span class="token punctuation">:</span> state <span class="token operator">=></span> state<span class="token punctuation">.</span>b
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
methods<span class="token punctuation">:</span> <span class="token punctuation">{</span>
<span class="token operator">...</span><span class="token function">mapActions</span><span class="token punctuation">(</span><span class="token string">'some/nested/module'</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>
<span class="token string">'foo'</span><span class="token punctuation">,</span> <span class="token comment">// -> this.foo()
<span class="token string">'bar'</span> <span class="token comment">// -> this.bar()
<span class="token punctuation">]</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
Còn nếu không muốn dùng lại những string namespace đó nhiều lần bạn có thể sử dụng createNamespacedHelpers
. Nó sẽ trả về một đối tượng liên kết với các helper mà bạn muốn.
<span class="token keyword">import</span> <span class="token punctuation">{</span> createNamespacedHelpers <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'vuex'</span>
<span class="token keyword">const</span> <span class="token punctuation">{</span> mapState<span class="token punctuation">,</span> mapActions <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">createNamespacedHelpers</span><span class="token punctuation">(</span><span class="token string">'some/nested/module'</span><span class="token punctuation">)</span>
<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span>
computed<span class="token punctuation">:</span> <span class="token punctuation">{</span>
<span class="token comment">// look up in `some/nested/module`
<span class="token operator">...</span><span class="token function">mapState</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
a<span class="token punctuation">:</span> state <span class="token operator">=></span> state<span class="token punctuation">.</span>a<span class="token punctuation">,</span>
b<span class="token punctuation">:</span> state <span class="token operator">=></span> state<span class="token punctuation">.</span>b
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
methods<span class="token punctuation">:</span> <span class="token punctuation">{</span>
<span class="token comment">// look up in `some/nested/module`
<span class="token operator">...</span><span class="token function">mapActions</span><span class="token punctuation">(</span><span class="token punctuation">[</span>
<span class="token string">'foo'</span><span class="token punctuation">,</span>
<span class="token string">'bar'</span>
<span class="token punctuation">]</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
Ngoài ra bạn có thể đăng ký các module sau khi store đã được tạo với method store.registerModule
<span class="token comment">// register a module `myModule`
store<span class="token punctuation">.</span><span class="token function">registerModule</span><span class="token punctuation">(</span><span class="token string">'myModule'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
<span class="token comment">// ...
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token comment">// register a nested module `nested/myModule`
store<span class="token punctuation">.</span><span class="token function">registerModule</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string">'nested'</span><span class="token punctuation">,</span> <span class="token string">'myModule'</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
<span class="token comment">// ...
<span class="token punctuation">}</span><span class="token punctuation">)</span>
Cấu trúc ứng dụng
Theo hướng dẫn thì cấu trúc của ứng dụng Vuex nên như này
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
├── index.html
├── main.js
├── api
│ └── ... # abstractions for making API requests
├── components
│ ├── App.vue
│ └── ...
└── store
├── index.js # where we assemble modules and export the store
├── actions.js # root actions
├── mutations.js # root mutations
└── modules
├── cart.js # cart module
└── products.js # products module
|
Để quản lý chặt chẽ hơn thì chúng ta nên kết hợp vừa tách thành các modules cho những đối tượng chứa nhiều hàm trong mutations, actions, getters và vừa tách ra các file actions, mutations dùng cho các đối tượng ít hàm hơn.
Nguồn: Devmaster Academy via Viblo