UNPKG

3.64 kBJavaScriptView Raw
1/* @flow */
2
3import { makeMap, isBuiltInTag, cached, no } from 'shared/util'
4
5let isStaticKey
6let isPlatformReservedTag
7
8const genStaticKeysCached = cached(genStaticKeys)
9
10/**
11 * Goal of the optimizer: walk the generated template AST tree
12 * and detect sub-trees that are purely static, i.e. parts of
13 * the DOM that never needs to change.
14 *
15 * Once we detect these sub-trees, we can:
16 *
17 * 1. Hoist them into constants, so that we no longer need to
18 * create fresh nodes for them on each re-render;
19 * 2. Completely skip them in the patching process.
20 */
21export function optimize (root: ?ASTElement, options: CompilerOptions) {
22 if (!root) return
23 isStaticKey = genStaticKeysCached(options.staticKeys || '')
24 isPlatformReservedTag = options.isReservedTag || no
25 // first pass: mark all non-static nodes.
26 markStatic(root)
27 // second pass: mark static roots.
28 markStaticRoots(root, false)
29}
30
31function genStaticKeys (keys: string): Function {
32 return makeMap(
33 'type,tag,attrsList,attrsMap,plain,parent,children,attrs,start,end,rawAttrsMap' +
34 (keys ? ',' + keys : '')
35 )
36}
37
38function markStatic (node: ASTNode) {
39 node.static = isStatic(node)
40 if (node.type === 1) {
41 // do not make component slot content static. this avoids
42 // 1. components not able to mutate slot nodes
43 // 2. static slot content fails for hot-reloading
44 if (
45 !isPlatformReservedTag(node.tag) &&
46 node.tag !== 'slot' &&
47 node.attrsMap['inline-template'] == null
48 ) {
49 return
50 }
51 for (let i = 0, l = node.children.length; i < l; i++) {
52 const child = node.children[i]
53 markStatic(child)
54 if (!child.static) {
55 node.static = false
56 }
57 }
58 if (node.ifConditions) {
59 for (let i = 1, l = node.ifConditions.length; i < l; i++) {
60 const block = node.ifConditions[i].block
61 markStatic(block)
62 if (!block.static) {
63 node.static = false
64 }
65 }
66 }
67 }
68}
69
70function markStaticRoots (node: ASTNode, isInFor: boolean) {
71 if (node.type === 1) {
72 if (node.static || node.once) {
73 node.staticInFor = isInFor
74 }
75 // For a node to qualify as a static root, it should have children that
76 // are not just static text. Otherwise the cost of hoisting out will
77 // outweigh the benefits and it's better off to just always render it fresh.
78 if (node.static && node.children.length && !(
79 node.children.length === 1 &&
80 node.children[0].type === 3
81 )) {
82 node.staticRoot = true
83 return
84 } else {
85 node.staticRoot = false
86 }
87 if (node.children) {
88 for (let i = 0, l = node.children.length; i < l; i++) {
89 markStaticRoots(node.children[i], isInFor || !!node.for)
90 }
91 }
92 if (node.ifConditions) {
93 for (let i = 1, l = node.ifConditions.length; i < l; i++) {
94 markStaticRoots(node.ifConditions[i].block, isInFor)
95 }
96 }
97 }
98}
99
100function isStatic (node: ASTNode): boolean {
101 if (node.type === 2) { // expression
102 return false
103 }
104 if (node.type === 3) { // text
105 return true
106 }
107 return !!(node.pre || (
108 !node.hasBindings && // no dynamic bindings
109 !node.if && !node.for && // not v-if or v-for or v-else
110 !isBuiltInTag(node.tag) && // not a built-in
111 isPlatformReservedTag(node.tag) && // not a component
112 !isDirectChildOfTemplateFor(node) &&
113 Object.keys(node).every(isStaticKey)
114 ))
115}
116
117function isDirectChildOfTemplateFor (node: ASTElement): boolean {
118 while (node.parent) {
119 node = node.parent
120 if (node.tag !== 'template') {
121 return false
122 }
123 if (node.for) {
124 return true
125 }
126 }
127 return false
128}