preface

Those of you who follow React should know that React introduced Hooks APIS in nineteen, which allowed us to discard obscure classes and move entirely to functional components. More importantly, it provides the ability to extract state logic from components, break them down in fine-grained business logic, and reuse business logic between components.

The Vue function-based API RFC (later rewritten as the Composition API) was released in Uma as a core feature of Vue3, so you can see its importance.

Let’s move on from React and take a look at the Composition API.

Introduce a,

1.1 What is the Composition API?

A new way of writing Vue components, introduced in Vue 3, allows the options related to component logic in 2.x to be redesigned as API functions.

1.2 Basic Examples

We use the Composition API to refactor the left side, which is essentially a setup function that separates component options and returns the variables used by the template. The result is no different from defining variables in data or methods.

Of course, it’s fine to mix setup with 2.x options, and the variables returned by setup can still be accessed through this.xx.

❓ what’s the point of migrating the code to the setup function and turning it into a long piece of spaghetti code 🤔

1.3 Further examples

This time we have taken the setup code a step further and removed the useMouse composite functions that encapsulate the mouse-position listening logic. UseMouse only needs the state (x, Y) to be exposed to the component to be returned as a responsive data source to be used in our component code.

For Component, there is no logical code to listen for mouse position, just call useMouse to get x and y.

❓ I can also do this with Mixins 🤔

1.4 Further examples

First, we understand what useDark does by using the template. It returns a Boolean value indicating whether it is night mode or not, and we can quickly get a toggle function by useToggle.

When we look at useDark, we can see that while it is a composite function, it also calls other composite functions usePreferredDark and useLocalStorage to get the system theme color and local configuration color. Of course, we don’t need to worry too much about their implementation, just focus on processing the results and returning results that take precedence over local configuration colors.

The combinatorial function call relationship is shown below:

The bottom layer of combinatorial functions relies on other combinatorial functions. For example, useLocalStorage relies on useStorage that encapsulates the logic of SessionStorage and LocalStorage. UsePreferredDark relies on the useMediaQuery that encapsulates the window.matchmedia logic. They all respond to state changes with useEventListener and can be automatically uninstalled.

Each of the functions involved can be used independently and has its own responsibilities, and then combined.

This example comes from VueUse.

Of course, you can’t always write such generic code, but you can also use the Composition API in your business to break up the business logic and let each Composition function take care of its own business, such as buried point reporting, RichTextPanel component parameters and callbacks, page control logic, and so on. The state and methods within these components can now be extracted into composite functions, either for reuse or simply to make the.vue file look cleaner.

Second, the API

2.1 setup

Setup is the entry point to the composite API. It is executed on beforeCreate and created and only once.

So we don’t need onBeforeCreate and onCreated. The code for these hooks should be written directly in the setup function.

It takes two arguments:

  1. Responsive props

  2. Non-responsive context, including attrs, slots, emit

It executes before the component instance is created, so we don’t have access to component options like Data, computed, and Methods.

If it returns an object, the object’s property can be accessed in options or templates.

2.1.1 Return to render function

With Babel Plugin you can return JSX: feat: add @vue/ composition-API support #142

2.1.2 getCurrentInstance

If you really need to access a component instance, you can use getCurrentInstance().

Only available in setup, lifecycle hooks, and composition functions.

2.2 reactive

Returns a reactive copy of the object, equivalent to vue.Observable (obj).

This reactive transformation is a “deep transformation” — it affects all properties passed by nested objects.

How do we understand the response here? When we use reactive objects during rendering, the view updates automatically when the value changes, just as it changes the value hanging in the data object.

2.2.1 Primitive value types cannot be proxy

The reactive() function can delegate an object, but not a value of a primitive type. This is because the primitive value type has only values but no references and cannot track subsequent changes to variables.

2.2.2 Response Lost

2.3 ref

Receives a value and returns a responsive, mutable wrapper object.

It has only one attribute:.value, which points to the internally wrapped value. This value can be modified directly.

We can’t use Reactive to handle primitive value types, but we can create an object that will hang the primitive value under its property name and pass it to Reactive. Vue provides the REF to do this for us.

2.3.1 Automatic Unpacking (Without adding.value)

  1. Ref is automatically unpacked in the template

2. Watch can directly accept ref as the listening object and directly return the unpacked value in the callback function.

3. Unpack nested refs using Reactive ()/readonly()

But when a REF is accessed from Array or native collection types such as Map, unpacking is not done.

2.3.2 unref (Reverse operation of ref)

Pass a ref, return ref. Value, otherwise return as is.

Entangled with whether you need to add the value (such as the type of a function parameter is Ref | number) to value, can use unref to unpack.

2.3.3 Reusing an existing REF

Pass a ref to the ref() constructor, which returns it as is.

If your function needs to return a ref but is not sure of the type of argument, you can return ref(param) directly. If ref is used, it will be reused, if not, a new ref will be returned.

2.3.4 You can use ES6 to deconstruct syntax

Deconstructing an object whose value is ref does not lose its response.

2.3.5 This.$refs solution

In the virtual DOM patching algorithm, if the REF key of a VNode corresponds to the REF in the rendering context, the corresponding element or component instance of a VNode will be assigned to the value of that REF.

Since this is done during virtual DOM mounting/patching, ref will only get the assignment after the initial rendering.

2.4 toRefs

Convert reactive objects to normal objects, and convert the value of each property to ref.

It can be used to solve the problem of reactive or props losing responsiveness when deconstructing.

2.5 the computed

