Vue 3.0 RFC: Function-based Component API plugin Vue 3.0 RFC: Function-based Component API Plugin Vue 2.0 Vue-function-api, which can be used to experience the function-based Component API of VUE 3.0. For the purpose of learning, the author tried vue-function-API in the project in advance.
The author plans to write two articles, and this is the first one, which mainly focuses on the learning experience of Vue Function API. In the second part, I plan to read the core code principles of VUe-function-API, including Setup, Observable and Lifecycle.
This article should take about 15 to 20 minutes to read.
An overview of the
The organization of higher-order components in Vue 2.x and before it is more or less problematic, especially in projects that deal with repetitive logic. If the developer does not organize the project structure well, the component code can be criticized as “glue code.” In Vue 2.x and earlier versions, the solution to this problem is roughly as follows:
- mixin
- Functional component
- slots
The project I maintain also deals with a lot of reuse logic, and I have been trying to reuse components using mixins until now. Developers and maintainers are often confused by issues such as a component mixin multiple components at the same time, making it difficult to tell which mixin the corresponding property or method is written in. Second, namespace conflicts in mixins can also cause problems. It is difficult to guarantee that different mixins do not have the same property name. To this end, the official team proposed a request for comments in a functional form, the RFC: Function-based Component API. Using functional writing, can achieve more flexible reuse of components, developers in the organization of higher-order components, do not have to consider reuse in the component organization, can better focus on the development of the function itself.
Note: This article is an early experience of the Vue Function API using vue-function-API. This API is only the RFC of VUE 3.0, not the final Vue 3.x API. There may be inconsistencies after release.
Used in Vue 2.x
In order to experience Vue Function API in Vue 2.x in advance, it is necessary to introduce vue-function-API. The basic introduction method is as follows:
import Vue from 'vue';
import { plugin as VueFunctionApiPlugin } from 'vue-function-api';
Vue.use(VueFunctionApiPlugin);
Copy the code
Basic Component Examples
Let’s start with a basic example:
<template>
<div>
<span>count is {{ count }}</span>
<span>plusOne is {{ plusOne }}</span>
<button @click="increment">count++</button>
</div>
</template>
<script>
import Vue from 'vue';
import { value, computed, watch, onMounted } from 'vue-function-api';
export default {
setup(props, context) {
// reactive state
const count = value(0);
// computed state
const plusOne = computed((a)= > count.value + 1);
// method
const increment = (a)= > {
count.value++;
};
// watch
watch(
(a)= > count.value * 2,
val => {
console.log(`count * 2 is ${val}`); });// lifecycle
onMounted((a)= > {
console.log(`mounted`);
});
// expose bindings on render context
return{ count, plusOne, increment, }; }};</script>
Copy the code
Break down
setup
The setup Function is the main logic of the functional writing built by the Vue Function API. It is called when a component is created. The Function takes two arguments: props passed by the parent component and the context of the current component. If you look at the following example, you can get the following property values in context:
const MyComponent = {
props: {
name: String
},
setup(props, context) {
console.log(props.name);
// context.attrs
// context.slots
// context.refs
// context.emit
// context.parent
// context.root}}Copy the code
value & state
The value function creates a wrapper object that contains a reactive property value:
So why use value, because in JavaScript, basic types and no reference, in order to ensure that property is responsive, can only be object to realize the aid of the packing, the advantage is component state will be saved in the form of reference, which can be invoked in the setup of different modules of function in the form of parameters, It can reuse logic and realize responsiveness easily.
Getting the value of the wrapped object directly must use.value, but if the wrapped object is a property of another reactive object, the wrapped object is automatically expanded internally by proxy. It is also automatically expanded in the context of template rendering.
import { state, value } from 'vue-function-api';
const MyComponent = {
setup() {
const count = value(0);
const obj = state({
count,
});
console.log(obj.count) // As a property of another responsive object, it is automatically expanded
obj.count++ // As a property of another responsive object, it is automatically expanded
count.value++ // To get a responsive object directly, use.value
return {
count,
};
},
template: `<button @click="count++">{{ count }}</button>`};Copy the code
If a state does not need to be modified responsively in different functions, state can be used to create a reactive object, which is not a wrapper object and does not require a. Value value.
watch & computed
The basic concepts of Watch and computed are the same as watch and computed in Vue 2.x, where watch can be used to track state changes to perform some subsequent operations, and computed for properties to be recalculated depending on property changes.
Computed returns a read-only wrapper object that can be returned by the setup function just as a normal wrapper object, so that computed properties can be used in the template context. You can take two arguments, the first of which returns the current computed property value, and computed is writable when you pass the second argument.
import { value, computed } from 'vue-function-api';
const count = value(0);
const countPlusOne = computed((a)= > count.value + 1);
console.log(countPlusOne.value); / / 1
count.value++;
console.log(countPlusOne.value); / / 2
// Writable computed attribute values
const writableComputed = computed(
// read
() => count.value + 1.// write
val => {
count.value = val - 1; });Copy the code
The first parameter of watch, like computed, returns the value of the listened wrapper object property, but you need to pass two additional parameters: the second is a callback function that is triggered when the data source changes, and the third is options. The default behavior is different from Vue 2.x:
- Lazy: Whether the callback function is called once the component is created. In contrast to Vue 2.x, lazy defaults to false and is called once when the component is created.
- Deep: the same as deep in Vue 2.x
- Flush: Three optional values are ‘post’ (call callback after rendering, i.e., nextTick), ‘pre’ (call callback before rendering, i.e., nextTick), and ‘sync’ (trigger synchronization). The default is ‘post’.
// Double is a computational wrapper object
const double = computed((a)= > count.value * 2);
watch(double, value => {
console.log('double the count is: ', value);
}); // -> double the count is: 0
count.value++; // -> double the count is: 2
Copy the code
When watch has multiple wrapped object attributes, the parameters can be passed as an array, and, like Vue 2.x’s vm.$watch, watch returns an unlisten function:
const stop = watch(
[valueA, () => valueB.value],
([a, b], [prevA, prevB]) => {
console.log(`a is: ${a}`);
console.log(`b is: ${b}`); }); stop();Copy the code
Note: in the first draft of the RFC: function-based component API, there was a mention of effect-cleanup, which was used to cleanup side effects in special cases, and has been removed from the proposal.
The life cycle
All existing hook cycles have corresponding hook functions created in the form of onXXX. However, the deStoryed hook function needs to be unmounted instead:
import { onMounted, onUpdated, onUnmounted } from 'vue-function-api';
const MyComponent = {
setup() {
onMounted((a)= > {
console.log('mounted! ');
});
onUpdated((a)= > {
console.log('updated! ');
});
// Adjust to unmounted
onUnmounted((a)= > {
console.log('unmounted! '); }); }};Copy the code
Some think
The above description mainly extracts the common parts of Vue Function API, not the RFC: The function-based Component API includes all the advantages of dependency injection and TypeScript type derivation. For more information about the function-based Component API, check out the RFC: Function-based Component API. I also saw some more comments on the function-based Component API discussion board:
-
Due to the underlying design, setup cannot get the component instance this. I also encountered this problem when I tried to experience it. I expect the official release of Vue 3.x can improve this problem.
-
The issue of having to use wrapped objects for values of primitive types is the most hotly debated in the RFC discussion section, where.value must be used for wrapped properties to preserve TypeScript type inference, reusability, and Vue data listening
-
The proposed Amendment Proposal to Function-based Component API has been discussed in terms of unclear naming of the value and state methods of the wrapped object, which may lead to misleading developers:
setup() {
const state = reactive({
count: 0});const double = computed((a)= > state.count * 2);
function increment() {
state.count++;
}
return {
...toBindings(state), // retains reactivity on mutations made to `state`
double,
increment,
};
}
Copy the code
- The introduction of
reactive
API andbinding
API,reactive
The API is similar tostate
API,binding
The API is similar tovalue
API. - The name of the method used previously
state
Could be used as a component state object in Vue 2.x, leading to variable namespace conflicts, which the team believes willstate
API was renamedreactive
More elegant. Developers can writeconst state = ...
And then throughstate.xxxx
This is also a relatively natural way to retrieve component state. value
The lack of elegance does occur when methods are used to encapsulate primitive types.value
Developers may forget to use it when evaluating the wrapped object directly.value
, the amendment proposedreactive
API, which means to create a responsive object, initialize the statestate
Use thereactive
Created to preserve each attributegetter
andsetter
This allows both type derivation and reactive references to be shared across different modules.- but
reactive
May cause the following problems and need to be introducedbinding
API. Solve, such as usingreactive
Creates a reactive object on which the extended operator is used.
Object will be lostgetter
andsetter
To providetoBindings
Method can preserve a reactive form of state.
In the next article, I will read the core code principles of VUe-function-API, including Setup, Observable, Lifecycle, etc., and explore the changes that the Vue Function API may bring to us from the inside.
Of course, the Vue Function API is still in the discussion stage, and Vue 3.0 is still under development. Let’s look forward to the release of the first version of Vue 3.0 in the second half of the year, hopefully to bring us more surprises.