That’s the compile part

(Compile part is divided into parser + optimizer + code generator),

Finally came to render, today to record the second part of render trilogy for myself,

(compile + render trilogy = compile + render generates Vnode + mount Vnode to page via update),

Update has a series of diff operations.

Hey, render is not much

Unter den unter den

The function of render should be clear

Compile generates the render function and returns the vNode

For example, there is this simple template


After compile, the corresponding render function is parsed as follows

function render() {    

    with(this) {        

        return _c('div', {            
  attrs: {   "data": 111   }  },  [_v(111)])  } } Copy the code

Look at this baffling render function, what’s in it?

No, basically there are two functions, and those are the two things we’re going to explore

_c , _v

Both functions create vNodes, but the process is different

And when the render function is executed, it will bind the corresponding instance of the template as a context object

The template is bound to the instance to which it belongs

Render. Call (instance) via the function of with

Calls to _c and _v are equivalent to vm._c and vm._v

What is a vm. _v

Now where does vm._v come from

function installRenderHelpers(target) {
    target._v = createTextVNode;
}

installRenderHelpers(Vue.prototype);
Copy the code

As you can see from above, each Vue instance inherits the _v method, so it can be called directly from vm._v

What is the function of createTextVNode for _v

Create a text node!!

Look at the source

function createTextVNode(val) {    

    return new VNode(        

        undefined.undefined.  undefined.String(val)   ) } Copy the code

Like this template


{{data}} is a string, but also exists as a child node, so it is treated as a text node

And the value of data is 111

The template above should then have the following Vnode structure


What is a vm. _c

_c is a big head, render the most important, first let’s see how he came to

function initRender(vm) {
    vm._c = function(a, b, c, d) {        

        return createElement(vm, a, b, c, d);

 }; }  Vue.prototype._init = function(options) {  initRender(this) } Copy the code

The _c method is bound to the instance when it is initialized

So, VM can call _c directly

If you look at the source code above, you can see that _c calls createElement internally

Take a look at the createElement source

The individual has simplified very simply and feels that it is ok not to deviate from our theme

function createElement(
    context, tag, data, children

) {    

 return _createElement(   context, tag, data, children  ) } Copy the code
function _createElement(

    context, tag, data, children
 ) {    
    var vnode;     if(if tag is a normal HTML tag) {  vnode = new VNode(  tag, data, children,  undefined.undefined. context  );  } . If the tag is a component name, special processing is done and the processing flow is omitted    if (Array.isArray(vnode))   return vnode   else {   / /... Dynamically binding style, class, code has been omitted   return vnode  } } Copy the code

You can see that createElement is basically a call to new VNode, and render is used to create a VNode

As you saw earlier, the render function passes a lot of arguments to _c, as shown below, and _C passes those arguments to the constructor VNode

_c('div'.    {            

        attrs: {"data": 111}

 },  [_v(111)] ) Copy the code

These parameters are passed to the Vnode and stored in the created Vnode

function VNode(
    tag, data, children, text

) {    

 this.tag = tag;   this.data = data;   this.children = children;   this.text = text; } Copy the code

And then you get a Vnode like this

{    

    tag:"div".
    data: {  attrs: {"data": 111}   },   children: [{  tag:undefined.  data:undefined.  text:111   }] } Copy the code

At this point, it’s clear how vNodes are created inside Render

But this is just one of those little simple renderings

If the render in the project, the data is a lot of, very complex

And the main thing we want to grasp is the main process

However, it is necessary to record another render, which is traversal

Traverse the related

Look at the template below

What is the result of this render

function render() {    

    with(this) {        

        return _c('div'.  _l(2.function(item, index) {   return _c('span')   })  )  } } Copy the code

If you see a _L, you must be behind the Vnode traversal

Again, _l and _v are registered at the same place installRenderHelpers

function installRenderHelpers(target) {    
    target._l = renderList;
}
Copy the code

Don’t hesitate to search out the source code for renderList

Skip to the next analysis ah, the source code is a bit long, although very simple