Receives the getter function and returns a read-only reactive ref object.

If the argument is an object with get and set functions, the result will be a writable responsive ref object.

2.6 watch

The first parameter receives the data source and can be:

  • Getter function, ref

  • An array containing both of the above types (that is, multiple sources can be watched, with any change triggering a callback)

The second argument is a callback function that is fired when the data source changes.

2.6.1 Stop Observation

Watch () returns a function that stops the observation:

If watch() is called in a component setup() or lifecycle function, the Watcher is automatically stopped when the current component is destroyed

2.6.2 cleaning effect

The third argument watcher’s callback receives is a function that registers the cleanup operation.

Sometimes when the data source being observed changes, we may need to clean up the side effects that were previously performed. For example, if an asynchronous operation changes the data before it completes, we may have to undo the previous operation that we are still waiting on.

Call this function to register a cleanup function when:

  • Before the callback is called again

  • Before Watcher was stopped

Third, design motivation

3.1 From Class API to Function API to Composition API

We can see in Vue RFC the process from the original Class API to the function-based Component API taking the essence of its FP, and then to the revised Composition API adopting Reactivity.

3.2 What problems are solved

2. X object API:

  • Easy to reuse.

  • State can be detached from components and put into composition functions.

  • Clearer logic.

  • Code can be organized by function/logic rather than scattered among options.

  • Better TypeScript type support.

  • Class-based apis have a TS type problem, while function-based apis are naturally friendly to type derivation because TS has complete support for function parameters, return values, and generics.

  • Smaller packing volume.

  • The function-based API each function can be introduced separately as named ES export, making them tree-shaking friendly. Code associated with unused apis can be removed when the final package is completed.

  • Also, code written based on the function API is more efficient because all function names and variable names inside the setup function body can be compressed, but property/method names of objects and classes cannot.

3.3 Compare the reuse mode of 2.x

Vue 2.x API we have some common logic reuse patterns, including:

  • Mixins

  • Higher-order Components (aka HOCs)

  • Renderless Components (Components based on Scoped slots/scope slots encapsulation logic)

In general, these modes have the following problems:

  • The source of the data in the template is unclear.

  • For example, when multiple mixins are used in a component, it can be difficult to tell from the template which mixin a property comes from. HOC has a similar problem.

  • Using the Composition API, you can simply look at the setup function to see where the data comes from.

  • Namespace conflict.

  • There is no guarantee that mixins developed by different developers won’t use exactly the same properties or method names. HOC has a similar problem with the injected props.

  • Composition functions can be renamed in both the use and return phases of the rendering layer.

  • Performance.

  • Both HOC and Renderless Components require additional component instances to be nested to encapsulate logic, resulting in unnecessary performance overhead.

  • Composite functions do not incur the performance penalty of creating additional component instances.

3.4 Contrast React Hooks

Composition API borrowed from React Hooks, both of which have the same function-based extraction and reuse of logic, but because of the nature of the framework the two implementations are very different.

React Hooks are called every time a component is rendered, by implicitly attaching state to the current internal component node and fetching it in order of invocation at the next rendering. Vue setup(), on the other hand, is called only once per component instance at initialization, and the state is stored in the setup() closure by reference.

That is, the Composition API compares to React Hooks:

  • More intuitive with JavaScript overall;

  • Mutable.

  • Can be called conditionally without restriction of call order;

  • Hooks need to use subscripts to get the corresponding state.

  • It does not cause engine optimization or GC stress by constantly creating a large number of inline functions during subsequent updates;

  • Each rendering of Hooks is a separate closure.

  • You don’t always need to use useCallback to cache callbacks to child components to prevent overupdating;

  • If you pass functions to child components, each render will be treated as new props.

  • You don’t need to worry about to send the wrong depend on the array to useEffect/useMemo useCallback leading to use outdated values in the callback.

  • Vue’s dependency collection is fully automated and can be updated with minimal granularity.

This is all based on Vue’s responsive update capability.

Those interested can see how heavy the React Hooks mental model is by following the link below:

Hooks FAQ

Use React Hooks to declare setInterval

UseEffect complete guide

3.5 Does it have disadvantages?

3.5.1 Difference between REF and Reactive

ref

reactive

You can handle raw values as well as objects

Can only handle objects

Value needs to be added, and automatic unpacking needs to be considered

There is no need to add.value

There are separate types

It’s no different than a normal object, you can’t tell whether it’s reactive by type, right

You can use destruct assignment

You can’t use deconstructed assignment because you lose the response

You need to use the arrow function wrapper to use watch

3.5.2 Consider reactive and.value

While writing the Composition API is useeffect-free and uses the same lifecycle hook functions, Vue exposes the reactive API to the fact that when writing code, The need to consider whether or not the reactive, ref and reactive are missing, also imposes a certain mental burden.

conclusion

Lifecycle oriented programming vs. Business logic Composition API is optimized for extracting and reusing component logic. Extracting logic is much easier, so we don’t have to extract functions only when we need to reuse logic, we can also extract functions for better organization of code. This prevents the code for each logical task from being sliced into multiple pieces scattered around. Of course, if the component is simple enough, we can still just use the Options API and write the code as usual.

Can I use it in Vue 2? Yes, the official team provided a plugin for Vue 2: @vue/composition-api. They also plan to make the Composition API native to Vue 2.7.

reference

The official documentation

Vue Function-based API RFC

VueUse author Anthony Fu shares the composable Vue

Have an in-depth understanding of the Vue3 Reactivity API