bd24cdee0f90dc5a2923a089570618fbc5a5b74061edd7cd98c36d995074ec26fbf0714ab9051a9214b87d3763001eac3e747f1fb9da9283c1ba9cfb437d1d 3.6 KB

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