Read the Vue official document series at 🎉
- Transition & Animation (1)
- Read the Vue official document rendering function & JSX
Say little nonsense, directly on the dry goods after reading summary!!
Template syntax
The advantages of template syntax are:
- Easy to use.
- But for
VNode
To optimize.
Because the template content is static, it can be analyzed to know which nodes and attributes are dynamic and which nodes and attributes are static. With this information, it can be targeted to update vNodes when creating or comparing (Diff) them — also known as patchFlags.
Of course, template syntax has some local learning costs and mental burdens, such as:
- ScopeSlots Scope slot pass values.
- Props, attrs, and domProps.
- Rich and powerful instruction system for user – oriented black box.
Vue recommends using templates in most cases to create your HTML. In some scenarios, however, you really need JavaScript’s full programming capabilities. You can use the render function or JSX for more flexibility.
Rendering function
The render function refers to the render option provided in the optionsAPI. The value is a function that takes a createELement method as a parameter to create the virtual DOM.
createElement
The createElement method takes three arguments.
createElement(<tag | component | asyncComponent> [, VNodeAttrData, Children])
Copy the code
<tag | component | asyncComponent>
The value can be an HTML tag in the form of a string
export default {
render(createElement) {
return createElement("h1"); }};Copy the code
Or receive a component object that has been converted to the form of a rendering function:
import { compileToFunctions } from "vue-template-compiler";
// Import a component that will be converted to a rendering function by the CLI
import Content from "./Content.vue";
// Customize a component in the form of a rendering function
const Badge = {
name: "Badge".render(createElement) {
return createElement("span", [
createElement("i", { attrs: { class: "icon icon-badge"}},this.$slots.default), ]); }};// Use the built-in compile method to convert template strings to render functions
const Title = compileToFunctions("<h1>Title</h1>");
export default {
render(createElement) {
return createElement("div", [
createElement(Title),
createElement(Content),
createElement(Badge, ["open"]),]); }};Copy the code
For Vue’s built-in components, note that the component name is lowercase and the Kebab-case naming is used.
export default {
render(createElement) {
return createElement("transition", { props: { appear: true } }, [
createElement("p"."this is p"),]); }};Copy the code
In addition, you can accept a method that returns an asynchronous component.
<script>
const asyncComponent = () = >import('./Test.vue');
const Badge = {
name:'Badge'.render(createElement){
return createElement('div'.'lazy component'); }};const LazyBadge = () = > new Promise((resolve, reject) = >{
setTimeout(() = >{
resolve(Badge);
}, 1000)})export default {
render(createElement) {
return createElement('div', [asyncComponent, LazyBadge]); }}; </script>Copy the code
VNodeAttrData
This parameter is optional. The value is the Attributes data object of the VNode.
Attributes (Object) | type | The sample | instructions |
---|---|---|---|
class | Object | {foo:true, bar:false} | withv-bind:class The API is the same |
style | Object | {color: ‘red’, fontSize: ’14 px’} | withv-bind:style The API is the same |
attrs | Object | {id:’avatar’, title:’user avatar’} | Plain HTML attributes |
props | Object | {level:1} | Components of the prop |
domProps | Object | { innerHTML: “Rewrite the title” } | Property of a DOM object, for exampleinnerHTML ,title ,lang ç‰ |
on | Object | {click: this.clickHandler} | Event listening using vue |
nativeOn | Object | {click: this.nativeClickHandler} | Use native event listening |
directives | Object | {name:’my-custom-directive’,value:’2′,expression:’1 + 1′,arg:’foo’,modifiers:{bar:true}} | Define custom directives to be used. |
slot | String | ‘default’ | If the component is a child of another component, specify the slot name for it. |
scopedSlots | Object | {default:(slotProps)=>createElement(‘span’, slotProps.message)} | A method that defines the content generated by a scoped slot, which is defined in the parent component, passed to the child component, and then called by the child component passing in the slot properties |
key | String | Math.random().toString() | Key that uniquely identifies the component |
ref | String | ‘myRef’ | – |
refsInFor | Boolean | true | If there are multiple elements in the render function with the same ref name, then$refs.myRef It’s going to be an array. |
DomProps example:
createElement(
"h1",
{ domProps: { innerHTML: "Rewrite the title"}},"This is the first level heading"
);
Copy the code
Scope slots and pass values:
Take a look at using scope slots based on template-based syntax.
<! --App.vue-->
<template>
<world-time>
<template v-slot:BeiJing="slotProps">
<b>{{ slotProps.time }}</b>
</template>
<template v-slot:London="slotProps">
{{ slotProps.time }}
</template>
</world-time>
</template>
<! --WorldTime.vue-->
<template>
<div>
<slot name="BeiJing" :time="new Date().toLocaleString()"></slot>
<slot name="London" :time="new Date().toUTCString()"></slot>
</div>
</template>
Copy the code
To transform it into a rendered function, declare the scopedSlots method where the component is used with the scopedSlots option, and then pass it as a property data object to the component world-time that defines the receive slot.
import WorldTime from "./WorldTime.vue";
export default {
components: { "world-time": WorldTime },
render: function(c) {
return c("world-time", {
scopedSlots: {
BeiJing: (props) = > c("b", props.message),
London: (props) = >props.message, }, }); }};Copy the code
The WorldTime component eventually calls the scope slot method, passing in parameters to be passed across the scope.
export default {
render(c) {
return c("div"[this.$scopedSlots.BeiJing({ message: new Date().toLocaleString() }),
this.$scopedSlots.London({ message: new Date().toUTCString() }), ]); }};Copy the code
Children
This parameter is optional. It is a sub-virtual node. Values can be text nodes:
export default {
render(c) {
return c("h1", { attrs: { style: "color:red"}},"This is a title!"); }};Copy the code
It can also be an array containing multiple virtual child nodes.
export default {
render(c) {
return c("ul", [c("li"."item1"), c("li"."item2")]); }};Copy the code
practice
Using the ability of the render function to render the entire component object, we can simulate the dynamic component technology
<script lang="ts">
import Vue from "vue";
export default Vue.extend({
name: "Comp".props: {
is: Object,},render(c) {
return c(this.is); }}); </script>Copy the code
Virtual DOM
Vue tracks how to change the real DOM in a document by creating a virtual DOM.
A Virtual DOM is a VNode tree composed of Virtual nodes. Each VNode describes information about the corresponding DOM node and its children.
All vNodes in the VNode tree must be unique. This is also consistent with the specification of real DOM nodes. If you add multiple times to a DOM object in a document, you are actually moving it.
For elements/components that really need to be repeated many times, you can do this using the factory function:
render: function (createElement) {
return createElement('div'.Array.apply(null, { length: 20 }).map(function () {
return createElement('p'.'hi')}}))Copy the code
JSX
JSX is syntax sugar that extends JavaScript syntax. JSX = Javascript + XML; XML is written in JavaScript, so it has the flexibility of JavaScript, but also has the semantic and intuitive HTML.
Vue-cli 3+ already has built-in support for JSX syntax. If it is earlier than this version, Babel needs to be configured separately
Basic examples:
<script>
export default {
data() {
return {
search: "".list: [{name: "peach".key: 1 },
{ name: "grape".key: 2 },
{ name: "mango".key: 3},]}; },computed: {
filters() {
return this.list.filter((item) = > item.name.indexOf(this.search) ! = = -1);
},
isEmpty() {
return !this.filters.length; }},render() {
const InputProps = {
class: { empty: this.isEmpty },
attrs: { type: "text".placeHolder: "Please enter search content". this.$attrs }, };return (
<div>
<input
{. InputProps}
onInput={(e)= >{ this.search = e.target.value; }} / ><ul>
{this.filters.map((item) => (
<li key={item.key}>{item.name}</li>
))}
</ul>
</div>); }};</script>
Copy the code
When developing with JSX in Vue, you also need to be careful to avoid the following situations:
- Can no longer
render
Method defined name ish
Because the variable name already exists, redefining it will result in a renaming error. - A label or component can only be assigned to another scalar inside a component instance, otherwise it is reported
createElement
Method not defined.
const h1 = <h1></h1>; // Uncaught ReferenceError: h is not defined
export default {
data(){
return {
elements: [<li></li>.<li></li>] //correct!}},methods: {getImage(){ //correct!
return <img />}},render() {
returnh1; }};Copy the code
- JSX’s functional components can be defined both inside and outside the component instance, but be careful not to wrap them in an object or an array, otherwise the compiler will not recognize them correctly and return a render function.
const H1 = () = > <h1></h1>;
const H3 = [() = > <h3></h3>]; //bad
export default {
functional: true.render() {
const H2 = () = > <h2></h2>;
return (
<div>
<H1 />
<H2 />
</div>); }};Copy the code
For more JSX practices in Vue, read the AntV or Vant source code.
Functional component
Functional components are stateless components.
A functional component is essentially a function that has no lifecycle, no state (responsive data), no instance (this context), and only passes the Prop through the second context of the render function, so it executes with a much lower rendering overhead.
We can create different forms of “functional components” through template syntax, rendering functions, and JSX, respectively.
Template syntax version functional component
<template functional>
<p>This is a functional component</p>
</template>
Copy the code
JSX version of functional components
const H1 = () = > <h1></h1>; // Subcomponents are also functional
export default {
functional: true.// Declare the current component to be functional.
render() {
return <H1 />; }};Copy the code
Render functional versions of functional components
export default {
functional: true.render(c, context) {
return c("p", context.data, "This is a functional component!"); }};Copy the code
For more information on functional component parameter passing, see > Functional Components
Templates, JSX, and render functions
Vue creates HTML in the following ways: templates, JSX, render functions, and eventually they all go the same way and are converted into “render functions”.
Both “JSX” and “render functions” need to be used in the Render option of the OptionsAPI, while the template syntax is used under the SFC’s separate template block.
There are several ways to generate a render function:
- call
compileToFunctions
Method to obtain the transformed results. - With the help of vue-CLI, import a component of template syntax to obtain the results of the compiled render function.
- use
import()
Import Vue components asynchronously, again with the help of vue-CLI. - Write the render function manually, paying attention to the structure of the wrapper:
{render: Æ’}
.
Finally, an important question is, when should we choose to use templates, JSX, or render functions?
features | The template | JSX | Rendering function |
---|---|---|---|
Does it need to be simple and intuitive? | U u u | u | Do things |
Is JS full programming capability required? | ✖ | End to end | U u u |
Do you need to define multiple components in a module (SFC)? | ✖ | U u u | U u u |
Do individual components or labels need to be assigned to a variable or defined as functional components for flexible assembly of content? | ✖ | U u u | U u u |
Whether better reuse is neededprops ,attrs ,events ? Better combination, decomposition, and binding, for example, through operators such as spread or reset. |
✖ | U u u | U u u |
Do YOU need to dynamically change the label or component name? | ✖ | ✖ | U u u |
Do I need to access variables or data outside of the component? | ✖ | U u u | U u u |
Do components, props, attrs, events, and so on need more flexibility to participate in conditional computation? | End to end | U u u | U u u |
Do I need to support Typescript? | Do things | U u u | U u u |
Do recursive calls need to be simpler and more intuitive? | Do things | U u u | U u u |
Do you need a more understandable way to use scopeSlots? | u | End to end | U u u |
Is JS full programming capability required?
Since component templates are separate from component logic, component logic that wants to operate on labels in templates must do so in a data-driven way.
export default {
props: {
reverse: Boolean,},render() {
const children = [
<li class="foo" style="color:red">
1
</li>.<li>2</li>.<li class="bar">3</li>,];if (this.reverse) {
children.reverse();
}
console.log(children);
return <ul>{children.map((item) => item)}</ul>; }};Copy the code
🤔, think about the template approach, we would have to solve this with a data-driven approach, specifically defining the corresponding data results, and then traversing the render.
Do I need to access variables or data outside of the component?
The execution context of the template is bound at compile time, and the target of the binding is the component instance this, so the variables to be interpolated in the template can only be retrieved from the data already mounted by the component instance, and not from other scopes, limiting the flexibility of the data sources involved in HTML content generation.
Template compilation
The Vue.com compile method converts a template string into a rendering function as a string and preserves the AST object.
Vue.compile('<p></p>');
/* { "ast": { "type": 1, "tag": "p", "attrsList": [], "attrsMap": {}, "rawAttrsMap": {}, "children": [], "plain": true, "static": true, "staticInFor": false, "staticRoot": false }, "render": "with(this){return _c('p')}", "staticRenderFns": [], "errors": [], "tips": [] } */
Copy the code
StaticRenderFns stores static nodes, such as components or labels that use the V-once directive.