diff --git a/src/compiler/index.js b/src/compiler/index.js index 9aad038a..645f6649 100644 --- a/src/compiler/index.js +++ b/src/compiler/index.js @@ -1,4 +1,5 @@ import { parse } from './parser/index' +import { optimize } from './optimizer/index' import { generate } from './codegen/index' import { directives } from './codegen/directives/index' @@ -14,6 +15,7 @@ export function compile (html, options) { return hit } else { const ast = parse(html, options) + optimize(ast) return (cache[html] = generate(ast)) } } diff --git a/src/compiler/optimizer/index.js b/src/compiler/optimizer/index.js new file mode 100644 index 00000000..e468b3c6 --- /dev/null +++ b/src/compiler/optimizer/index.js @@ -0,0 +1,64 @@ +import { makeMap } from '../../shared/util' + +/** + * Goal of the optimizier: 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. + */ + +export function optimize (root) { + // first pass: mark all dynamic nodes + mark(root) + debugger + // second pass: collect static roots + const staticRoots = [] + sweep(root, staticRoots) + return staticRoots +} + +function mark (node) { + node.dynamic = !isStatic(node) + if (node.children) { + for (let i = 0, l = node.children.length; i < l; i++) { + let child = node.children[i] + mark(child) + if (child.dynamic) { + node.dynamic = true + } + } + } +} + +function sweep (node, staticRoots) { + if (node.tag && !node.dynamic) { + node.staticRoot = true + return + } + if (node.children) { + for (let i = 0, l = node.children.length; i < l; i++) { + sweep(node.children[i], staticRoots) + } + } +} + +const isStaticKey = makeMap( + 'tag,attrsList,attrsMap,plain,parent,children,' + + 'staticAttrs,staticClass' +) + +function isStatic (node) { + return node.pre || node.text || ( + !node.render && + !node.slotName && + !node.component && + !node.expression && ( + node.plain || Object.keys(node).every(isStaticKey) + ) + ) +} diff --git a/src/compiler/parser/index.js b/src/compiler/parser/index.js index 855889a2..21e4c86b 100644 --- a/src/compiler/parser/index.js +++ b/src/compiler/parser/index.js @@ -271,7 +271,10 @@ function processSlot (el) { if (el.tag === 'slot') { el.slotName = getBindingAttr(el, 'name') } else { - el.slotTarget = getBindingAttr(el, 'slot') + const slotTarget = getBindingAttr(el, 'slot') + if (slotTarget) { + el.slotTarget = slotTarget + } } } @@ -284,17 +287,26 @@ function processComponent (el) { function processClassBinding (el) { const staticClass = getAndRemoveAttr(el, 'class') el.staticClass = parseText(staticClass) || JSON.stringify(staticClass) - el.classBinding = getBindingAttr(el, 'class', false /* getStatic */) + const classBinding = getBindingAttr(el, 'class', false /* getStatic */) + if (classBinding) { + el.classBinding = classBinding + } } function processStyleBinding (el) { - el.styleBinding = getBindingAttr(el, 'style', false /* getStatic */) + const styleBinding = getBindingAttr(el, 'style', false /* getStatic */) + if (styleBinding) { + el.styleBinding = styleBinding + } } function processTransition (el) { - const transition = getBindingAttr(el, 'transition') - el.transition = transition === '""' ? true : transition - if (el.transition) { + let transition = getBindingAttr(el, 'transition') + if (transition === '""') { + transition = true + } + if (transition) { + el.transition = transition el.transitionOnAppear = getBindingAttr(el, 'transition-on-appear') != null } }