[[📗05 状態で広がるセカイ Ref編]] < [[📒Vue.jsクエスト]] > [[📗07 計算でテンプレートをシンプルに]] ## Abstract [[リアクティブ]]にするための魔法は[[ref()]]だけではありません。[[reactive()]]の使い方を学びましょう。 ## Lesson [[reactive()]]で[[オブジェクト (JavaScript)|オブジェクト]]や[[配列 (JavaScript)|配列]]を包むと、それら全体が[[リアクティブ]]になります。 ```html <script setup lang="ts"> import { reactive } from "vue"; const state = reactive({ count: 1, }); const handleClick = () => { state.count *= 2; }; </script> <template> <div>{{ state.count }}</div> <div>NEXT: {{ state.count * 2 }}</div> <button @click="handleClick">Double up</button> </template> ``` 上記は[[📗05 状態で広がるセカイ Ref編#Mission 3]]の回答例コードと同等の動きをします。 ```html <script setup lang="ts"> import { ref } from "vue"; const countState = { count: ref(1), }; const handleClick = () => { countState.count.value *= 2; }; const { count } = countState; </script> <template> <div>{{ count }}</div> <div>NEXT: {{ count * 2 }}</div> <button @click="handleClick">Double up</button> </template> ``` [[ref()]]を使うケースより階層が1つ深くなりますが、以下の点から[[reactive()]]をオススメしています。 - [[reactive()]]の中で[[プロパティ (TypeScript)|プロパティ]]ごとに[[ref()]]を呼び出す必要がない - `<script>`、`<template>`の両方において`.value`を呼び出す必要がない[^1] - 状態が集約され、状態であることが一目で分かる また、[[reactive()]]はデフォルトで`Deep Reactivity`です。[[reactive()]]の中では[[オブジェクト (JavaScript)|オブジェクト]]や[[配列 (JavaScript)|配列]]を何度ネストさせても、すべての変更を期待通り検知します。 ```ts const obj = reactive({ nested: { nested: { ary: [{nested: {count: 1}}] } }, }) ``` ## Missions ### Mission 1 #🙂NORMAL 以下のコードを[[reactive()]]を使って書き直してください。 ```html <script setup lang="ts"> import { ref } from "vue"; const countSingle = ref(0); const countDouble = ref(0); const handleClick = () => { countSingle.value++; countDouble.value = countSingle.value * 2; }; </script> <template> <div>{{ countSingle }} * 2 = {{ countDouble }}</div> <button @click="handleClick">Increment</button> </template> ``` %% 回答例 ```html <script setup lang="ts"> import { reactive } from "vue"; const state = reactive({ countSingle: 0, countDouble: 0, }); const handleClick = () => { state.countSingle++; state.countDouble = state.countSingle * 2; }; </script> <template> <div>{{ state.countSingle }} * 2 = {{ state.countDouble }}</div> <button @click="handleClick">Increment</button> </template> ``` %% ### Mission 2 #😵HARD 以下を実行したとき、コンソール出力結果が`{hoge: "huga"}`に==ならない==理由を説明してください。 ```html <script setup lang="ts"> import { reactive } from "vue"; const state = reactive({ hoge: "huga" }); console.log(state); </script> ``` > [!hint]- Hint 1 > https://vuejs.org/guide/essentials/reactivity-fundamentals.html#reactive-proxy-vs-original-1 %% 回答例 [[reactive()]]の返却結果は引数として渡された[[オブジェクト (JavaScript)|オブジェクト]]の[[Proxy (JavaScript)|Proxy]]になるから。 %% ### Mission 3 #😱NIGHTMARE [[Vue]]が[[#Mission 2]] 回答のような挙動になる背景を説明してください。 > [!hint]- Hint 1 > [リアクティブの探求 \| Vue\.js](https://v3.ja.vuejs.org/guide/reactivity.html#vue-%E3%81%8B%E3%82%99%E3%81%A8%E3%82%99%E3%81%AE%E3%82%B3%E3%83%BC%E3%83%88%E3%82%99%E3%82%92%E5%AE%9F%E8%A1%8C%E3%81%97%E3%81%A6%E3%81%84%E3%82%8B%E3%81%AE%E3%81%8B%E7%9F%A5%E3%82%8B%E6%96%B9%E6%B3%95) %% 回答例 [[リアクティブ]]を実現するには、[[Proxy (JavaScript)|Proxy]]を使って追跡や変更検知を行い、必要な時に必要な処理を再実行する必要があったから。 %% ## References [Reactivity Fundamentals \| Vue\.js](https://vuejs.org/guide/essentials/reactivity-fundamentals.html)