From bbcc027a7e3b1df7bdfe647ca40437eeff2f92a3 Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 25 Feb 2020 21:48:23 -0500 Subject: [PATCH] Update Composition API: separate `watchEffect` and `watch` --- active-rfcs/0013-composition-api.md | 50 +++++++++++++++-------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/active-rfcs/0013-composition-api.md b/active-rfcs/0013-composition-api.md index 105b4f85..cb9c5073 100644 --- a/active-rfcs/0013-composition-api.md +++ b/active-rfcs/0013-composition-api.md @@ -65,7 +65,7 @@ In comparison, the APIs proposed in this RFC utilize mostly plain variables and ### API Introduction -Instead of bringing in new concepts, the APIs being proposed here are more about exposing Vue's core capabilities - such as creating and observing reactive state - as standalone functions. Here we will introduce a number of the most fundamental APIs and how they can be used in place of 2.x options to express in-component logic. Note this section focuses on introducing the basic ideas so it does not goes into full details for each API. Full API specs can be found in the [API Reference](https://vue-composition-api-rfc.netlify.com/api.html) section. +Instead of bringing in new concepts, the APIs being proposed here are more about exposing Vue's core capabilities - such as creating and observing reactive state - as standalone functions. Here we will introduce a number of the most fundamental APIs and how they can be used in place of 2.x options to express in-component logic. Note this section focuses on introducing the basic ideas so it does not go into full details for each API. Full API specs can be found in the [API Reference](./api) section. #### Reactive State and Side Effects @@ -82,24 +82,26 @@ const state = reactive({ `reactive` is the equivalent of the current `Vue.observable()` API in 2.x, renamed to avoid confusion with RxJS observables. Here, the returned `state` is a reactive object that all Vue users should be familiar with. -The essential use case for reactive state in Vue is that we can use it during render. Thanks to dependency tracking, the view automatically updates when reactive state changes. Rendering something in the DOM is considered a "side effect": our program is modifying state external to the program itself (the DOM). To apply and *automatically re-apply* a side effect based on reactive state, we can use the `watch` API: +The essential use case for reactive state in Vue is that we can use it during render. Thanks to dependency tracking, the view automatically updates when reactive state changes. Rendering something in the DOM is considered a "side effect": our program is modifying state external to the program itself (the DOM). To apply and *automatically re-apply* a side effect based on reactive state, we can use the `watchEffect` API: ``` js -import { reactive, watch } from 'vue' +import { reactive, watchEffect } from 'vue' const state = reactive({ count: 0 }) -watch(() => { +watchEffect(() => { document.body.innerHTML = `count is ${state.count}` }) ``` -`watch` expects a function that applies the desired side effect (in this case, setting `innerHTML`). It executes the function immediately, and tracks all the reactive state properties it used during the execution as dependencies. Here, `state.count` would be tracked as a dependency for this watcher after the initial execution. When `state.count` is mutated at a future time, the inner function will be executed again. +`watchEffect` expects a function that applies the desired side effect (in this case, setting `innerHTML`). It executes the function immediately, and tracks all the reactive state properties it used during the execution as dependencies. Here, `state.count` would be tracked as a dependency for this watcher after the initial execution. When `state.count` is mutated at a future time, the inner function will be executed again. This is the very essence of Vue's reactivity system. When you return an object from `data()` in a component, it is internally made reactive by `reactive()`. The template is compiled into a render function (think of it as a more efficient `innerHTML`) that makes use of these reactive properties. +> `watchEffect` is similar to the 2.x `watch` option, but it doesn't require separating the watched data source and the side effect callback. Composition API also provides a `watch` function that behaves exactly the same as the 2.x option. + Continuing the above example, this is how we would handle user input: ``` js @@ -113,7 +115,7 @@ document.body.addEventListener('click', increment) But with Vue's templating system we don't need to wrangle with `innerHTML` or manually attaching event listeners. Let's simplify the example with a hypothetical `renderTemplate` method so we can focus on the reactivity side: ``` js -import { reactive, watch } from 'vue' +import { reactive, watchEffect } from 'vue' const state = reactive({ count: 0 @@ -128,7 +130,7 @@ const renderContext = { increment } -watch(() => { +watchEffect(() => { // hypothetical internal code, NOT actual API renderTemplate( ``, @@ -157,7 +159,7 @@ What is `computed` returning here? If we take a guess at how `computed` is imple // simplified pseudo code function computed(getter) { let value - watch(() => { + watchEffect(() => { value = getter() }) return value @@ -176,7 +178,7 @@ function computed(getter) { const ref = { value: null } - watch(() => { + watchEffect(() => { ref.value = getter() }) return ref @@ -188,7 +190,7 @@ In addition, we also need to intercept read / write operations to the object's ` ``` js const double = computed(() => state.count * 2) -watch(() => { +watchEffect(() => { console.log(double.value) }) // -> 0 @@ -197,7 +199,7 @@ state.count++ // -> 2 **Here `double` is an object that we call a "ref", as it serves as a reactive reference to the internal value it is holding.** -> You might be aware that Vue already has the concept of "refs", but only for referencing DOM elements or component instances in templates ("template refs"). Check out [this](https://vue-composition-api-rfc.netlify.com/api.html#template-refs) to see how the new refs system can be used for both logical state and template refs. +> You might be aware that Vue already has the concept of "refs", but only for referencing DOM elements or component instances in templates ("template refs"). Check out [this](./api.html#template-refs) to see how the new refs system can be used for both logical state and template refs. In addition to computed refs, we can also directly create plain mutable refs using the `ref` API: @@ -231,7 +233,7 @@ const renderContext = { increment } -watch(() => { +watchEffect(() => { renderTemplate( ``, renderContext @@ -256,7 +258,7 @@ console.log(state.double) Our code so far already provides a working UI that can update based on user input - but the code runs only once and is not reusable. If we want to reuse the logic, a reasonable next step seems to be refactoring it into a function: ``` js -import { reactive, computed, watch } from 'vue' +import { reactive, computed, watchEffect } from 'vue' function setup() { const state = reactive({ @@ -276,7 +278,7 @@ function setup() { const renderContext = setup() -watch(() => { +watchEffect(() => { renderTemplate( `