Vueでの反応性

公開: 2022-03-10
クイックサマリー↬反応性とは、変数(配列、文字列、数値、オブジェクトなど)が、宣言後にその値または参照するその他の変数が変更されたときに更新する機能です。

この記事では、Vueでの反応性、その仕組み、および新しく作成されたメソッドと関数を使用してリアクティブ変数を作成する方法について説明します。 この記事は、Vue 2.xがどのように機能するかをよく理解していて、新しいVue3に慣れようとしている開発者を対象としています。

このトピックをよりよく理解するために、簡単なアプリケーションを作成します。 このアプリのコードはGitHubにあります。

デフォルトでは、 JavaScriptはリアクティブではありません。 これは、変数boyを作成し、それをアプリケーションのパートAで参照してから、パートBでboyの変更に進むと、パートAはboyの新しい値で更新されないことを意味します。

 let framework = 'Vue'; let sentence = `${framework} is awesome`; console.log(sentence) // logs "Vue is awesome" framework = 'React'; console.log(sentence) //should log "React is awesome" if 'sentence' is reactive.

上記のスニペットは、JavaScriptの非反応性の完璧な例です。したがって、変更がsentence変数に反映されないのはなぜですか。

Vue 2.xでは、 propscomputed 、およびdata()は、そのようなコンポーネントが作成されたときにdataに存在しないプロパティを除いて、すべてデフォルトでリアクティブでした。 つまり、コンポーネントがDOMに挿入されると、コンポーネントのdataオブジェクト内の既存のプロパティのみが、そのようなプロパティが変更された場合にコンポーネントを更新します。

内部的には、Vue 3はProxyオブジェクト(ECMAScript 6機能)を使用してこれらのプロパティがリアクティブであることを確認しますが、Internet Explorerサポート(ECMAScript 5)のためにVue2のObject.definePropertyを使用するオプションを提供します。 このメソッドは、オブジェクトに直接新しいプロパティを定義するか、オブジェクトの既存のプロパティを変更して、オブジェクトを返します。

一見すると、Vueでは反応性が新しいものではないことを私たちのほとんどがすでに知っているので、これらのプロパティを使用する必要はないように見えるかもしれませんが、いくつかの再利用可能な機能を持つ大規模なアプリケーションを扱う場合、OptionsAPIには制限がありますアプリケーションの一部。 この目的のために、新しいComposition APIが導入され、コードベースの読み取りと保守を容易にするためのロジックの抽象化を支援します。 また、新しいプロパティとメソッドを使用して、データ型に関係なく、変数を簡単にリアクティブにすることができるようになりました。

Composition APIのエントリポイントとして機能するsetupオプションを使用すると、 setupの実行時にコンポーネントインスタンスがまだ作成されていないため、 dataオブジェクト、 computedれたプロパティ、およびmethodsにアクセスできません。 これにより、 setupでこれらの機能のいずれかに組み込まれている反応性を利用することができなくなります。 このチュートリアルでは、これを行うためのすべての方法について学習します。

ジャンプした後もっと! 以下を読み続けてください↓

リアクティブメソッド

ドキュメントによると、Vue 2.6のVue.observable()と同等のreactiveメソッドは、すべてのプロパティがリアクティブであるオブジェクト(オプションのdataオブジェクトなど)を作成しようとしているときに役立ちます。 API)。 内部的には、Options APIのdataオブジェクトはこのメソッドを使用して、その中のすべてのプロパティをリアクティブにします。

ただし、次のような独自のリアクティブオブジェクトを作成できます。

 import { reactive } from 'vue' // reactive state let user = reactive({ "id": 1, "name": "Leanne Graham", "username": "Bret", "email": "[email protected]", "address": { "street": "Kulas Light", "suite": "Apt. 556", "city": "Gwenborough", "zipcode": "92998-3874", "geo": { "lat": "-37.3159", "lng": "81.1496" } }, "phone": "1-770-736-8031 x56442", "website": "hildegard.org", "company": { "name": "Romaguera-Crona", "catchPhrase": "Multi-layered client-server neural-net", "bs": "harness real-time e-markets" }, "cars": { "number": 0 } })

ここでは、Vueからreactiveメソッドをインポートし、その値を引数としてこの関数に渡すことでuser変数を宣言しました。 そうすることで、 userをリアクティブにしました。したがって、テンプレートでuserを使用し、このオブジェクトのオブジェクトまたはプロパティのいずれかが変更された場合、この値はこのテンプレートで自動的に更新されます。

