Evan You пре 9 година
родитељ
комит
85e24b3b31

+ 3 - 6
flow/compiler.js

@@ -129,8 +129,7 @@ declare type ASTElement = {
   wrapData?: (code: string) => string;
 
   // 2.4 ssr optimization
-  ssrOptimizable?: boolean;
-  ssrOptimizableRoot?: boolean;
+  ssrOptimizability?: number;
 
   // weex specific
   appendAsTree?: boolean;
@@ -142,8 +141,7 @@ declare type ASTExpression = {
   text: string;
   static?: boolean;
   // 2.4 ssr optimization
-  ssrOptimizable?: boolean;
-  ssrOptimizableRoot?: boolean;
+  ssrOptimizability?: number;
 };
 
 declare type ASTText = {
@@ -151,8 +149,7 @@ declare type ASTText = {
   text: string;
   static?: boolean;
   // 2.4 ssr optimization
-  ssrOptimizable?: boolean;
-  ssrOptimizableRoot?: boolean;
+  ssrOptimizability?: number;
 };
 
 // SFC-parser related declarations

+ 19 - 11
src/compiler/codegen/index.js

@@ -119,14 +119,15 @@ function genOnce (el: ASTElement, state: CodegenState): string {
   }
 }
 
-function genIf (el: any, state: CodegenState): string {
+export function genIf (el: any, state: CodegenState, altGen?: Function): string {
   el.ifProcessed = true // avoid recursion
-  return genIfConditions(el.ifConditions.slice(), state)
+  return genIfConditions(el.ifConditions.slice(), state, altGen)
 }
 
 function genIfConditions (
   conditions: ASTIfConditions,
-  state: CodegenState
+  state: CodegenState,
+  altGen?: Function
 ): string {
   if (!conditions.length) {
     return '_e()'
@@ -145,11 +146,15 @@ function genIfConditions (
 
   // v-if with v-once should generate code like (a)?_m(0):_m(1)
   function genTernaryExp (el) {
-    return el.once ? genOnce(el, state) : genElement(el, state)
+    return altGen
+      ? altGen(el, state)
+      : el.once
+        ? genOnce(el, state)
+        : genElement(el, state)
   }
 }
 
-function genFor (el: any, state: CodegenState): string {
+export function genFor (el: any, state: CodegenState, altGen?: Function): string {
   const exp = el.for
   const alias = el.alias
   const iterator1 = el.iterator1 ? `,${el.iterator1}` : ''
@@ -172,11 +177,11 @@ function genFor (el: any, state: CodegenState): string {
   el.forProcessed = true // avoid recursion
   return `_l((${exp}),` +
     `function(${alias}${iterator1}${iterator2}){` +
-      `return ${genElement(el, state)}` +
+      `return ${(altGen || genElement)(el, state)}` +
     '})'
 }
 
-function genData (el: ASTElement, state: CodegenState): string {
+export function genData (el: ASTElement, state: CodegenState): string {
   let data = '{'
 
   // directives first.
@@ -345,10 +350,12 @@ function genForScopedSlot (
     '})'
 }
 
-function genChildren (
+export function genChildren (
   el: ASTElement,
   state: CodegenState,
-  checkSkip?: boolean
+  checkSkip?: boolean,
+  altGenElement?: Function,
+  altGenNode?: Function
 ): string | void {
   const children = el.children
   if (children.length) {
@@ -359,12 +366,13 @@ function genChildren (
       el.tag !== 'template' &&
       el.tag !== 'slot'
     ) {
-      return genElement(el, state)
+      return (altGenElement || genElement)(el, state)
     }
     const normalizationType = checkSkip
       ? getNormalizationType(children, state.maybeComponent)
       : 0
-    return `[${children.map(c => genNode(c, state)).join(',')}]${
+    const gen = altGenNode || genNode
+    return `[${children.map(c => gen(c, state)).join(',')}]${
       normalizationType ? `,${normalizationType}` : ''
     }`
   }

+ 59 - 13
src/server/optimizing-compiler/codegen.js

@@ -5,7 +5,16 @@
 // a node is not optimizable it simply falls back to the default codegen.
 
 // import * as directives from './directives'
-import { CodegenState, genElement } from 'compiler/codegen/index'
+import { FULL, PARTIAL, CHILDREN } from './optimizer'
+
+import {
+  genIf,
+  genFor,
+  genData,
+  genElement,
+  genChildren,
+  CodegenState
+} from 'compiler/codegen/index'
 
 type SSRCompileResult = {
   render: string;
@@ -27,7 +36,7 @@ export function generate (
   options: CompilerOptions
 ): SSRCompileResult {
   const state = new SSRCodegenState(options)
-  const code = ast ? genSSRElement(ast, state, true) : '_c("div")'
+  const code = ast ? genSSRElement(ast, state) : '_c("div")'
   return {
     render: `with(this){return ${code}}`,
     staticRenderFns: state.staticRenderFns,
@@ -35,18 +44,55 @@ export function generate (
   }
 }
 
-function genSSRElement (
-  el: ASTElement,
-  state: SSRCodegenState,
-  isComponentRoot?: boolean
-): string {
-  if (el.ssrOptimizableRoot && !isComponentRoot) {
-    return genStringRenderFn(el, state)
-  } else {
-    return genElement(el, state)
+function genSSRElement (el: ASTElement, state: SSRCodegenState): string {
+  if (el.for && !el.forProcessed) {
+    return genFor(el, state, genSSRElement)
+  } else if (el.if && !el.ifProcessed) {
+    return genIf(el, state, genSSRElement)
+  }
+
+  switch (el.ssrOptimizability) {
+    case FULL:
+      // stringify whole tree
+      return genStringNode(el, state, true)
+    case PARTIAL:
+      // stringify self and check children
+      return genStringNode(el, state, false)
+    case CHILDREN:
+      // generate self as VNode and check children
+      return genVNode(el, state)
+    default:
+      // bail whole tree
+      return genElement(el, state)
+  }
+}
+
+function genSSRNode (el, state) {
+  return el.type === 1
+    ? genSSRElement(el, state)
+    : genStringNode(el, state)
+}
+
+function genSSRChildren (el, state, checkSkip) {
+  return genChildren(el, state, checkSkip, genSSRElement, genSSRNode)
+}
+
+function genVNode (el, state) {
+  let code
+  const data = el.plain ? undefined : genData(el, state)
+  const children = el.inlineTemplate ? null : genSSRChildren(el, state, true)
+  code = `_c('${el.tag}'${
+    data ? `,${data}` : '' // data
+  }${
+    children ? `,${children}` : '' // children
+  })`
+  // module transforms
+  for (let i = 0; i < state.transforms.length; i++) {
+    code = state.transforms[i](el, code)
   }
+  return code
 }
 
-function genStringRenderFn (el, state) {
-  return ''
+function genStringNode (el, state, includeChildren) {
+  return '!!!'
 }

+ 7 - 0
src/server/optimizing-compiler/directives.js

@@ -0,0 +1,7 @@
+/* @flow */
+
+export default {
+  show () {
+
+  }
+}

+ 41 - 45
src/server/optimizing-compiler/optimizer.js

@@ -1,6 +1,12 @@
 /* @flow */
 
-import { no, isBuiltInTag } from 'shared/util'
+import { no, makeMap, isBuiltInTag } from 'shared/util'
+
+// optimizability constants
+export const FALSE = 0 // whole sub tree un-optimizable
+export const FULL = 1 // whole sub tree optimizable
+export const PARTIAL = 2 // self optimizable but has un-optimizable children
+export const CHILDREN = 3 // self un-optimizable but may have optimizable children
 
 let isPlatformReservedTag
 
@@ -16,70 +22,60 @@ let isPlatformReservedTag
 export function optimize (root: ?ASTElement, options: CompilerOptions) {
   if (!root) return
   isPlatformReservedTag = options.isReservedTag || no
-  // first pass: mark all non-optimizable nodes.
-  markNonOptimizable(root)
-  // second pass: mark optimizable trees.
-  markOptimizableTrees(root, false)
+  walk(root, true)
 }
 
-function markNonOptimizable (node: ASTNode) {
-  node.ssrOptimizable = isOptimizable(node)
+function walk (node: ASTNode, isRoot?: boolean) {
+  if (isUnOptimizableTree(node)) {
+    node.ssrOptimizability = FALSE
+    return
+  }
+  // root node or nodes with custom directives should always be a VNode
+  if (isRoot || hasCustomDirective(node)) {
+    node.ssrOptimizability = CHILDREN
+  }
   if (node.type === 1) {
-    // do not make component slot content optimizable so that render fns can
-    // still manipulate the nodes.
-    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]
-      markNonOptimizable(child)
-      if (!child.ssrOptimizable) {
-        node.ssrOptimizable = false
+      walk(child)
+      if (child.ssrOptimizability !== FULL && node.ssrOptimizability == null) {
+        node.ssrOptimizability = PARTIAL
       }
     }
     if (node.ifConditions) {
       for (let i = 1, l = node.ifConditions.length; i < l; i++) {
         const block = node.ifConditions[i].block
-        markNonOptimizable(block)
-        if (!block.ssrOptimizable) {
-          node.ssrOptimizable = false
+        walk(block)
+        if (block.ssrOptimizability !== FULL && node.ssrOptimizability == null) {
+          node.ssrOptimizability = PARTIAL
         }
       }
     }
+    if (node.ssrOptimizability == null) {
+      node.ssrOptimizability = FULL
+    }
+  } else {
+    node.ssrOptimizability = FULL
   }
 }
 
-function isOptimizable (node: ASTNode): boolean {
+function isUnOptimizableTree (node: ASTNode): boolean {
   if (node.type === 2 || node.type === 3) { // text or expression
-    return true
+    return false
   }
   return (
-    !isBuiltInTag(node.tag) && // not a built-in (slot, component)
-    !!isPlatformReservedTag(node.tag) // not a component
+    isBuiltInTag(node.tag) || // built-in (slot, component)
+    !isPlatformReservedTag(node.tag) // custom component
   )
 }
 
-function markOptimizableTrees (node: ASTNode) {
-  if (node.type === 1) {
-    if (node.ssrOptimizable) {
-      node.ssrOptimizableRoot = true
-      return
-    } else {
-      node.ssrOptimizableRoot = false
-    }
-    if (node.children) {
-      for (let i = 0, l = node.children.length; i < l; i++) {
-        markOptimizableTrees(node.children[i])
-      }
-    }
-    if (node.ifConditions) {
-      for (let i = 1, l = node.ifConditions.length; i < l; i++) {
-        markOptimizableTrees(node.ifConditions[i].block)
-      }
-    }
-  }
+// only need to check built-in dirs with runtime
+const isBuiltInDir = makeMap('model,show')
+
+function hasCustomDirective (node: ASTNode): ?boolean {
+  return (
+    node.type === 1 &&
+    node.directives &&
+    node.directives.some(d => !isBuiltInDir(d.name))
+  )
 }

+ 16 - 3
src/server/render.js

@@ -38,8 +38,8 @@ const normalizeRender = vm => {
 }
 
 function renderNode (node, isRoot, context) {
-  if (node.isTextNode) {
-    renderTextNode(node, context)
+  if (node.isString) {
+    renderStringNode(node, context)
   } else if (isDef(node.componentOptions)) {
     renderComponent(node, isRoot, context)
   } else {
@@ -145,6 +145,18 @@ function renderComponentWithCache (node, isRoot, key, context) {
   renderComponentInner(node, isRoot, context)
 }
 
+function StringNode (open, close, children) {
+  this.isString = true
+  this.open = open
+  this.close = close
+  this.children = children
+}
+
+function createStringNode (id, children) {
+  const { open, close } = this.$options.stringRenderFns[id]
+  return new StringNode(open, close, children)
+}
+
 function renderComponentInner (node, isRoot, context) {
   const prevActive = context.activeInstance
   // expose userContext on vnode
@@ -154,6 +166,7 @@ function renderComponentInner (node, isRoot, context) {
     context.activeInstance
   )
   normalizeRender(child)
+  child._ss = createStringNode
   const childNode = child._render()
   childNode.parent = node
   context.renderStates.push({
@@ -163,7 +176,7 @@ function renderComponentInner (node, isRoot, context) {
   renderNode(childNode, isRoot, context)
 }
 
-function renderTextNode (el, context) {
+function renderStringNode (el, context) {
   const { write, next } = context
   if (isUndef(el.children) || el.children.length === 0) {
     write(el.open() + (el.close || ''), next)