82d926f3dcb179a18b8558e304f62a42b2b740ed29b36185a9394f7f0aaad022dc767a15d70b70b36ef9c0bfa02b415215479d4a4f0b83a6d8f57284ef744f 4.1 KB

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