ref

オブジェクトをリアクティブにする方法があるのと同じように、他のスタンドアロンのプリミティブ値(文字列、ブール値、未定義の値、数値など)と配列をリアクティブにする方法も必要です。 開発中は、これらの他のデータタイプを処理すると同時に、それらをリアクティブにする必要があります。 私たちが考えるかもしれない最初のアプローチは、 reactiveにしたい変数の値を渡すことです。

 import { reactive } from 'vue' const state = reactive({ users: [], });

reactiveには深いリアクティブ変換があるため、プロパティとしてのuserもリアクティブになり、それによって目標を達成します。 したがって、 userは、そのようなアプリのテンプレートで使用されている場所であればどこでも常に更新します。 ただし、 refプロパティを使用すると、その変数の値をrefに渡すことで、任意のデータ型の任意の変数をリアクティブにすることができます。 このメソッドはオブジェクトに対しても機能しますが、 reactiveメソッドを使用する場合よりも1レベル深くオブジェクトをネストします。

 let property = { rooms: '4 rooms', garage: true, swimmingPool: false } let reactiveProperty = ref(property) console.log(reactiveProperty) // prints { // value: {rooms: "4 rooms", garage: true, swimmingPool: false} // }

内部的には、 refは渡されたこの引数を受け取り、 valueのキーを持つオブジェクトに変換します。 つまり、variable.valueを呼び出すことでvariable.valueにアクセスでき、同じ方法で呼び出すことでその値を変更することもできます。

 import {ref} from 'vue' let age = ref(1) console.log(age.value) //prints 1 age.value++ console.log(age.value) //prints 2

これにより、 refをコンポーネントにインポートし、リアクティブ変数を作成できます。

 <template> <div class="home"> <form @click.prevent=""> <table> <tr> <th>Name</th> <th>Username</th> <th>email</th> <th>Edit Cars</th> <th>Cars</th> </tr> <tr v-for="user in users" :key="user.id"> <td>{{ user.name }}</td> <td>{{ user.username }}</td> <td>{{ user.email }}</td> <td> <input type="number" name="cars" v-model.number="user.cars.number" /> </td> <td> <cars-number :cars="user.cars" /> </td> </tr> </table> <p>Total number of cars: {{ getTotalCars }}</p> </form> </div> </template> <script> // @ is an alias to /src import carsNumber from "@/components/cars-number.vue"; import axios from "axios"; import { ref } from "vue"; export default { name: "Home", data() { return {}; }, setup() { let users = ref([]); const getUsers = async () => { let { data } = await axios({ url: "data.json", }); users.value = data; }; return { users, getUsers, }; }, components: { carsNumber, }, created() { this.getUsers(); }, computed: { getTotalCars() { let users = this.users; let totalCars = users.reduce(function(sum, elem) { return sum + elem.cars.number; }, 0); return totalCars; }, }; </script>

ここでは、コンポーネントにリアクティブusers変数を作成するためにrefをインポートしました。 次に、 axiosをインポートしてpublicフォルダー内のJSONファイルからデータをフェッチし、 carsNumberコンポーネントをインポートしました。これは後で作成します。 次に行ったのは、 refメソッドを使用してリアクティブusers変数を作成し、JSONファイルからの応答が変更されるたびにusersが更新できるようにすることでした。

また、axiosを使用してJSONファイルからusers配列をフェッチするgetUser関数を作成し、このリクエストの値をusers変数に割り当てました。 最後に、テンプレートセクションで変更したため、ユーザーが所有する車の総数を計算する計算プロパティを作成しました。

テンプレートセクションまたはsetup()の外部で返されるrefプロパティにアクセスする場合、それらは自動的に浅くアンラップされることに注意することが重要です。 これは、オブジェクトである参照にアクセスする.value refs必要であることを意味します。 usersは配列であるため、 users.valuegetTotalCarsなくusersを使用するだけで済みます。

テンプレートセクションでは、各ユーザーの情報を<cars-number />コンポーネントとともに表示するテーブルを表示しました。 このコンポーネントは、各ユーザーの行に車の数として表示されるcarsの小道具を受け入れます。 この値は、ユーザーオブジェクトcarsの値が変更されるたびに更新されます。これは、OptionsAPIを使用している場合にdataオブジェクトまたはcomputedれたプロパティがどのように機能するかを正確に示しています。

toRefs

