123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128 |
- /* @flow */
- import { makeMap, isBuiltInTag, cached, no } from 'shared/util'
- let isStaticKey
- let isPlatformReservedTag
- const genStaticKeysCached = cached(genStaticKeys)
- /**
- * Goal of the optimizer: 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: ?ASTElement, options: CompilerOptions) {
- if (!root) return
- isStaticKey = genStaticKeysCached(options.staticKeys || '')
- isPlatformReservedTag = options.isReservedTag || no
- // first pass: mark all non-static nodes.
- markStatic(root)
- // second pass: mark static roots.
- markStaticRoots(root, false)
- }
- function genStaticKeys (keys: string): Function {
- return makeMap(
- 'type,tag,attrsList,attrsMap,plain,parent,children,attrs,start,end,rawAttrsMap' +
- (keys ? ',' + keys : '')
- )
- }
- function markStatic (node: ASTNode) {
- node.static = isStatic(node)
- if (node.type === 1) {
- // do not make component slot content static. this avoids
- // 1. components not able to mutate slot nodes
- // 2. static slot content fails for hot-reloading
- if (
- !isPlatformReservedTag(node.tag) &&
- node.tag !== 'slot' &&
- node.attrsMap['inline-template'] == null
- ) {
- return
- }
- for (let i = 0, l = node.children.length; i < l; i++) {
- const child = node.children[i]
- markStatic(child)
- if (!child.static) {
- node.static = false
- }
- }
- if (node.ifConditions) {
- for (let i = 1, l = node.ifConditions.length; i < l; i++) {
- const block = node.ifConditions[i].block
- markStatic(block)
- if (!block.static) {
- node.static = false
- }
- }
- }
- }
- }
- function markStaticRoots (node: ASTNode, isInFor: boolean) {
- if (node.type === 1) {
- if (node.static || node.once) {
- node.staticInFor = isInFor
- }
- // For a node to qualify as a static root, it should have children that
- // are not just static text. Otherwise the cost of hoisting out will
- // outweigh the benefits and it's better off to just always render it fresh.
- if (node.static && node.children.length && !(
- node.children.length === 1 &&
- node.children[0].type === 3
- )) {
- node.staticRoot = true
- return
- } else {
- node.staticRoot = false
- }
- if (node.children) {
- for (let i = 0, l = node.children.length; i < l; i++) {
- markStaticRoots(node.children[i], isInFor || !!node.for)
- }
- }
- if (node.ifConditions) {
- for (let i = 1, l = node.ifConditions.length; i < l; i++) {
- markStaticRoots(node.ifConditions[i].block, isInFor)
- }
- }
- }
- }
- function isStatic (node: ASTNode): boolean {
- if (node.type === 2) { // expression
- return false
- }
- if (node.type === 3) { // text
- return true
- }
- return !!(node.pre || (
- !node.hasBindings && // no dynamic bindings
- !node.if && !node.for && // not v-if or v-for or v-else
- !isBuiltInTag(node.tag) && // not a built-in
- isPlatformReservedTag(node.tag) && // not a component
- !isDirectChildOfTemplateFor(node) &&
- Object.keys(node).every(isStaticKey)
- ))
- }
- function isDirectChildOfTemplateFor (node: ASTElement): boolean {
- while (node.parent) {
- node = node.parent
- if (node.tag !== 'template') {
- return false
- }
- if (node.for) {
- return true
- }
- }
- return false
- }
|