123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141 |
- /* @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: {},
- rawAttrsMap: {},
- 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')
- )
- }
|