Composition APIを使用する場合、 setup関数はpropscontextの2つの引数を受け入れます。 このpropsはコンポーネントからsetup()に渡され、この新しいAPIの内部からコンポーネントが持つ小道具にアクセスできるようになります。 この方法は、反応性を失うことなくオブジェクトを破壊できるため、特に便利です。

 <template> <p>{{ cars.number }}</p> </template> <script> export default { props: { cars: { type: Object, required: true, }, gender: { type: String, required: true, }, }, setup(props) { console.log(props); // prints {gender: "female", cars: Proxy} }, }; </script> <style></style>

その反応性を維持しながら、Composition APIのpropsからのオブジェクトである値を使用するには、 toRefsを使用します。 このメソッドは、リアクティブオブジェクトを取得し、それをプレーンオブジェクトに変換します。このオブジェクトでは、元のリアクティブオブジェクトの各プロパティがrefになります。 これが意味するのは、 cars支えているということです…

 cars: { number: 0 }

…これで次のようになります。

 { value: cars: { number: 0 }

これにより、反応性を維持しながら、セットアップAPIの任意の部分でcarsを利用できます。

 setup(props) { let { cars } = toRefs(props); console.log(cars.value); // prints {number: 0} },

この新しい変数は、Composition APIのwatchを使用して監視し、必要に応じてこの変更に対応できます。

 setup(props) { let { cars } = toRefs(props); watch( () => cars, (cars, prevCars) => { console.log("deep ", cars.value, prevCars.value); }, { deep: true } ); }

toRef

直面する可能性のあるもう1つの一般的な使用例は、必ずしもオブジェクトではなく、 refで機能するデータ型(配列、数値、文字列、ブール値など)の1つである値を渡すことです。 toRefを使用すると、ソースのリアクティブオブジェクトからリアクティブプロパティ(つまりref )を作成できます。 これを行うと、プロパティがリアクティブのままになり、親ソースが変更されるたびに更新されます。

 const cars = reactive({ Toyota: 1, Honda: 0 }) const NumberOfHondas = toRef(state, 'Honda') NumberOfHondas.value++ console.log(state.Honda) // 1 state.Honda++ console.log(NumberOfHondas.value) // 2

ここでは、 reactiveメソッドを使用して、 ToyotaHondaのプロパティを持つリアクティブオブジェクトを作成しました。 また、 toRefを使用して、 Hondaからリアクティブ変数を作成しました。 上記の例から、リアクティブcarsオブジェクトまたはNumberOfHondasのいずれかを使用してHondaを更新すると、両方のインスタンスで値が更新されることがわかります。

このメソッドは、ソースへの接続を維持し、文字列、配列、および数値に使用できるという意味で、上記で説明したtoRefsメソッドと似ていますが、非常に異なります。 toRefsとは異なり、作成時にソースにプロパティが存在することを心配する必要はありません。これは、このrefが作成されたときにこのプロパティが存在せず、代わりにnullを返す場合でも、保存されるためです。有効なプロパティとして、 watcherの形式が配置されているため、この値が変更されると、 toRefを使用して作成されたこのrefも更新されます。

このメソッドを使用して、 propsからリアクティブプロパティを作成することもできます。 これは次のようになります。

 <template> <p>{{ cars.number }}</p> </template> <script> import { watch, toRefs, toRef } from "vue"; export default { props: { cars: { type: Object, required: true, }, gender: { type: String, required: true, }, }, setup(props) { let { cars } = toRefs(props); let gender = toRef(props, "gender"); console.log(gender.value); watch( () => cars, (cars, prevCars) => { console.log("deep ", cars.value, prevCars.value); }, { deep: true } ); }, }; </script>

ここでは、 propsから取得したgenderプロパティに基づくrefを作成しました。 これは、特定のコンポーネントのプロップで追加の操作を実行する場合に便利です。

結論

この記事では、Vue 3から新しく導入されたメソッドと関数のいくつかを使用して、 Proxyの反応性がどのように機能するかを確認しました。 また、 reactiveを使用してリアクティブオブジェクトを作成する方法と、 refを使用してリアクティブプロパティを作成する方法についても説明しました。

最後に、リアクティブオブジェクトをプレーンオブジェクトに変換する方法を確認しました。各プロパティは、元のオブジェクトの対応するプロパティを指すrefであり、リアクティブソースオブジェクトのプロパティのrefを作成する方法を確認しました。

その他のリソース

  • 「プロキシ」(オブジェクト)、MDN Web Docs
  • 「ReactivityFundamentals」、Vue.js
  • 「参照」、Vue.js
  • setup内のライフサイクルフック登録」、Vue.js