Blame view

node_modules/vue/src/server/optimizing-compiler/optimizer.js 4.11 KB
aaac7fed   liuqimichale   add
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
  /* @flow */
  
  /**
   * In SSR, the vdom tree is generated only once and never patched, so
   * we can optimize most element / trees into plain string render functions.
   * The SSR optimizer walks the AST tree to detect optimizable elements and trees.
   *
   * The criteria for SSR optimizability is quite a bit looser than static tree
   * detection (which is designed for client re-render). In SSR we bail only for
   * components/slots/custom directives.
   */
  
  import { no, makeMap, isBuiltInTag } from 'shared/util'
  
  // optimizability constants
  export const optimizability = {
    FALSE: 0,    // whole sub tree un-optimizable
    FULL: 1,     // whole sub tree optimizable
    SELF: 2,     // self optimizable but has some un-optimizable children
    CHILDREN: 3, // self un-optimizable but have fully optimizable children
    PARTIAL: 4   // self un-optimizable with some un-optimizable children
  }
  
  let isPlatformReservedTag
  
  export function optimize (root: ?ASTElement, options: CompilerOptions) {
    if (!root) return
    isPlatformReservedTag = options.isReservedTag || no
    walk(root, true)
  }
  
  function walk (node: ASTNode, isRoot?: boolean) {
    if (isUnOptimizableTree(node)) {
      node.ssrOptimizability = optimizability.FALSE
      return
    }
    // root node or nodes with custom directives should always be a VNode
    const selfUnoptimizable = isRoot || hasCustomDirective(node)
    const check = child => {
      if (child.ssrOptimizability !== optimizability.FULL) {
        node.ssrOptimizability = selfUnoptimizable
          ? optimizability.PARTIAL
          : optimizability.SELF
      }
    }
    if (selfUnoptimizable) {
      node.ssrOptimizability = optimizability.CHILDREN
    }
    if (node.type === 1) {
      for (let i = 0, l = node.children.length; i < l; i++) {
        const child = node.children[i]
        walk(child)
        check(child)
      }
      if (node.ifConditions) {
        for (let i = 1, l = node.ifConditions.length; i < l; i++) {
          const block = node.ifConditions[i].block
          walk(block, isRoot)
          check(block)
        }
      }
      if (node.ssrOptimizability == null ||
        (!isRoot && (node.attrsMap['v-html'] || node.attrsMap['v-text']))
      ) {
        node.ssrOptimizability = optimizability.FULL
      } else {
        node.children = optimizeSiblings(node)
      }
    } else {
      node.ssrOptimizability = optimizability.FULL
    }
  }
  
  function optimizeSiblings (el) {
    const children = el.children
    const optimizedChildren = []
  
    let currentOptimizableGroup = []
    const pushGroup = () => {
      if (currentOptimizableGroup.length) {
        optimizedChildren.push({
          type: 1,
          parent: el,
          tag: 'template',
          attrsList: [],
          attrsMap: {},
          children: currentOptimizableGroup,
          ssrOptimizability: optimizability.FULL
        })
      }
      currentOptimizableGroup = []
    }
  
    for (let i = 0; i < children.length; i++) {
      const c = children[i]
      if (c.ssrOptimizability === optimizability.FULL) {
        currentOptimizableGroup.push(c)
      } else {
        // wrap fully-optimizable adjacent siblings inside a template tag
        // so that they can be optimized into a single ssrNode by codegen
        pushGroup()
        optimizedChildren.push(c)
      }
    }
    pushGroup()
    return optimizedChildren
  }
  
  function isUnOptimizableTree (node: ASTNode): boolean {
    if (node.type === 2 || node.type === 3) { // text or expression
      return false
    }
    return (
      isBuiltInTag(node.tag) || // built-in (slot, component)
      !isPlatformReservedTag(node.tag) || // custom component
      !!node.component || // "is" component
      isSelectWithModel(node) // <select v-model> requires runtime inspection
    )
  }
  
  const isBuiltInDir = makeMap('text,html,show,on,bind,model,pre,cloak,once')
  
  function hasCustomDirective (node: ASTNode): ?boolean {
    return (
      node.type === 1 &&
      node.directives &&
      node.directives.some(d => !isBuiltInDir(d.name))
    )
  }
  
  // <select v-model> cannot be optimized because it requires a runtime check
  // to determine proper selected option
  function isSelectWithModel (node: ASTNode): boolean {
    return (
      node.type === 1 &&
      node.tag === 'select' &&
      node.directives != null &&
      node.directives.some(d => d.name === 'model')
    )
  }