Usage Scenarios and Source Analysis of Functional Components of Vue Advanced Components
- 2021-12-04 18:09:19
- OfStack
Introduction
Vue provides a functionalized component that makes the component stateless and instantiless. In principle, all subcomponents go through the process of instantiation, but simple function components do not have this process. It can be simply understood as a middle layer, which only processes data and does not create instances. Because of this behavior, its rendering overhead will be much lower. The actual application scenario is that when we need to choose one of multiple components to render, or process data before transferring data such as children, props and data to sub-components, we can use functional components to complete it, which is essentially an external packaging of components.
Usage scenario
Defines two component objects, test1, test2
var test1 = {
props: ['msg'],
render: function (createElement, context) {
return createElement('h1', this.msg)
}
}
var test2 = {
props: ['msg'],
render: function (createElement, context) {
return createElement('h2', this.msg)
}
}
Defines 1 functional component, which will select 1 component for selection according to the calculation result
Vue.component('test3', {
// Flags for functional components functional Set to true
functional: true,
props: ['msg'],
render: function (createElement, context) {
var get = function() {
return test1
}
return createElement(get(), context)
}
})
Use of functional components
<test3 :msg="msg" id="test">
</test3>
new Vue({
el: '#app',
data: {
msg: 'test'
}
})
The final rendering result is:
<h2>test</h2>
Source code analysis
Functional components set the functional property to true in the object definition of the component, which is the key to distinguish ordinary components from functional components. Similarly, when a subcomponent placeholder is encountered, it will enter createComponent to create a subcomponent Vnode. Because of the functional attribute, the code goes into the branch of the functional component and returns the result of the createFunctionalComponent call. Note that after createFunctionalComponent is executed, the subsequent logic for creating child Vnode will not be executed, which is why there will be no child Vnode to instantiate child components during the process of creating real nodes. (No instances)
function createComponent(){
...
if (isTrue(Ctor.options.functional)) {
return createFunctionalComponent(Ctor, propsData, data, context, children)
}
}
The createFunctionalComponent method detects and merges the incoming data, instantiates FunctionalRenderContext, and finally calls the render method customized by the functional component to perform the rendering process.
function createFunctionalComponent(
Ctor, // Functional component constructor
propsData, // Object of the incoming component props
data, // Object passed in by the placeholder component attr Attribute
context, // vue Instances
children// Child node
){
// Data detection merging
var options = Ctor.options;
var props = {};
var propOptions = options.props;
if (isDef(propOptions)) {
for (var key in propOptions) {
props[key] = validateProp(key, propOptions, propsData || emptyObject);
}
} else {
// Merge attrs
if (isDef(data.attrs)) { mergeProps(props, data.attrs); }
// Merge props
if (isDef(data.props)) { mergeProps(props, data.props); }
}
var renderContext = new FunctionalRenderContext(data,props,children,contextVm,Ctor);
// Call the custom in the functional component render Function
var vnode = options.render.call(null, renderContext._c, renderContext)
}
The ultimate goal of the FunctionalRenderContext class is to define an render method that is different from the real component rendering.
function FunctionalRenderContext() {
// Omit other logic
this._c = function (a, b, c, d) { return createElement(contextVm, a, b, c, d, needNormalization); };
}
When the render function is executed, the createElement method will be called recursively. At this time, the component is already a real component, and the normal component mounting process begins to be executed.
Question: Why do functional components need to define a different createElement method? -The difference between the functional component createElement and the previous only 1 is that the last parameter is different. As mentioned in the previous chapter, createElement will decide whether to flatten the sub-Vnode according to the last parameter. Under normal circumstances, the compiled results of children are of Vnode type. Only the functional component is special, and it can return an array. At this time, flatten is necessary. Let's look at the following example:
Vue.component('test', {
functional: true,
render: function (createElement, context) {
return context.slots().default
}
})
<test>
<p>slot1</p>
<p>slot</p>
</test>
At this time, the render function of the functional component test returns Vnode of two slot, which exists in the form of array, which is the scene that needs to be flattened.
Simple summary of 1 functional components, from the source code can be seen that functional components do not have the process of instantiating components like ordinary components, so there is no process including the life cycle of components and the data management of components. It will only receive the data transmitted to components intact and process them, and render the required content. Therefore, as a pure function, the rendering overhead can also be greatly reduced.