function renderList(val, _render) {    



    var ret, i, l, keys, key;    
    // go through the number group   if ( Array.isArray(val) ) {   ret = new Array(val.length);     // Call the function passed in, pass the value in, and store the result in the array  for (i = 0, l = val.length; i < l; i++) {  ret[i] = _render(val[i], i);  }  }     // Iterate over the numbers   else if (typeof val === 'number') {   ret = new Array(val);     // Call the function passed in, pass the value in, and store the result in the array   for (i = 0; i < val; i++) {  ret[i] = _render(i + 1, i);  }  }     // Iterate over the object   else if (typeof val =="object") {   keys = Object.keys(val);  ret = new Array(keys.length);     // Call the function passed in, pass the value in, and store the result in the array   for (i = 0, l = keys.length; i < l; i++) {  key = keys[i];  ret[i] = _render(val[key], key, i);  }  }     // Return the vNode array  return ret } Copy the code

See that renderList receives two arguments, val and render, which are passed in when _L is called, as shown below

 _l(2.function(item, index) {                
    return _c('span')
})
Copy the code

Val is 2 and _render is the function above

1 Traversal of data val

The data traversed is divided into three types, one is an object, one is a number, and one is an array

2 Single VNode render callback _render

The important thing is this callback

RenderList executes a callback for each iteration, passing each item and index into the callback

2. After the callback is complete, vNode is returned

3. Store vNodes in arrays and return arrays when traversal is complete

Look at the render function above, which passes in the number 2 and creates the span callback

_l(2.function(item, index) {    

    return _c('span')

})
Copy the code

_l returns an array of two span vnodes, which are then passed to _C as vnode.children

Render completes execution and gets this vnode

{    

    tag:"div".
    data:undefined.  children: [{  tag:"span".  data:undefined  }, {  tag:"span".  data:undefined   }] } Copy the code

All ash often simple ah, did not write before, I also feel that the content should be quite much, after writing found that can also

Of course there are other render

For example, if we want the template to contain filter, let’s see

Filters – Source version

The following example will be used as a template for this tutorial

There is a filter all to filter parentName

<div>{{parentName|all }}</div>
new Vue({    

    el:document.getElementsByTagName("div")[0],
  data(){   return {   parentName:111   }  },   filters:{  All (){return "I am a filter"}  } })  Copy the code

What does the filter of the page resolve into

First, the above example will be parsed into the following render function

(function() {      
    with(this) {            
        return _c('div'[            _v(_s(_f("all")(parentName)))            
        ])      
 } } Copy the code

So let’s continue with this code

  1. _c is the function that renders the component, which renders the root component

  2. This is an anonymous self-executing function that will be called later during rendering and will bind the current instance to scope

  3. The effect of with is to bind the variable access scope of the code in braces, so all variables inside are fetched from the instance

Then, you can see the ‘parentName | all’ is resolved into _f (‘ all ‘) (parentName)

How does that work?

Simple said is that when the match to | this symbol, will know that you use a filter, and then parsed into _f to obtain corresponding filter and calls, this process is not here

What is alpha f?

_f is the function that gets the concrete filter

1_f is registered with the Vue prototype when the Vue is initialized

/ / has been simplified

function installRenderHelpers(target) {

      target._s = toString;
 target._f = resolveFilter; }  installRenderHelpers(Vue.prototype); Copy the code

After scoping the current instance VM with the render function above, _f is taken from the VM as vm._f

_f is resolveFilter, a function that gets concrete filters

Get the all filter by using _f(“all”), resolveFilter will say below

How to obtain the following continue…….

How is the set filter called

_f is assigned to resolveFilter (resolveFilter)

/ / has been simplified

function resolveFilter(id) {    

    return resolveAsset(
  this.$options, 'filters', id, true   ) || identity  } Copy the code

Directive, vue.filter, vue.component.directive, vue.filter, vue.component.directive, vue.filter, vue.component.directive, vue.filter, vue.component.directive, vue.filter, vue.component.directive, vue.filter, vue.component.directive, vue.filter, vue.component.directive, vue.filter, vue.component.directive, vue.filter, vue.component.directive, vue.filter, vue.component.directive


This. options will get all options for the current component

You ask me why?

We know from the last question

  1. _f calls the vm.resolveFilter class vm.resolveFilter

  2. Therefore, the execution context of resolveFilter, this, is vm

  3. So, this.$options is the instance options

Next, we call resolveAsset to get the specific filter in the component options

Pass in the options for the current component, specifying the filters it wants to select, and specifying the name of the filter

function resolveAsset( 

    options, type, id, warnMissing
 ) {      
  // g: Get the filters option   var assets = options[type];   // g: returns the called filter   return assets[id] } Copy the code

The _f(“all”) process looks like this


  1. Get the Filters in the component option

  2. And then from the filters, get the filter all

  3. When the returned all filter function is executed, the value parentName to be filtered is passed

  4. Returns the filtered value


So, when the rendering function is parsed, it encounters the place where the filter is used, and after it gets the filter value, it can render to the page

_f(“all”)(parentName)) will become “I am the filter” in the render function, and finally render to the page


conclusion

Fitler simply takes one of the filters that you set from the component option Filters, calls it, and renders it using the return value that your function executes

Too simple, summary with no summary…….

When will render be executed?

If you’ve seen learning vue source code (4) handwritten VM.$mount method, I’m sure you already know


This is performed during the mount phase, as shown in the figure.

conclusion

Compile generates a render function for each template

Render as the second part of the render trilogy, the main function is to execute render and generate VNodes

Save all data bound to template to vNode

Vnodes are then generated to power the rendering of the third Diff in the trilogy

This completes the DOM mount

Here in fact, the basic has ended the render idea, but the source code has a static render, this has great help to improve the rendering performance, so you must see.

Yes, is static render, have seen learning vue source code (8) handwritten optimizer, should know what is static render

Static render is used to render nodes that do not change

How does Vue determine if a node is static

Ok, let’s start our text. On second thought, let’s start with a few questions

What does static render look like

How static render is generated and saved

3, static render how to implement

What is static Render

Static render is actually the same as render, it is executed Vnode

Render is static, no dynamic data binding, that is, no change

For example, a simple render looks like this


Dynamic data is bound and needs to be retrieved from the instance

_c('div',[_v(_s(aa))])
Copy the code

Static render looks like this


Without dynamic data, the static render will never change

_c('div',[_c('span',[_v("1")])])
Copy the code

Generate save static Render

Static render is generated in the generate phase in the same way as Render

For example, in a template, there are many static root nodes, like this


First, When Vue iterates through the template, it finds that the span and strong themselves and their children are static

The span and strong nodes themselves are given a property staticRoot, indicating that they are staticRoot nodes

The two static root nodes then generate their own static render

If you have been reading my Vue notes, you should have some impression here

after

Static render needs to be saved after it is generated, so where to save it?

It’s stored in an array called staticRenderFns, which is just pushed in

Of course, the static render pushed in is still a string, not a function

Take the template above, for example, for staticRenderFns, which contains two strings

staticRenderFns  = [
    "_c('span',[_c('b',[_v("1")]])".
    "_c('strong',[_c('b',[_v("1")]])"

] Copy the code

But it will be iterated one by one later to become an executable function

staticRenderFns = staticRenderFns.map(code= > {    

    return new Function(code)

});
Copy the code

So what is this staticRenderFns?

Each Vue instance has a separate staticRenderFns that holds the static render of the instance itself

The location of staticRenderFns is

vm.$options.staticRenderFns


Performing static Render

Static render needs to be used with render

Look at an example


The render function of this template is

_c('div'[    _m(0), 
    _v(_s(a), 
    _m(1) 
])
Copy the code

_m(0), _m(1) executes the static render function and returns Vnode

Render can then finish building the VNode tree

So what is alpha m?

This function is registered with the prototype of the Vue when the Vue is initialized, that is, each instance inherits _m

function installRenderHelpers(target) {
    target._m = renderStatic;
}


installRenderHelpers(Vue.prototype); Copy the code

Look at renderStatic

function renderStatic(index) {    



    var cached = this._staticTrees || (this._staticTrees = []);    
  var tree = cached[index];     // If the cache exists, return it directly  if (tree) return tree     // This is where render is executed   tree = cached[index] =   this.$options.staticRenderFns[index].call(   this.null.this   );     // Just mark the static and node ids  markStatic(tree, "__static__" + index, false);     return tree  } Copy the code

This function does several things

Perform static render

Cache static render results

3. Mark the Vnode as static render is executed

Let’s do it one by one

**1 Perform static render **

As we mentioned above, staticRender is stored in the array staticRenderFns

So this function takes an index value indicating which static render in the array to perform

After taking the static render, execute and bind the Vue instance as a context object

And then you get vNodes

Cache static render results

This step is to cache the Vnode obtained in the previous step

So where is the cache?

_staticTrees

This is an array, and each instance will have a separate _staticTrees to store its own vNodes from the static render execution

Take a look at the _staticTrees saved in the last template instance


3 marks vNodes from static render execution

We have performed static render to get the Vnode, this step is for marking purposes

What to mark

1. Add flag bit isStatic

2. Add a unique Vnode ID

In renderStatic we call the markStatic method when we see the markup, so let’s look at it now

function markStatic(
    tree, key

) {    

   if (Array.isArray(tree)) {     for (var i = 0; i < tree.length; i++) {     if ( tree[i] && typeoftree[i] ! = ='string') {     var node = tree[i]    node.isStatic = true;  node.key = key + "_" + i;  }  }  }  else {   tree.isStatic = true;  tree.key = key  } } Copy the code

Why add flag bit isStatic?

All the static flag bits we added earlier were for the template-generated AST

Here we add isStatic to Vnode to accomplish the purpose of Vue

The purpose of Vue is to optimize performance by updating as few nodes as possible when the page changes

So when Vue detects vNode. isStatic = true when the page changes, it does not compare this part of the content

Thus reducing the comparison time

The only id Vnode

A property that exists for each static root Vnode


I also did not expect static Vnode keys have any function, after all, do not need to compare, perhaps is easy to distinguish?

The last

Static render we finished, is not very simple, before I did not look at the source, I thought it was difficult

Now read, found also simple, but I also saw a few months…


In view of my limited ability, there will inevitably be omissions and mistakes, please forgive me, if there is any improper description, welcome to contact me backstage, get a red envelope

This article is formatted using MDNICE