Trong Vue 3 có hai Reactivity API mà dễ làm newbie gây nhầm lẫn khi sử dụng đó là ref và reactive. Bài viết này mình sẽ hướng dẫn cách sử dụng 2 API trên, kèm một số so sánh với Vue 2 cho những ai mới chuyển từ Vue 2 lên Vue 3.
Ref
Ví dụ đơn giản khi thay đổi một reactive state bằng Vue 2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<span class="token operator"><</span>template<span class="token operator">></span> <span class="token operator"><</span>h1<span class="token operator">></span>Count<span class="token operator">:</span> <span class="token punctuation">{</span><span class="token punctuation">{</span> count <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span>h1<span class="token operator">></span> <span class="token operator"><</span>button @click<span class="token operator">=</span><span class="token string">"increaseCount"</span><span class="token operator">></span>Increase Count<span class="token operator"><</span><span class="token operator">/</span>button<span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>template<span class="token operator">></span> <span class="token operator"><</span>script<span class="token operator">></span> <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span> <span class="token function">data</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 punctuation">{</span> count<span class="token operator">:</span> <span class="token number">0</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> methods<span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token function">increaseCount</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>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><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token operator"><</span><span class="token operator">/</span>script<span class="token operator">></span> |
Chức năng tương tự nhưng sử dụng ref() trong Vue 3:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<span class="token operator"><</span>template<span class="token operator">></span> <span class="token operator"><</span>h1<span class="token operator">></span>Count<span class="token operator">:</span> <span class="token punctuation">{</span><span class="token punctuation">{</span> count <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span>h1<span class="token operator">></span> <span class="token operator"><</span>button @click<span class="token operator">=</span><span class="token string">"increaseCount"</span><span class="token operator">></span>Increase Count<span class="token operator"><</span><span class="token operator">/</span>button<span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>template<span class="token operator">></span> <span class="token operator"><</span>script<span class="token operator">></span> <span class="token keyword">import</span> <span class="token punctuation">{</span>ref<span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"vue"</span><span class="token punctuation">;</span> <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span> <span class="token function">setup</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// Tạo 1 reactive state count = 0</span> <span class="token comment">// (count ở đây là một Proxy object chứ không phải number)</span> <span class="token keyword">const</span> count <span class="token operator">=</span> <span class="token function">ref</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token function-variable function">increaseCount</span> <span class="token operator">=</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">// Tăng giá trị của count bằng cách cập nhật giá trị thuộc tính value</span> count<span class="token punctuation">.</span>value<span class="token operator">++</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 punctuation">{</span> count<span class="token punctuation">,</span> increaseCount<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> <span class="token operator"><</span><span class="token operator">/</span>script<span class="token operator">></span> |
Để hiểu rõ hơn về nguyên lý hoạt động của ref(), các bạn nên tìm hiểu thêm về Proxy trong Javascript.
Một số chú ý về ref():
- Chúng ta có thể lưu dữ liệu gì vào ref object cũng được.
- Ref object là mutable, khi cần thay đổi giá trị thì có thể thay đổi trực tiếp thuộc tính value của nó. Tuy nhiên khi dùng ref object ở template thì chúng ta không cần .value vì nó được tự động unwrap.
Reactive
Trong đa số trường hợp, chúng ta chỉ cần dùng ref() là đủ. Vậy dùng reactive() để làm gì?
reactive() hoạt động tương tự ref() nhưng nó chỉ nhận tham số là object, không nhận các kiểu dữ liệu primitives (number, string, boolean). Và chúng ta thay đổi giá trị của reactive object bằng cách thay đổi các thuộc tính của nó (thay vì thay đổi thuộc tính value như ref). Ví dụ ở trên viết lại bằng reactive():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
<span class="token operator"><</span>template<span class="token operator">></span> <span class="token operator"><</span>h1<span class="token operator">></span>Count<span class="token operator">:</span> <span class="token punctuation">{</span><span class="token punctuation">{</span> state<span class="token punctuation">.</span>count <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span>h1<span class="token operator">></span> <span class="token operator"><</span>button @click<span class="token operator">=</span><span class="token string">"increaseCount"</span><span class="token operator">></span>Increase Count<span class="token operator"><</span><span class="token operator">/</span>button<span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>template<span class="token operator">></span> <span class="token operator"><</span>script<span class="token operator">></span> <span class="token keyword">import</span> <span class="token punctuation">{</span>reactive<span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"vue"</span><span class="token punctuation">;</span> <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span> <span class="token function">setup</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// Tạo 1 reactive state có thuộc tính count = 0</span> <span class="token keyword">const</span> state <span class="token operator">=</span> <span class="token function">reactive</span><span class="token punctuation">(</span><span class="token punctuation">{</span>count<span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token function-variable function">increaseCount</span> <span class="token operator">=</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">// Tăng giá trị của thuộc tính count</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 keyword">return</span> <span class="token punctuation">{</span> state<span class="token punctuation">,</span> increaseCount<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> <span class="token operator"><</span><span class="token operator">/</span>script<span class="token operator">></span> |
Về bản chất ref() là một hàm wrap lại reactive (bên trong ref() sử dụng reactive()), nên trong đa số trường hợp chúng ta có thể sử dụng hầu hết ref() cho đồng bộ và đỡ phải nhớ nhiều, chỉ cần chú ý khi thay đổi giá trị của ref object phải thông qua thuộc tính value. Bạn cũng có thể dùng reactive khi muốn tạo 1 state tập trung để đỡ phải tạo nhiều biến, ví dụ:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<span class="token comment">// Dùng ref()</span> <span class="token keyword">const</span> isLoading <span class="token operator">=</span> <span class="token function">ref</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> isError <span class="token operator">=</span> <span class="token function">ref</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> user <span class="token operator">=</span> <span class="token function">ref</span><span class="token punctuation">(</span><span class="token punctuation">{</span> name<span class="token operator">:</span> <span class="token string">"Robin"</span><span class="token punctuation">,</span> role<span class="token operator">:</span> <span class="token string">"Admin"</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Dùng reactive()</span> <span class="token keyword">const</span> state <span class="token operator">=</span> <span class="token function">reactive</span><span class="token punctuation">(</span><span class="token punctuation">{</span> isLoading<span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span> isError<span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span> user<span class="token operator">:</span> <span class="token punctuation">{</span> name<span class="token operator">:</span> <span class="token string">"Robin"</span><span class="token punctuation">,</span> role<span class="token operator">:</span> <span class="token string">"Admin"</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> |
Chú ý khi dùng reactive chúng ta chỉ được truyền vào một object và khi update thì sẽ update các thuộc tính của object đó, chứ không dùng phép gán trực tiếp vào reactive object. Ví dụ như sau là sai:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
<span class="token operator"><</span>template<span class="token operator">></span> <span class="token operator"><</span>h1<span class="token operator">></span>User name<span class="token operator">:</span> <span class="token punctuation">{</span><span class="token punctuation">{</span> user<span class="token punctuation">.</span>name <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span>h1<span class="token operator">></span> <span class="token operator"><</span>h1<span class="token operator">></span>User role<span class="token operator">:</span> <span class="token punctuation">{</span><span class="token punctuation">{</span> user<span class="token punctuation">.</span>role <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span>h1<span class="token operator">></span> <span class="token operator"><</span>button @click<span class="token operator">=</span><span class="token string">"updateUser"</span><span class="token operator">></span>Update<span class="token operator"><</span><span class="token operator">/</span>button<span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>template<span class="token operator">></span> <span class="token operator"><</span>script<span class="token operator">></span> <span class="token keyword">import</span> <span class="token punctuation">{</span>reactive<span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"vue"</span><span class="token punctuation">;</span> <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span> <span class="token function">setup</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// Tạo 1 reactive object user</span> <span class="token keyword">const</span> user <span class="token operator">=</span> <span class="token function">reactive</span><span class="token punctuation">(</span><span class="token punctuation">{</span>name<span class="token operator">:</span> <span class="token string">"Robin"</span><span class="token punctuation">,</span> role<span class="token operator">:</span> <span class="token string">"Admin"</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token function-variable function">updateUser</span> <span class="token operator">=</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">// Ví dụ dữ liệu mới lấy từ form, api, ... sau đó update trực tiếp bằng phép gán</span> <span class="token comment">// Code sai</span> user <span class="token operator">=</span> <span class="token punctuation">{</span>name<span class="token operator">:</span> <span class="token string">"Huy"</span><span class="token punctuation">,</span> role<span class="token operator">:</span> <span class="token string">"Staff"</span><span class="token punctuation">}</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 punctuation">{</span> user<span class="token punctuation">,</span> updateUser<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> <span class="token operator"><</span><span class="token operator">/</span>script<span class="token operator">></span> |
Ở dòng 20 ví dụ trên là code sai do chúng ta gán giá trị cho biến user thành một object mới. Nếu ở lúc khai báo dùng const
thì sẽ báo lỗi luôn, còn nếu dùng let
thì code đúng cú pháp nhưng khi bấm nút thì giao diện không update do biến user không còn là reactive object nữa, chỉ là một object bình thường. Có thể sửa lại bằng cách cập nhật từng thuộc tính một:
1 2 3 |
user<span class="token punctuation">.</span>name <span class="token operator">=</span> <span class="token string">"Huy"</span><span class="token punctuation">;</span> user<span class="token punctuation">.</span>role <span class="token operator">=</span> <span class="token string">"Staff"</span><span class="token punctuation">;</span> |
Hoặc sử dụng ref()
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<span class="token operator"><</span>template<span class="token operator">></span> <span class="token operator"><</span>h1<span class="token operator">></span>User name<span class="token operator">:</span> <span class="token punctuation">{</span><span class="token punctuation">{</span> user<span class="token punctuation">.</span>name <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span>h1<span class="token operator">></span> <span class="token operator"><</span>h1<span class="token operator">></span>User role<span class="token operator">:</span> <span class="token punctuation">{</span><span class="token punctuation">{</span> user<span class="token punctuation">.</span>role <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span>h1<span class="token operator">></span> <span class="token operator"><</span>button @click<span class="token operator">=</span><span class="token string">"updateUser"</span><span class="token operator">></span>Update<span class="token operator"><</span><span class="token operator">/</span>button<span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>template<span class="token operator">></span> <span class="token operator"><</span>script<span class="token operator">></span> <span class="token keyword">import</span> <span class="token punctuation">{</span>ref<span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"vue"</span><span class="token punctuation">;</span> <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span> <span class="token function">setup</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// Tạo 1 ref object user</span> <span class="token keyword">const</span> user <span class="token operator">=</span> <span class="token function">ref</span><span class="token punctuation">(</span><span class="token punctuation">{</span>name<span class="token operator">:</span> <span class="token string">"Robin"</span><span class="token punctuation">,</span> role<span class="token operator">:</span> <span class="token string">"Admin"</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token function-variable function">updateUser</span> <span class="token operator">=</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">// Cập nhật ref object qua thuộc tính value</span> user<span class="token punctuation">.</span>value <span class="token operator">=</span> <span class="token punctuation">{</span>name<span class="token operator">:</span> <span class="token string">"Huy"</span><span class="token punctuation">,</span> role<span class="token operator">:</span> <span class="token string">"Staff"</span><span class="token punctuation">}</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 punctuation">{</span> user<span class="token punctuation">,</span> updateUser<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> <span class="token operator"><</span><span class="token operator">/</span>script<span class="token operator">></span> |
Nguồn: https://huydq.dev.