Writing an article is not easy, click a like brother focus on Vue source code sharing, the article is divided into vernacular version and source version, vernacular version to help understand the working principle, source version to help understand internal details, let us study together based on Vue version [2.5.17]

If you think the layout is ugly, please click the link below or pull to the following public account can also be

Compile – Source version of the optimize tag static node

Compile the three steps, parse we have covered

And now the second step, optimize is a little bit of a big deal, but it’s a big deal, so it’s an update optimization, it’s a big deal

Where is optimize, right after parse and before generate

var ast = parse(template.trim(), options);



if(options.optimize ! = =false) {

    optimize(ast, options);
}



var code = generate(ast, options);
Copy the code

The above code is in the function baseCompile, if you want to know, see here Compile – the main process from new instances to the end of Compile

And what is optimize?

The goal of Vue’s official annotation optimizer is to traverse the generated template AST tree, detecting purely static subtrees, that is, DOM that never needs to change. Once we detect these subtrees, we can: 1) make them constant so we don’t need to create new nodes for them every time we re-render; 2) skip them completely during the patch process.

How does that work?

Give the static AST node the property static, when the node is static

el.static = true
Copy the code

Here is the source code

function optimize(root, options) {    



    if(! root)return


    // first pass: mark all non-static nodes.
    markStaticThe $1(root);    



    // second pass: mark static roots.

    markStaticRoots(root);
}
Copy the code

There are two main functions called, which will be analyzed separately

But before we do that, let’s look at a function that is the workhorse for determining static nodes

Pass in the AST node directly, judge the various combinations, and add the static attribute to the AST node

functionIsStatic (node) {// Literal expressionif (node.type === 2) return false/ / plain textif (node.type === 3) return true



    return(/ / set the v - pre instructions, said don't have to parse the node. The pre | | (! Node.hasbindings && // No dynamic bindings! node.if && ! Node. for && // There is no v-if, v-for instruction! ['slot'.'component'].indexof (Node.tag)>-1 && isPlatformReservedTag(Node.tag) && isDirectChildOfTemplateFor(node) && Object.keys(node).every(isStaticKey) ) ) }Copy the code

If you want to judge a static node, you have to go through the following seven criteria (the code above is listed)

1 Check whether v-Pre exists

If the directive v-pre is added, then node.pre is true, indicating that all nodes are not parsed

Node. HasBindings cannot exist

Node.hasbindings is set to true when a node hasBindings for Vue attributes, such as directives, events, etc

3 Node. if and Node. for cannot exist

Similarly, node.if or node.for is true when a node has v-if or v-for

4 The node name cannot be slot or Component

Because they are compiled dynamically, they are not static

So no slot or component can be a static node

5isPlatformReservedTag(node.tag)

IsPlatformReservedTag is used to determine if the tag is a normal HTML tag. What tags are there?

The tags must be normal HTML tags, The following HTML, body, base, head, link, meta, style, title, address, the article, value, footer, header, h1, h2, h3, h4, h5, and h6, hgroup, nav, section div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,rtc,ruby s,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video embed,object,param,source,canvas,script,noscript,del,ins

caption,col,colgroup,table,thead,tbody,td,th,tr

button,datalist,section,form,input,label,legend,meter,optgroup,option

output,progress,select,textarea

details,dialog,menu,menuitem,summary

content,element,shadow,template,blockquote,iframe,tfoot

svg,animate,circle,clippath,cursor,defs,desc,ellipse,filter,font-face

foreignObject,g,glyph,image,line,marker,mask,missing-glyph,path,pattern

polygon,polyline,rect,switch,symbol,text,textpath,tspan,use,view
Copy the code

Is not a lot of, ha ha, especially big true fierce, estimated collected a lot of, I think should be useful, all put up

6isDirectChildOfTemplateFor(node)

Take a look at the source code for this function

function isDirectChildOfTemplateFor(node) {    



    while (node.parent) {

        node = node.parent;        



        if(node.tag ! = ='template') {            

            return false

        }        



        if (node.for) {            

            return true}}return false

}
Copy the code

Indicates that none of the above nodes can be template or have v-for

7Object.keys(node).every(isStaticKey)

IsStaticKey is a function that determines whether the attribute passed in is in the range below

type,tag,attrsList,attrsMap,plain,parent,children,attrs

An AST like this

<div style="" ></div>



{    

    attrsList: []

    attrsMap: {style: ""}
    children: []
    parent: undefined
    plain: false
    tag: "div"
    type: 1}Copy the code

All of the attributes of the ast above are static in the range of attributes listed above by isStaticKey, so this is a static node

This node is not a static AST when you have attributes other than those

And then let’s look at the two functions that come into Optimize

And markStaticRoot markStatic $1


Mark static nodes

How do you mark a node as static, just in markStatic$1

// Marks whether the node is staticfunction markStaticThe $1(node) {

    
    node.static = isStatic(node);    



    if(node.type ! = = 1)return// Do not set component slot content to static. // This avoids // 1. Components cannot change slot nodes. // 2if(// Normal THML tags are processed down, components are not! IsPlatformReservedTag (Node. tag) && // The tag name is slot. = ='slot'&& // Inline-tempalte is used to proceed to node.attrsmap ['inline-template'] == null
    ) {        

        return} // If the child is not a static node, then the father is not a static nodefor(var i = 0; i < l; i++) { var child = node.children[i]; // Recursively set the child node, which then calls the child node markStaticThe $1(child);        

        if(! child.static) { node.static =false; }}if (node.ifConditions) {    

   

        var c = node.ifConditions.length  

     

        for(var j = 1; j < c; J++) {// ifConditions = node.ifconditions [j]. markStaticThe $1(block);  

          

            if(! block.static) { node.static =false; }}}}Copy the code

