From aa22928525e73e750e1f3f368f08762b51aa2f8b Mon Sep 17 00:00:00 2001 From: Naoki Endoh Date: Thu, 6 May 2021 19:48:28 +0900 Subject: [PATCH 1/8] fix: delete nonexistent output https://github.com/vuejs/docs-next/commit/6fad8c71557a9af07f8417dd24b18f64deaa25f3#diff-4d47e7364676bfe0411297303264e00087269482dd4665c4fc9b139dfae30fbc --- src/guide/reactivity.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/guide/reactivity.md b/src/guide/reactivity.md index ff2cedfc..2b19a590 100644 --- a/src/guide/reactivity.md +++ b/src/guide/reactivity.md @@ -77,7 +77,7 @@ const dinner = { const handler = { get(target, prop) { - console.log(‘intercepted!’) + console.log('intercepted!') return target[prop] } } From e9e0302563d5fbd62b3a69fd7a5bc7614e1f7554 Mon Sep 17 00:00:00 2001 From: Naoki Endoh Date: Thu, 6 May 2021 19:50:51 +0900 Subject: [PATCH 2/8] fix: switch the reactivity/proxies CodePen to the Vue account https://github.com/vuejs/docs-next/commit/d941acde84f023054bb2908e623f4bc93848fada#diff-4d47e7364676bfe0411297303264e00087269482dd4665c4fc9b139dfae30fbc --- src/guide/reactivity.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/guide/reactivity.md b/src/guide/reactivity.md index 2b19a590..c2815566 100644 --- a/src/guide/reactivity.md +++ b/src/guide/reactivity.md @@ -44,7 +44,7 @@ val1 = 3 プレーンな JavaScript オブジェクトを `data` オプションとしてアプリケーションまたはコンポーネントインスタンスに渡すと、Vue はそのすべてのプロパティを走査して、ゲッターとセッターのハンドラを使用しそれらを[プロキシ](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Proxy)に変換します。 これは ES6 のみの機能ですが、旧式の `Object.defineProperty` を使用した Vue 3 のバージョンを IE ブラウザをサポートするために提供しています。どちらも表面的には同じ API を提供しますが、プロキシバージョンの方がよりスリムで、パフォーマンスが改良されています。
- +
この例はかなり素早いので、理解するには[プロキシ](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Proxy)についての知識がある程度必要です!では、少し詳しく見ていきましょう。プロキシに関する文献はたくさんありますが、本当に知っておく必要があることは **プロキシは別のオブジェクトまたは関数を包み、操作を差し込むこと(intercept)ができるオブジェクトだということです。** From 69974bc80cd0cfe5aa5c4381e7cdb3e7e283ae84 Mon Sep 17 00:00:00 2001 From: Naoki Endoh Date: Thu, 6 May 2021 19:51:59 +0900 Subject: [PATCH 3/8] Fix typo, track function name should be 'track' instead of 'effect' https://github.com/vuejs/docs-next/commit/64f9be3928511ee5b9530a7c8976db731009db44#diff-4d47e7364676bfe0411297303264e00087269482dd4665c4fc9b139dfae30fbc --- src/guide/reactivity.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/guide/reactivity.md b/src/guide/reactivity.md index c2815566..5422ee2f 100644 --- a/src/guide/reactivity.md +++ b/src/guide/reactivity.md @@ -157,7 +157,7 @@ console.log(proxy.meal) 数段落前のこのリストを覚えていますか?これで Vue がこれらの変更を処理する方法に対するいくつかの回答が出揃いました。 - いずれかの値に変化があった時に検出する: プロキシがそれに対する操作の差し込みを可能にしているため、その必要がなくなりました -- **それを変更する関数を追跡する**: これは、 `effect` と呼ばれるプロキシ内のゲッターで行います +- **それを変更する関数を追跡する**: これは、 `track` と呼ばれるプロキシ内のゲッターで行います - **最終的な値を更新できるように関数を発火させる**: `trigger` と呼ばれるプロキシ内のセッターで行います プロキシされたオブジェクトはユーザーには見えませんが、内部的にはプロパティがアクセスまたは変更されたときに、Vue が依存関係の追跡と変更通知を実行できるようになっています。 Vue 3 以降、リアクティブは[個別のパッケージ](https://github.com/vuejs/vue-next/tree/master/packages/reactivity)で利用できるようになりました。注意点の 1 つは、変換されたデータオブジェクトがログに記録された時は、ブラウザコンソールが違った整形をすることです。そのため、 [vue-devtools](https://github.com/vuejs/vue-devtools) をインストールして、より見やすいインターフェイスにすることをお勧めします。 From 27a580f49d4589119759aff9a3cad642cb4b5d1b Mon Sep 17 00:00:00 2001 From: Naoki Endoh Date: Thu, 6 May 2021 20:09:31 +0900 Subject: [PATCH 4/8] docs: enhance the Reactivity in Depth guide https://github.com/vuejs/docs-next/commit/91a4425fd78687f46a74b18797840805c015f7aa#diff-4d47e7364676bfe0411297303264e00087269482dd4665c4fc9b139dfae30fbc --- src/guide/reactivity.md | 204 +++++++++++++++++++++++++++++----------- 1 file changed, 147 insertions(+), 57 deletions(-) diff --git a/src/guide/reactivity.md b/src/guide/reactivity.md index 5422ee2f..f492636e 100644 --- a/src/guide/reactivity.md +++ b/src/guide/reactivity.md @@ -18,57 +18,95 @@ JavaScript は通常このように機能しません。JavaScript で同等のものを書こうとしたら次のようになります: ```js -var val1 = 2 -var val2 = 3 -var sum = val1 + val2 +let val1 = 2 +let val2 = 3 +let sum = val1 + val2 -// sum -// 5 +console.log(sum) // 5 val1 = 3 -// sum -// 5 +console.log(sum) // Still 5 ``` 最初の値を更新しても、合計値は調整されません。 では、 JavaScript を使って以下の要素をどうやって実現するのでしょうか。 -- いずれかの値に変化があった時に検出する -- それを変更する関数を追跡する -- 最終的な値を更新できるように関数を発火させる +As a high-level overview, there are a few things we need to be able to do: -## Vue がこれらの変更を追跡する方法 +1. **Track when a value is read.** e.g. `val1 + val2` reads both `val1` and `val2`. +2. **Detect when a value changes.** e.g. When we assign `val1 = 3`. +3. **Re-run the code that read the value originally.** e.g. Run `sum = val1 + val2` again to update the value of `sum`. -プレーンな JavaScript オブジェクトを `data` オプションとしてアプリケーションまたはコンポーネントインスタンスに渡すと、Vue はそのすべてのプロパティを走査して、ゲッターとセッターのハンドラを使用しそれらを[プロキシ](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Proxy)に変換します。 これは ES6 のみの機能ですが、旧式の `Object.defineProperty` を使用した Vue 3 のバージョンを IE ブラウザをサポートするために提供しています。どちらも表面的には同じ API を提供しますが、プロキシバージョンの方がよりスリムで、パフォーマンスが改良されています。 +We can't do this directly using the code from the previous example but we'll come back to this example later to see how to adapt it to be compatible with Vue's reactivity system. -
- -
+First, let's dig a bit deeper into how Vue implements the core reactivity requirements outlined above. -この例はかなり素早いので、理解するには[プロキシ](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Proxy)についての知識がある程度必要です!では、少し詳しく見ていきましょう。プロキシに関する文献はたくさんありますが、本当に知っておく必要があることは **プロキシは別のオブジェクトまたは関数を包み、操作を差し込むこと(intercept)ができるオブジェクトだということです。** +## How Vue Knows What Code Is Running -proxy は次のように使用します: `new Proxy(target, handler)` +To be able to run our sum whenever the values change, the first thing we need to do is wrap it in a function: ```js -const dinner = { - meal: 'tacos' +const updateSum = () => { + sum = val1 + val2 } +``` -const handler = { - get(target, prop) { - return target[prop] +But how do we tell Vue about this function? + +Vue keeps track of which function is currently running by using an *effect*. An effect is a wrapper around the function that initiates tracking just before the function is called. Vue knows which effect is running at any given point and can run it again when required. + +To understand that better, let's try to implement something similar ourselves, without Vue, to see how it might work. + +What we need is something that can wrap our sum, like this: + +```js +createEffect(() => { + sum = val1 + val2 +}) +``` + +We need `createEffect` to keep track of when the sum is running. We might implement it something like this: + +```js +// Maintain a stack of running effects +const runningEffects = [] + +const createEffect = fn => { + // Wrap the passed fn in an effect function + const effect = () => { + runningEffects.push(effect) + fn() + runningEffects.pop() } + + // Automatically run the effect immediately + effect() } +``` -const proxy = new Proxy(dinner, handler) -console.log(proxy.meal) +When our effect is called it pushes itself onto the `runningEffects` array, before calling `fn`. Anything that needs to know which effect is currently running can check that array. -// tacos -``` +Effects act as the starting point for many key features. For example, both component rendering and computed properties use effects internally. Any time something magically responds to data changes you can be pretty sure it has been wrapped in an effect. + +While Vue's public API doesn't include any way to create an effect directly, it does expose a function called `watchEffect` that behaves a lot like the `createEffect` function from our example. We'll discuss that in more detail [later in the guide](/guide/reactivity-computed-watchers.html#watcheffect). + +But knowing what code is running is just one part of the puzzle. How does Vue know what values the effect uses and how does it know when they change? + +## How Vue Tracks These Changes -今のところは、オブジェクトをラップしてそれをそのまま返すだけです。かっこいいですが、まだ役に立つ物ではありません。しかしこれを見てください。プロキシでラップしている中で、このオブジェクトに操作を差し込むこともできます。この操作の差し込みはトラップと呼ばれています。 +We can't track reassignments of local variables like those in our earlier examples, there's just no mechanism for doing that in JavaScript. What we can track are changes to object properties. + +When we return a plain JavaScript object from a component's `data` function, Vue will wrap that object in a [Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) with handlers for `get` and `set`. Proxies were introduced in ES6 and allow Vue 3 to avoid some of the reactivity caveats that existed in earlier versions of Vue. + +
+ +
+ +That was rather quick and requires some knowledge of [Proxies](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) to understand! So let’s dive in a bit. There’s a lot of literature on Proxies, but what you really need to know is that a **Proxy is an object that encases another object and allows you to intercept any interactions with that object.** + +We use it like this: `new Proxy(target, handler)` ```js const dinner = { @@ -76,9 +114,9 @@ const dinner = { } const handler = { - get(target, prop) { + get(target, property) { console.log('intercepted!') - return target[prop] + return target[property] } } @@ -89,9 +127,11 @@ console.log(proxy.meal) // tacos ``` +Here we've intercepted attempts to read properties of the target object. A handler function like this is also known as a *trap*. There are many different types of trap available, each handling a different type of interaction. + コンソールログ以外にも、ここでは思い通りの操作が可能です。必要な場合は、実際の値を返さ _ない_ ようにすることさえできます。これにより、プロキシは API の作成において強力なものになっています。 -さらに、プロキシは別の機能も提供してくれます。`target[prop]` のような値をただ返すだけではなく、これをさらに一歩進めて `this` のバインディングを適切に行うことができる `Reflect` と呼ばれる機能を使用することができます。これは次のようになります。 +One challenge with using a Proxy is the `this` binding. We'd like any methods to be bound to the Proxy, rather than the target object, so that we can intercept them too. Thankfully, ES6 introduced another new feature, called `Reflect`, that allows us to make this problem disappear with minimal effort: ```js{7} const dinner = { @@ -99,7 +139,7 @@ const dinner = { } const handler = { - get(target, prop, receiver) { + get(target, property, receiver) { return Reflect.get(...arguments) } } @@ -110,7 +150,7 @@ console.log(proxy.meal) // tacos ``` -前述の通り、何らかの変更があった時に最終的な値を更新する API を実装するには、何らかの変更があった時に新しい値を設定する必要があるでしょう。この処理をハンドラー内の `track` という関数で、 `target` と `key` を引数として渡して行います。 +The first step towards implementing reactivity with a Proxy is to track when a property is read. We do this in the handler, in a function called `track`, where we pass in the `target` and `property`: ```js{7} const dinner = { @@ -118,8 +158,8 @@ const dinner = { } const handler = { - get(target, prop, receiver) { - track(target, prop) + get(target, property, receiver) { + track(target, property) return Reflect.get(...arguments) } } @@ -130,7 +170,9 @@ console.log(proxy.meal) // tacos ``` -最後に、何らかの変更があった時に新しい値を設定します。このために、これらの変更を発火させることで、新しいプロキシに変更をセットします。 +The implementation of `track` isn't shown here. It will check which *effect* is currently running and record that alongside the `target` and `property`. This is how Vue knows that the property is a dependency of the effect. + +Finally, we need to re-run the effect when the property value changes. For this we're going to need a `set` handler on our proxy: ```js const dinner = { @@ -138,12 +180,12 @@ const dinner = { } const handler = { - get(target, prop, receiver) { - track(target, prop) + get(target, property, receiver) { + track(target, property) return Reflect.get(...arguments) }, - set(target, key, value, receiver) { - trigger(target, key) + set(target, property, value, receiver) { + trigger(target, property) return Reflect.set(...arguments) } } @@ -154,13 +196,52 @@ console.log(proxy.meal) // tacos ``` -数段落前のこのリストを覚えていますか?これで Vue がこれらの変更を処理する方法に対するいくつかの回答が出揃いました。 +Remember this list from earlier? Now we have some answers to how Vue implements these key steps: -- いずれかの値に変化があった時に検出する: プロキシがそれに対する操作の差し込みを可能にしているため、その必要がなくなりました -- **それを変更する関数を追跡する**: これは、 `track` と呼ばれるプロキシ内のゲッターで行います -- **最終的な値を更新できるように関数を発火させる**: `trigger` と呼ばれるプロキシ内のセッターで行います +1. **Track when a value is read**: the `track` function in the proxy's `get` handler records the property and the current effect. +2. **Detect when that value changes**: the `set` handler is called on the proxy. +3. **Re-run the code that read the value originally:** the `trigger` function looks up which effects depend on the property and runs them. -プロキシされたオブジェクトはユーザーには見えませんが、内部的にはプロパティがアクセスまたは変更されたときに、Vue が依存関係の追跡と変更通知を実行できるようになっています。 Vue 3 以降、リアクティブは[個別のパッケージ](https://github.com/vuejs/vue-next/tree/master/packages/reactivity)で利用できるようになりました。注意点の 1 つは、変換されたデータオブジェクトがログに記録された時は、ブラウザコンソールが違った整形をすることです。そのため、 [vue-devtools](https://github.com/vuejs/vue-devtools) をインストールして、より見やすいインターフェイスにすることをお勧めします。 +The proxied object is invisible to the user, but under the hood it enables Vue to perform dependency-tracking and change-notification when properties are accessed or modified. One caveat is that console logging will format proxied objects differently, so you may want to install [vue-devtools](https://github.com/vuejs/vue-devtools) for a more inspection-friendly interface. + +If we were to rewrite our original example using a component we might do it something like this: + +```js +const vm = createApp({ + data() { + return { + val1: 2, + val2: 3 + } + }, + computed: { + sum() { + return this.val1 + this.val2 + } + } +}).mount('#app') + +console.log(vm.sum) // 5 + +vm.val1 = 3 + +console.log(vm.sum) // 6 +``` + +The object returned by `data` will be wrapped in a reactive proxy and stored as `this.$data`. The properties `this.val1` and `this.val2` are aliases for `this.$data.val1` and `this.$data.val2` respectively, so they go through the same proxy. + +Vue will wrap the function for `sum` in an effect. When we try to access `this.sum`, it will run that effect to calculate the value. The reactive proxy around `$data` will track that the properties `val1` and `val2` were read while that effect is running. + +As of Vue 3, our reactivity is now available in a [separate package](https://github.com/vuejs/vue-next/tree/master/packages/reactivity). The function that wraps `$data` in a proxy is called [`reactive`](/api/basic-reactivity.html#reactive). We can call this directly ourselves, allowing us to wrap an object in a reactive proxy without needing to use a component: + +```js +const proxy = reactive({ + val1: 2, + val2: 3 +}) +``` + +We'll explore the functionality exposed by the reactivity package over the course of the next few pages of this guide. That includes functions like `reactive` and `watchEffect` that we've already met, as well as ways to use other reactivity features, such as `computed` and `watch`, without needing to create a component. ## プロキシされたオブジェクト @@ -168,12 +249,13 @@ Vue はリアクティブに作られたすべてのオブジェクトを内部 ネストされたオブジェクトがリアクティブプロキシからアクセスされると、次のようにそのオブジェクト _も_ 返却される前にプロキシに変換されます: -```js +```js{6-7} const handler = { - get(target, prop, receiver) { - track(target, prop) + get(target, property, receiver) { + track(target, property) const value = Reflect.get(...arguments) if (isObject(value)) { + // Wrap the nested object in its own reactive proxy return reactive(value) } else { return value @@ -194,9 +276,9 @@ const wrapped = new Proxy(obj, handlers) console.log(obj === wrapped) // false ``` -オリジナルとラップされたバージョンはほとんどの場合同じように動作しますが、 `.filter()` や `.map()` などの強力な同一性比較に依存する操作は失敗することに注意してください。オプション API を使用する場合、この注意点に出くわすことはほとんどありません。すべてのリアクティブな状態が `this` からアクセスされ、すでにプロキシだということが保証されているためです。 +Other operations that rely on strict equality comparisons can also be impacted, such as `.includes()` or `.indexOf()`. -しかし、コンポジション API を使用して明示的にリアクティブオブジェクトを作成する場合、元の生のオブジェクトへの参照を保持せず、次のようにリアクティブバージョンでのみ処理をすることがベストプラクティスです: +The best practice here is to never hold a reference to the original raw object and only work with the reactive version: ```js const obj = reactive({ @@ -204,18 +286,26 @@ const obj = reactive({ }) // no reference to original ``` -## ウォッチャ +This ensures that both equality comparisons and reactivity behave as expected. -すべてのコンポーネントインスタンスには対応するウォッチャインスタンスがあり、コンポーネントのレンダリング中に「触れられた」プロパティを依存関係として記録します。後に依存関係にあるもののセッターが発火されると、ウォッチャーに通知され、コンポーネントが再レンダリングされます。 +Note that Vue does not wrap primitive values such as numbers or strings in a Proxy, so you can still use `===` directly with those values: -
- -
+```js +const obj = reactive({ + count: 0 +}) + +console.log(obj.count === 0) // true +``` + +## How Rendering Reacts to Changes -オブジェクトをデータとしてコンポーネントインスタンスに渡すと、Vue はそれをプロキシに変換します。このプロキシにより、Vue はプロパティがアクセスまたは変更されたときに、依存関係の追跡と変更通知の実行ができるようになります。各プロパティは依存関係と見なされます。 +The template for a component is compiled down into a [`render`](/guide/render-function.html) function. The `render` function creates the [VNodes](/guide/render-function.html#the-virtual-dom-tree) that describe how the component should be rendered. It is wrapped in an effect, allowing Vue to track the properties that are 'touched' while it is running. -最初のレンダリングの後、コンポーネントはレンダリング中にアクセスしたプロパティを依存関係一覧として追跡します。逆に言えば、コンポーネントはこれらの各プロパティの値を監視する購読者になります。プロキシがセット処理を傍受すると、プロパティは購読されているすべてのコンポーネントに再レンダリングを通知します。 +A `render` function is conceptually very similar to a `computed` property. Vue doesn't track exactly how dependencies are used, it only knows that they were used at some point while the function was running. If any of those properties subsequently changes, it will trigger the effect to run again, re-running the `render` function to generate new VNodes. These are then used to make the necessary changes to the DOM. -[//]: # 'TODO: Insert diagram' +
+ +
> Vue 2.x 以前を使用している場合は、それらのバージョンに存在する変更検出の注意点に興味があるかもしれません[詳細はこちらをご覧ください](change-detection.md)。 From b362ead7cc4d76e4e9cc8d0fdff404ab1e6e6853 Mon Sep 17 00:00:00 2001 From: Naoki Endoh Date: Thu, 6 May 2021 20:10:34 +0900 Subject: [PATCH 5/8] docs: migrate 3 CodePens to the Vue account https://github.com/vuejs/docs-next/commit/beb65fbd5562f233a0ad0a16f9496541b25055e8#diff-4d47e7364676bfe0411297303264e00087269482dd4665c4fc9b139dfae30fbc --- src/guide/reactivity.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/guide/reactivity.md b/src/guide/reactivity.md index f492636e..9159bd78 100644 --- a/src/guide/reactivity.md +++ b/src/guide/reactivity.md @@ -305,7 +305,7 @@ The template for a component is compiled down into a [`render`](/guide/render-fu A `render` function is conceptually very similar to a `computed` property. Vue doesn't track exactly how dependencies are used, it only knows that they were used at some point while the function was running. If any of those properties subsequently changes, it will trigger the effect to run again, re-running the `render` function to generate new VNodes. These are then used to make the necessary changes to the DOM.
- +
> Vue 2.x 以前を使用している場合は、それらのバージョンに存在する変更検出の注意点に興味があるかもしれません[詳細はこちらをご覧ください](change-detection.md)。 From b035deb8436fa2ba2c7c92c166321aa1ae3ec9a7 Mon Sep 17 00:00:00 2001 From: Naoki Endoh Date: Thu, 6 May 2021 20:11:19 +0900 Subject: [PATCH 6/8] chore: whitespace changes suggested by Prettier https://github.com/vuejs/docs-next/commit/42f3656d2c10209c811013a5a5b2b9406c75b9fd#diff-4d47e7364676bfe0411297303264e00087269482dd4665c4fc9b139dfae30fbc --- src/guide/reactivity.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/guide/reactivity.md b/src/guide/reactivity.md index 9159bd78..8cee3f8b 100644 --- a/src/guide/reactivity.md +++ b/src/guide/reactivity.md @@ -98,7 +98,7 @@ But knowing what code is running is just one part of the puzzle. How does Vue kn We can't track reassignments of local variables like those in our earlier examples, there's just no mechanism for doing that in JavaScript. What we can track are changes to object properties. -When we return a plain JavaScript object from a component's `data` function, Vue will wrap that object in a [Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) with handlers for `get` and `set`. Proxies were introduced in ES6 and allow Vue 3 to avoid some of the reactivity caveats that existed in earlier versions of Vue. +When we return a plain JavaScript object from a component's `data` function, Vue will wrap that object in a [Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) with handlers for `get` and `set`. Proxies were introduced in ES6 and allow Vue 3 to avoid some of the reactivity caveats that existed in earlier versions of Vue.
From 2c5681be9fb5ffddb056d3f9691b25d812ff2848 Mon Sep 17 00:00:00 2001 From: Naoki Endoh Date: Sat, 8 May 2021 10:51:38 +0900 Subject: [PATCH 7/8] docs: translate reactivity page --- src/guide/reactivity.md | 106 ++++++++++++++++++++-------------------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/src/guide/reactivity.md b/src/guide/reactivity.md index 8cee3f8b..112e718d 100644 --- a/src/guide/reactivity.md +++ b/src/guide/reactivity.md @@ -33,19 +33,19 @@ console.log(sum) // Still 5 では、 JavaScript を使って以下の要素をどうやって実現するのでしょうか。 -As a high-level overview, there are a few things we need to be able to do: +ハイレベルな概要として、いくつかのことをできるようにする必要があります: -1. **Track when a value is read.** e.g. `val1 + val2` reads both `val1` and `val2`. -2. **Detect when a value changes.** e.g. When we assign `val1 = 3`. -3. **Re-run the code that read the value originally.** e.g. Run `sum = val1 + val2` again to update the value of `sum`. +1. **値が読み込まれたときに追跡する。** 例: `val1 + val2` は `val1` と `val2` の両方を読み込む。 +2. **値の変更を検知する。** 例: `val1 = 3` と入れるとき。 +3. **最初に値を読み込んだコードを再実行する。** 例: `sum = val1 + val2` を再度実行して、 `sum` の値を更新する。 -We can't do this directly using the code from the previous example but we'll come back to this example later to see how to adapt it to be compatible with Vue's reactivity system. +前の例のコードを使って直接これを行うことはできませんが、あとでこの例に戻って、 Vue のリアクティブなシステムと互換性をもたせる方法を見てみましょう。 -First, let's dig a bit deeper into how Vue implements the core reactivity requirements outlined above. +まずは、 Vue が上で説明した中核となるリアクティブな要件をどのように実装しているのか、もう少し掘り下げてみましょう。 -## How Vue Knows What Code Is Running +## Vue がどのコードを実行しているのか知る方法 -To be able to run our sum whenever the values change, the first thing we need to do is wrap it in a function: +値が変化したときにいつでも合計するためには、まず合計の算出を関数でラップする必要があります: ```js const updateSum = () => { @@ -53,13 +53,13 @@ const updateSum = () => { } ``` -But how do we tell Vue about this function? +しかし、どうやって Vue にこの機能を伝えるのでしょうか? -Vue keeps track of which function is currently running by using an *effect*. An effect is a wrapper around the function that initiates tracking just before the function is called. Vue knows which effect is running at any given point and can run it again when required. +Vue はどの関数が現在実行されているのかを、 **作用** を使って追跡します。 作用は、関数が呼び出される直前に追跡を開始する関数のラッパーです。 Vue はどの時点でどの作用が実行されているかを把握して、必要になったときに再度実行することができます。 -To understand that better, let's try to implement something similar ourselves, without Vue, to see how it might work. +そのことをより理解するために、似たようなことを Vue を抜きにして自分で実装してみましょう。 -What we need is something that can wrap our sum, like this: +必要なものは、このように合計の算出をラップできるものです: ```js createEffect(() => { @@ -67,46 +67,46 @@ createEffect(() => { }) ``` -We need `createEffect` to keep track of when the sum is running. We might implement it something like this: +合計がいつ実行されたのか追跡するために、 `createEffect` が必要です。次のような実装になるでしょう: ```js -// Maintain a stack of running effects +// 実行している作用のスタックを維持 const runningEffects = [] const createEffect = fn => { - // Wrap the passed fn in an effect function + // 渡された fn を effect 関数でラップ const effect = () => { runningEffects.push(effect) fn() runningEffects.pop() } - // Automatically run the effect immediately + // 自動的に作用をすぐに実行 effect() } ``` -When our effect is called it pushes itself onto the `runningEffects` array, before calling `fn`. Anything that needs to know which effect is currently running can check that array. +作用が呼び出されると `fn` を呼び出す前に、自分自身を `runningEffects` 配列の末尾に追加します。どの作用が現在実行されているかを知る必要があるものは、この配列を確認できます。 -Effects act as the starting point for many key features. For example, both component rendering and computed properties use effects internally. Any time something magically responds to data changes you can be pretty sure it has been wrapped in an effect. +作用は多くの重要な昨日の出発点となります。例えば、コンポーネントのレンダリングや算出プロパティはどちらも、内部的に作用を使っています。データの変更にいつでも魔法のように反応するものがあれば、それは間違いなく作用にラップされていると言えるでしょう。 -While Vue's public API doesn't include any way to create an effect directly, it does expose a function called `watchEffect` that behaves a lot like the `createEffect` function from our example. We'll discuss that in more detail [later in the guide](/guide/reactivity-computed-watchers.html#watcheffect). +Vue の公開 API には、作用を直接作成する方法は含まれていませんが、 `watchEffect` という関数が公開されています。この関数は先の例にある `createEffect` 関数によく似た振る舞いをします。これについて詳しくは [ガイドの後半](/guide/reactivity-computed-watchers.html#watcheffect) で説明します。 -But knowing what code is running is just one part of the puzzle. How does Vue know what values the effect uses and how does it know when they change? +しかし、どのコードが実行されているかを知ることは、パズルの一部にしかすぎません。Vue は、作用が使う値をどのように知り、いつ変更されたかをどのように知るのでしょうか? -## How Vue Tracks These Changes +## Vue が変更をどのように追跡するのか -We can't track reassignments of local variables like those in our earlier examples, there's just no mechanism for doing that in JavaScript. What we can track are changes to object properties. +先ほどの例のように、ローカル変数の再代入を追跡することはできません。 JavaScript にはそのような仕組みがないからです。オブジェクトのプロパティの変更は追跡することができます。 -When we return a plain JavaScript object from a component's `data` function, Vue will wrap that object in a [Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) with handlers for `get` and `set`. Proxies were introduced in ES6 and allow Vue 3 to avoid some of the reactivity caveats that existed in earlier versions of Vue. +コンポーネントの `data` 関数からプレーンな JavaScript オブジェクトを返すと、 Vue はそのオブジェクトを `get` と `set` ハンドラを持つ [Proxy](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Proxy) でラップします。プロキシは ES6 で導入されたもので、 Vue 3 では以前のバージョンの Vue にあったリアクティビティに関する注意点のいくつかを回避することができます。
-That was rather quick and requires some knowledge of [Proxies](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) to understand! So let’s dive in a bit. There’s a lot of literature on Proxies, but what you really need to know is that a **Proxy is an object that encases another object and allows you to intercept any interactions with that object.** +あまりに素っ気なく、理解するには [Proxy](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Proxy) の知識が必要です!もう少しだけ説明しましょう。プロキシについては多くの文献がありますが、本当に知っておくべきことは、 **プロキシとは他のオブジェクトをラップして、そのオブジェクトとのやりとりを傍受できるようにしたオブジェクトである** ということです。 -We use it like this: `new Proxy(target, handler)` +このように使います: `new Proxy(target, handler)` ```js const dinner = { @@ -127,11 +127,11 @@ console.log(proxy.meal) // tacos ``` -Here we've intercepted attempts to read properties of the target object. A handler function like this is also known as a *trap*. There are many different types of trap available, each handling a different type of interaction. +ここでは、対象のオブジェクトのプロパティを読み込む試みを傍受しています。このようなハンドラ関数は、 *トラップ* とも呼ばれています。トラップにはいろいろな種類があり、それぞれ異なるタイプのインタラクションを処理します。 -コンソールログ以外にも、ここでは思い通りの操作が可能です。必要な場合は、実際の値を返さ _ない_ ようにすることさえできます。これにより、プロキシは API の作成において強力なものになっています。 +コンソールログ以外にも、ここでは思い通りの操作が可能です。必要な場合は、実際の値を _返さない_ ようにすることさえできます。これにより、プロキシは API の作成において強力なものになっています。 -One challenge with using a Proxy is the `this` binding. We'd like any methods to be bound to the Proxy, rather than the target object, so that we can intercept them too. Thankfully, ES6 introduced another new feature, called `Reflect`, that allows us to make this problem disappear with minimal effort: +プロキシを使う際の 1 つの課題は `this` の束縛です。どのメソッドでも対象のオブジェクトではなく、プロキシに束縛されるようにして、それらを傍受できるようにしたいです。ありがたいことに、 ES6 では `Reflect` という新しい機能が導入され、最小限の労力でこの問題を解決できます: ```js{7} const dinner = { @@ -150,7 +150,7 @@ console.log(proxy.meal) // tacos ``` -The first step towards implementing reactivity with a Proxy is to track when a property is read. We do this in the handler, in a function called `track`, where we pass in the `target` and `property`: +プロキシでリアクティビティの実装をするための最初のステップは、プロパティが読み込まれたときに追跡することです。これはハンドラの中の `track` という関数で行い、 `target` と `property` を返します: ```js{7} const dinner = { @@ -170,9 +170,9 @@ console.log(proxy.meal) // tacos ``` -The implementation of `track` isn't shown here. It will check which *effect* is currently running and record that alongside the `target` and `property`. This is how Vue knows that the property is a dependency of the effect. +`track` の実装は、ここでは示されていません。これは、どの *作用* が現在実行されているのかをチェックして、 `target` と `property` を一緒に記録します。これにより、 Vue はプロパティが作用の依存関係にあることを知ることができます。 -Finally, we need to re-run the effect when the property value changes. For this we're going to need a `set` handler on our proxy: +最後に、プロパティの値が変わったら、作用を再度実行する必要があります。このためには、プロキシに `set` ハンドラが必要です: ```js const dinner = { @@ -196,15 +196,15 @@ console.log(proxy.meal) // tacos ``` -Remember this list from earlier? Now we have some answers to how Vue implements these key steps: +以前のこのリストを覚えているでしょうか?ここまでで Vue がこれらの重要なステップをどのように実装しているのか、いくつかの答えが得られました: -1. **Track when a value is read**: the `track` function in the proxy's `get` handler records the property and the current effect. -2. **Detect when that value changes**: the `set` handler is called on the proxy. -3. **Re-run the code that read the value originally:** the `trigger` function looks up which effects depend on the property and runs them. +1. **値が読み込まれたときに追跡する**: プロキシの `get` ハンドラ内にある `track` 関数が、プロパティと現在の作用を記録します。 +2. **値の変更を検知する**: プロキシの `set` ハンドラが呼び出されます。 +3. **最初に値を読み込んだコードを再実行する**: `trigger` 関数によって、どの作用がプロパティに依存しているか調べ、それらを実行します。 -The proxied object is invisible to the user, but under the hood it enables Vue to perform dependency-tracking and change-notification when properties are accessed or modified. One caveat is that console logging will format proxied objects differently, so you may want to install [vue-devtools](https://github.com/vuejs/vue-devtools) for a more inspection-friendly interface. +プロキシされたオブジェクトは、ユーザには見えませんが、内部では Vue が依存関係の追跡やプロパティがアクセスされたり変更されたりしたときの変更通知を行うことができます。注意点としては、コンソールログではプロキシされたオブジェクトのフォーマットが異なるため、 [vue-devtools](https://github.com/vuejs/vue-devtools) をインストールして、より検査しやすいインターフェイスにするとよいです。 -If we were to rewrite our original example using a component we might do it something like this: +最初の例をコンポーネントを使って書き直すと、次のようになります: ```js const vm = createApp({ @@ -228,11 +228,11 @@ vm.val1 = 3 console.log(vm.sum) // 6 ``` -The object returned by `data` will be wrapped in a reactive proxy and stored as `this.$data`. The properties `this.val1` and `this.val2` are aliases for `this.$data.val1` and `this.$data.val2` respectively, so they go through the same proxy. +`data` が返したオブジェクトは、リアクティブプロキシでラップされ、 `this.$data` として保存されます。プロパティの `this.val1` と `this.val2` は、それぞれ `this.$data.val1` と `this.$data.val2` のエイリアスなので、同じプロキシを経由します。 -Vue will wrap the function for `sum` in an effect. When we try to access `this.sum`, it will run that effect to calculate the value. The reactive proxy around `$data` will track that the properties `val1` and `val2` were read while that effect is running. +Vue は `sum` 関数を作用でラップします。 `this.sum` にアクセスしようとすると、値を計算するためにその作用が実行されます。 `$data` の周りにあるリアクティブプロキシは、その作用が実行されている間、 `val1` と `val2` プロパティが読み込まれたことを追跡します。 -As of Vue 3, our reactivity is now available in a [separate package](https://github.com/vuejs/vue-next/tree/master/packages/reactivity). The function that wraps `$data` in a proxy is called [`reactive`](/api/basic-reactivity.html#reactive). We can call this directly ourselves, allowing us to wrap an object in a reactive proxy without needing to use a component: +Vue 3 からは、リアクティビティが [別パッケージ](https://github.com/vuejs/vue-next/tree/master/packages/reactivity) になりました。プロキシで `$data` をラップする関数は [`reactive`](/api/basic-reactivity.html#reactive) と呼ばれています。これを自分で直接呼び出すと、コンポーネントを使わずにリアクティブプロキシでオブジェクトをラップすることができます: ```js const proxy = reactive({ @@ -241,13 +241,13 @@ const proxy = reactive({ }) ``` -We'll explore the functionality exposed by the reactivity package over the course of the next few pages of this guide. That includes functions like `reactive` and `watchEffect` that we've already met, as well as ways to use other reactivity features, such as `computed` and `watch`, without needing to create a component. +このガイドの続きでは、 reactivity パッケージが提供する機能について説明します。このパッケージにはすでに紹介した `reactive` や `watchEffect` といった関数が含まれるほか、コンポーネントを作ることなく `computed` や `watch` などの他のリアクティビティの機能を使う方法も含まれています。 ## プロキシされたオブジェクト Vue はリアクティブに作られたすべてのオブジェクトを内部的に追跡するため、常に同じオブジェクトに対して同じプロキシを返します。 -ネストされたオブジェクトがリアクティブプロキシからアクセスされると、次のようにそのオブジェクト _も_ 返却される前にプロキシに変換されます: +ネストされたオブジェクトがリアクティブプロキシからアクセスされると、次のように _そのオブジェクトも_ 返却される前にプロキシに変換されます: ```js{6-7} const handler = { @@ -255,7 +255,7 @@ const handler = { track(target, property) const value = Reflect.get(...arguments) if (isObject(value)) { - // Wrap the nested object in its own reactive proxy + // ネストしたオブジェクトを独自のリアクティブプロキシでラップする return reactive(value) } else { return value @@ -267,7 +267,7 @@ const handler = { ## プロキシとオリジナルの同一性 -プロキシを使用使うことにより、警戒すべき新しい注意点が発生します。プロキシ化されたオブジェクトは、同一性比較 (===) の点で元のオブジェクトと等しくないということです。 例えば: +プロキシを使用使うことにより、警戒すべき新しい注意点が発生します。プロキシ化されたオブジェクトは、同一性比較 (===) の点で元のオブジェクトと等しくないということです。例えば: ```js const obj = {} @@ -276,19 +276,19 @@ const wrapped = new Proxy(obj, handlers) console.log(obj === wrapped) // false ``` -Other operations that rely on strict equality comparisons can also be impacted, such as `.includes()` or `.indexOf()`. +`.includes()` や `.indexOf()` などの厳密な等値比較に依存する他の演算も、影響を受ける可能性があります。 -The best practice here is to never hold a reference to the original raw object and only work with the reactive version: +ここでのベストプラクティスは、オリジナルの raw オブジェクトへの参照を決して保持せずに、リアクティブ化したオブジェクトでのみ作業を行うことです。: ```js const obj = reactive({ count: 0 -}) // no reference to original +}) // オリジナルへの参照はなし ``` -This ensures that both equality comparisons and reactivity behave as expected. +これにより、均等比較とリアクティビティの両方が期待通りの振る舞いになることが保証されます。 -Note that Vue does not wrap primitive values such as numbers or strings in a Proxy, so you can still use `===` directly with those values: +注意点は Vue が数値や文字列などのプリミティブな値をプロキシでラップしないため、これらの値でも `===` を直接使うことができます: ```js const obj = reactive({ @@ -298,14 +298,14 @@ const obj = reactive({ console.log(obj.count === 0) // true ``` -## How Rendering Reacts to Changes +## 変更に対応するレンダリングの仕組み -The template for a component is compiled down into a [`render`](/guide/render-function.html) function. The `render` function creates the [VNodes](/guide/render-function.html#the-virtual-dom-tree) that describe how the component should be rendered. It is wrapped in an effect, allowing Vue to track the properties that are 'touched' while it is running. +コンポーネントのテンプレートは、 [`render`](/guide/render-function.html) 関数にコンパイルされます。 `render` 関数は、コンポーネントのレンダリング方法を記述する [VNode](/guide/render-function.html#仮想-dom-ツリー) を作成します。この関数は、作用にラップされていて、 Vue が実行中に 'touched' したプロパティを追跡できます。 -A `render` function is conceptually very similar to a `computed` property. Vue doesn't track exactly how dependencies are used, it only knows that they were used at some point while the function was running. If any of those properties subsequently changes, it will trigger the effect to run again, re-running the `render` function to generate new VNodes. These are then used to make the necessary changes to the DOM. +`render` 関数は、概念的に `computed` プロパティと非常によく似ています。 Vue はどのように依存関係が使われているかを正確に追跡しておらず、関数が実行中のある時点で使われていたことだけを知っています。これらのプロパティのいずれかが変更されると、作用の再実行が発火され、 `render` が再実行されて、新しい VNodes が生成されます。これらのプロパティは、 DOM に必要な変更を加えるために使われます。
-> Vue 2.x 以前を使用している場合は、それらのバージョンに存在する変更検出の注意点に興味があるかもしれません[詳細はこちらをご覧ください](change-detection.md)。 +> Vue 2.x 以前を使用している場合は、それらのバージョンに存在する変更検出の注意点に興味があるかもしれません [詳細はこちらをご覧ください](change-detection.md)。 From 61074c55d1d3788b104204a38fb49e9537aa4e49 Mon Sep 17 00:00:00 2001 From: Naoki Endoh Date: Mon, 10 May 2021 21:39:59 +0900 Subject: [PATCH 8/8] =?UTF-8?q?fix:=20=E6=98=A8=E6=97=A5=E2=86=92=E6=A9=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: kazuya kawaguchi --- src/guide/reactivity.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/guide/reactivity.md b/src/guide/reactivity.md index 112e718d..e4c95841 100644 --- a/src/guide/reactivity.md +++ b/src/guide/reactivity.md @@ -88,7 +88,7 @@ const createEffect = fn => { 作用が呼び出されると `fn` を呼び出す前に、自分自身を `runningEffects` 配列の末尾に追加します。どの作用が現在実行されているかを知る必要があるものは、この配列を確認できます。 -作用は多くの重要な昨日の出発点となります。例えば、コンポーネントのレンダリングや算出プロパティはどちらも、内部的に作用を使っています。データの変更にいつでも魔法のように反応するものがあれば、それは間違いなく作用にラップされていると言えるでしょう。 +作用は多くの重要な機能の出発点となります。例えば、コンポーネントのレンダリングや算出プロパティはどちらも、内部的に作用を使っています。データの変更にいつでも魔法のように反応するものがあれば、それは間違いなく作用にラップされていると言えるでしょう。 Vue の公開 API には、作用を直接作成する方法は含まれていませんが、 `watchEffect` という関数が公開されています。この関数は先の例にある `createEffect` 関数によく似た振る舞いをします。これについて詳しくは [ガイドの後半](/guide/reactivity-computed-watchers.html#watcheffect) で説明します。