The optimize template -> AST -> parse -> optimize -> generate -> render -> vnode This is to detect static subtrees that do not require DOM changes and process them accordingly.
Definition of static nodes
So how does Vue define static nodes? Let’s look at the Vue source code:
// vue/compiler/optimizer.js
// node.type === 1 is the tag element
function isStatic (node: ASTNode) :boolean {
if (node.type === 2) { // expression
return false
}
if (node.type === 3) { // text
return true
}
return!!!!! (node.pre || ( ! node.hasBindings &&// no dynamic bindings! node.if && ! node.for &&// not v-if or v-for or v-else! isBuiltInTag(node.tag) &&// not a built-in
isPlatformReservedTag(node.tag) && // not a component! isDirectChildOfTemplateFor(node) &&Object.keys(node).every(isStaticKey)
))
}
Copy the code
From the comments, we can know that the static node is:
- The node type is
text
; - Preformatted text node;
- Other nodes meet the following characteristics:
- There is no dynamic binding
- Does not contain
v-if
,v-for
.v-else
instruction - Not a node in construction (slot, component)
- Not a component type
- The parent of a static node, not with
v-for
The directivetemplate
node - Properties of nodes are static properties
For the second type of static node characteristics, a detailed explanation of points 3, 4, 5, and 6 May need to be looked at in conjunction with the source code. So let’s analyze it.
In order, let’s start with Point 3:
IsBuiltInTag is defined as follows:
// /vue/shared/util.js
// Check whether the field exists in the map
export function makeMap (str: string, expectsLowerCase? : boolean) : (key: string) = >true | void {
const map = Object.create(null)
const list: Array<string> = str.split(', ')
for (let i = 0; i < list.length; i++) {
map[list[i]] = true
}
return expectsLowerCase
? val= > map[val.toLowerCase()]
: val= > map[val]
}
/** * Check if a tag is a built-in tag. */
export const isBuiltInTag = makeMap('slot,component'.true)
Copy the code
As you can see in the buildIn code, the node refers to slot and component.
Here’s point 4:
IsPlatformReservedTag is defined as follows
// vue/compiler/optimizer.js
let isPlatformReservedTag
// ...
isPlatformReservedTag = options.isReservedTag || no
Copy the code
From the code, you can know that this field is to mark whether the node label is reserved by the platform, underline, platform. That is, the method executed by the field will match differently for different platforms.
This is the definition of the isReservedTag method for the Web platform:
// vue/platforms/web/util/element.js
export const isReservedTag = (tag: string): ?boolean= > {
return isHTMLTag(tag) || isSVG(tag)
}
Copy the code
As you can see from the code, isPlatformReservedTag is used to check whether the node type is that of the platform.
Here’s point 5:
IsDirectChildOfTemplateFor are defined as follows:
// vue/compiler/optimizer.js
function isDirectChildOfTemplateFor (node: ASTElement) :boolean {
while (node.parent) {
node = node.parent
if(node.tag ! = ='template') {
return false
}
if (node.for) {
return true}}return false
}
Copy the code
Clearly see from the code, isDirectChildOfTemplateFor is test is below this kind of situation:
<! <template v-for="item in [1,2,3]"> {{item}} </template>Copy the code
! IsDirectChildOfTemplateFor (node) illustrates the static node’s parent, can not be with v – for instruction of the template node.
Here’s point 6:
First, take a look at isStaticKey, which is defined on the outside.
let isStaticKey
Copy the code
The place to use isStaticKey is
// vue/compiler/optimizer.js
export function optimize (root: ? ASTElement, options: CompilerOptions) {
if(! root)return
isStaticKey = genStaticKeysCached(options.staticKeys || ' ')
/ /... Ignore other code
}
Copy the code
Let’s take a look at Genckey Scached, which is also defined to the very outside.
// vue/compiler/optimizer.js
const genStaticKeysCached = cached(genStaticKeys)
Copy the code
Take a look at genStaticKeys and cached.
// vue/compiler/optimizer.js
// genStaticKeys Static attribute map table
// See # 3 for the makeMap method
function genStaticKeys (keys: string) :Function {
return makeMap(
'type,tag,attrsList,attrsMap,plain,parent,children,attrs' +
(keys ? ', ' + keys : ' '))}// vue/shared/util.js
// cached cached method execution results
export function cached<F: Function> (fn: F) :F {
const cache = Object.create(null)
return (function cachedFn (str: string) {
const hit = cache[str]
return hit || (cache[str] = fn(str))
}: any)
}
Copy the code
Object.keys(node).every(isStaticKey) is used to check whether a node property is static.
To optimize the
// vue/compiler/optimizer.js
/** * Goal of the optimizer: walk the generated template AST tree * and detect sub-trees that are purely static, i.e. parts of * the DOM that never needs to change. * * Once we detect these sub-trees, we can: * * 1. Hoist them into constants, so that we no longer need to * create fresh nodes for them on each re-render; * 2. Completely skip them in the patching process. */
Copy the code
Why isn’t React optimized for static nodes
Before considering this question, consider another question: why can Vue optimize static nodes?
As we all know, the most significant difference between Vue and React is that Vue is modelled while React is written using JSX.
Vue can also be developed using JSX, as shown in the official documentation cn.vuejs.org/v2/guide/re… Shown below:
Vue.component('anchored-heading', { render: Function (createElement) {return createElement('h' + this.level, // tag this.$slot.default // subnode array)}, props: { level: { type: Number, required: true } } })Copy the code
As shown above, the createElement method is called in the Render method instead of template.
The process for converting a template to a VNode is as follows:
template -> AST -> parse -> optimize -> generate -> render -> vnode
As for JSX, the process of obtaining VNode is as follows:
render -> vnode
The aforementioned static node optimizations would not be enjoyed if they were written in JSX.
Therefore, the existence of template has natural advantages for the identification and optimization of static nodes. We can’t say for sure that JSX can’t do this kind of optimization, but it’s an alternative optimization direction that needs to be weighed against the benefits and costs.
React optimization takes a different direction from Vue in that it focuses on how to respond faster to the browser, creatively using time slicing, prioritization, etc., and solving the problem of pages getting stuck or stuck when rendering large numbers of elements or deep nested elements.
Should Vue also use time slicing and prioritizing to handle these optimizations for rendering? This needs to consider the trade-off between benefits and costs.