This method does three things

1 isStatic This method performs a preliminary judgment on the AST node itself

Conduct preliminary static node judgment

2 Determine the additional processing of static nodes

After determining whether the node itself is a static node, you need to do the additional processing of checking all descendant nodes

So the child nodes are recursed layer by layer, if a child node is not a static node, then the parent node cannot be a static node, but not all nodes are treated in a special way, it’s conditional

1. The node type is 1

Type 1 is the tag element

Type 2 is a literal expression

Type 3 is plain text

2, is a normal HTML tag, the tag is slot, inline-template attribute exists

1. It must be a normal label, that is, the custom label does not need to be processed again

2. Slot is extra processed

3, inline-template attributes are also handled extra

If only one is satisfied, then additional processing is done

I doubt

You can see the final step in the source code

IfCondition, and if the node in ifCondition is not static, then the node is not static either

That’s a judgment I can’t imagine

If v-if exists, node.static will be set to false at the beginning of isStatic

Why judge again at the end?

I feel a little redundant here, right? Anyway, I didn’t figure out what Utah was thinking uh, uh, uh, uh, uh, just to make sure?

After this step, all nodes are added to the static attribute, so you can see if the node is static


Mark the static root node

// mark whether the root node is staticfunction markStaticRoots(node) {    



    if (node.type === 1) return// For a node to qualify as a static root, it should have children like this // not just static text. Otherwise, lifting costs will increase // the benefits outweigh the benefits and it is best to always keep it fresh.ifNode.children. Length && // There are many children, or the first child is not plain text! (node.children.length === 1 && node.children[0].type === 3 ) ) { node.staticRoot =true;        

        return

    }
    else {
        node.staticRoot = false;
    }    



    if (node.children) {    

    

        var l = node.children.length    

    

        for(var i = 0; i < l; i++) { markStaticRoots( node.children[i] ); }}}Copy the code

This method will only continuously find the static root node, should be called the region root node, anyway a node has a horse node, this node is considered the root node

Recurse all its descendants to see who is the staticRoot node. If it is a static ast, staticRoot will be added

MarkStaticRoots is also called recursively, but not to all nodes

Because once you find a static root, you don’t recursively process its children

And then we need to understand two things

MarkStaticRoot and markStatic$1

2. What is the basis for judging the static root node

What is the difference between markStaticRoots and markStatic$1?

Finding the static root node is the ultimate agent of performance optimization

The markStatic$1 function is only for markStaticRoots. It is used to process each node first, so that it is easier and faster to static the root node

Identify all nodes first, and then when dealing with static nodes, only find the root node of that part (region manager is fine).

Of course, the above is my personal understanding, so what is my basis?

The static attribute added by markStatic$1, which I’m searching globally, is not used in DOM processing and render generation

The staticRoot added by markStaticRoots is used in render generation

And judging by the functional logic written by markStaticRoots and using the static attribute

So I think markStatic$1 is a function for markStaticRoots

2. What are the conditions for being judged as static root nodes?

1 All descendants of this node are static nodes

Node. static = true indicates that all descendants are static, otherwise the previous step would have been set to false

2 must have child nodes

3 Child nodes cannot have only one plain text node

I don’t understand this point. Why can’t this point be the static root when there is only one plain text child node?

Note: When only plain text children are present, they are static, but not static root nodes. The static root is the condition for the optimize optimization, and without the static root it won’t be optimized

The official Vue statement is that if the child node has only one plain text node, the cost of optimization is more than the benefit, so it is not optimized

So I’m confused

Why is it expensive when the child nodes are only static text?

Here are my thoughts on personal exploration

First, we make it clear that the benefit of optimization is to reduce DOM alignment and speed up updates

And what are the costs?

1. Maintain static template storage objects

2. Multi-layer function calls

Now let’s explain these two costs briefly

1 Maintain static template storage objects

Initially, all static root nodes are parsed into vNodes and stored in a cache object, in vue.proto._StaticTree

Take this static template for example

It was parsed and saved in

As the static root grows, the storage object grows larger and consumes more memory

To reduce unnecessary storage, all static root nodes that only have plain text are eliminated

2 Multi-level function calls

This issue involves the collaboration between render and static render

For example

A mix of dynamic and static templates

The generated render function looks like this

with(this) {    

    return _c('div', [

        _m(0),
        ( testStaticRender) ? _c('span') : _e()
    ])
}
Copy the code

You see _m(0), this function is going to get a static template

Thus, static template processing

More than a _m function call, plus the initial involves a lot of function processing, including the storage of the previous step

Furthermore, since the plain text nodes are not optimized

So you need to compare this part when updating?

But plain text comparison is just a direct comparison of strings for equality

The consumption is not too small, so why do I need to maintain another static template cache?

From what has been discussed above

Only plain text child nodes are best not treated as static templates

The above is just the individual’s fancy idea, if have different opinion can put forward

One day wonder

I can’t help but wonder if the benefits are greater when there is only one plain label child node.

As you can see, the template is placed on staticRenderFns with static template processing

Consequentially speaking, the cost is probably higher hahaha

When updated, the div and span are compared with the plain text within the span, which requires three comparisons

So just choose static processing hahaha


The last

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, thanks