Evan You 9 лет назад
Родитель
Сommit
8efa3a2b22
3 измененных файлов с 102 добавлено и 24 удалено
  1. 18 7
      src/compiler/codegen/index.js
  2. 55 13
      src/server/optimizing-compiler/codegen.js
  3. 29 4
      src/server/render.js

+ 18 - 7
src/compiler/codegen/index.js

@@ -119,18 +119,24 @@ function genOnce (el: ASTElement, state: CodegenState): string {
   }
 }
 
-export function genIf (el: any, state: CodegenState, altGen?: Function): string {
+export function genIf (
+  el: any,
+  state: CodegenState,
+  altGen?: Function,
+  altEmpty?: string
+): string {
   el.ifProcessed = true // avoid recursion
-  return genIfConditions(el.ifConditions.slice(), state, altGen)
+  return genIfConditions(el.ifConditions.slice(), state, altGen, altEmpty)
 }
 
 function genIfConditions (
   conditions: ASTIfConditions,
   state: CodegenState,
-  altGen?: Function
+  altGen?: Function,
+  altEmpty?: string
 ): string {
   if (!conditions.length) {
-    return '_e()'
+    return altEmpty || '_e()'
   }
 
   const condition = conditions.shift()
@@ -138,7 +144,7 @@ function genIfConditions (
     return `(${condition.exp})?${
       genTernaryExp(condition.block)
     }:${
-      genIfConditions(conditions, state)
+      genIfConditions(conditions, state, altGen, altEmpty)
     }`
   } else {
     return `${genTernaryExp(condition.block)}`
@@ -154,7 +160,12 @@ function genIfConditions (
   }
 }
 
-export function genFor (el: any, state: CodegenState, altGen?: Function): string {
+export function genFor (
+  el: any,
+  state: CodegenState,
+  altGen?: Function,
+  altHelper?: string
+): string {
   const exp = el.for
   const alias = el.alias
   const iterator1 = el.iterator1 ? `,${el.iterator1}` : ''
@@ -175,7 +186,7 @@ export function genFor (el: any, state: CodegenState, altGen?: Function): string
   }
 
   el.forProcessed = true // avoid recursion
-  return `_l((${exp}),` +
+  return `${altHelper || '_l'}((${exp}),` +
     `function(${alias}${iterator1}${iterator2}){` +
       `return ${(altGen || genElement)(el, state)}` +
     '})'

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

@@ -23,9 +23,9 @@ type SSRCompileResult = {
 };
 
 // segment types
-const HTML = 0
-const TEXT = 1
-const EXP = 2
+const RAW = 0
+const INTERPOLATION = 1
+const FLOW_CONTROL = 2
 
 type StringSegment = {
   type: number;
@@ -103,15 +103,15 @@ function genSSRNode (el, state) {
 }
 
 function genStringChildren (el, state) {
-  return `[_ss(${flattenSegments(childrenToSegments(el, state))})]`
+  return `[_ssrNode(${flattenSegments(childrenToSegments(el, state))})]`
 }
 
 function genStringElement (el, state, stringifyChildren) {
   if (stringifyChildren) {
-    return `_ss(${flattenSegments(elementToSegments(el, state))})`
+    return `_ssrNode(${flattenSegments(elementToSegments(el, state))})`
   } else {
     const children = genSSRChildren(el, state, true)
-    return `_ss(${
+    return `_ssrNode(${
       flattenSegments(elementToOpenTagSegments(el, state))
     }","${el.tag}"${
       children ? `,${children}` : ''
@@ -120,18 +120,37 @@ function genStringElement (el, state, stringifyChildren) {
 }
 
 function elementToSegments (el, state): Array<StringSegment> {
+  if (el.for && !el.forProcessed) {
+    el.forProcessed = true
+    return [{
+      type: FLOW_CONTROL,
+      value: genFor(el, state, elementToString, '_ssrList')
+    }]
+  } else if (el.if && !el.ifProcessed) {
+    el.ifProcessed = true
+    return [{
+      type: FLOW_CONTROL,
+      value: genIf(el, state, elementToString, '""')
+    }]
+  }
+
   const openSegments = elementToOpenTagSegments(el, state)
   const childrenSegments = childrenToSegments(el, state)
   const { isUnaryTag } = state.options
   const close = (isUnaryTag && isUnaryTag(el.tag))
     ? []
-    : [{ type: HTML, value: `</${el.tag}>` }]
+    : [{ type: RAW, value: `</${el.tag}>` }]
   return openSegments.concat(childrenSegments, close)
 }
 
+function elementToString (el, state) {
+  return flattenSegments(elementToSegments(el, state))
+}
+
 function elementToOpenTagSegments (el, state): Array<StringSegment> {
-  // TODO: handle attrs/props/styles/classes/directives
-  return [{ type: HTML, value: `<${el.tag}>` }]
+  // TODO: handle v-show, v-html & v-text
+  // TODO: handle attrs/props/styles/classes
+  return [{ type: RAW, value: `<${el.tag}>` }]
 }
 
 function childrenToSegments (el, state): Array<StringSegment> {
@@ -143,9 +162,9 @@ function childrenToSegments (el, state): Array<StringSegment> {
       if (c.type === 1) {
         segments.push.apply(segments, elementToSegments(c, state))
       } else if (c.type === 2) {
-        segments.push({ type: EXP, value: c.expression })
+        segments.push({ type: INTERPOLATION, value: c.expression })
       } else if (c.type === 3) {
-        segments.push({ type: TEXT, value: c.text })
+        segments.push({ type: RAW, value: c.text })
       }
     }
     return segments
@@ -155,6 +174,29 @@ function childrenToSegments (el, state): Array<StringSegment> {
 }
 
 function flattenSegments (segments: Array<StringSegment>): string {
-  console.log(segments)
-  return 'TODO'
+  const mergedSegments = []
+  let textBuffer = ''
+
+  const pushBuffer = () => {
+    if (textBuffer) {
+      mergedSegments.push(JSON.stringify(textBuffer))
+      textBuffer = ''
+    }
+  }
+
+  for (let i = 0; i < segments.length; i++) {
+    const s = segments[i]
+    if (s.type === RAW) {
+      textBuffer += s.value
+    } else if (s.type === INTERPOLATION) {
+      pushBuffer()
+      mergedSegments.push(`_ssrEscape(${s.value})`)
+    } else if (s.type === FLOW_CONTROL) {
+      pushBuffer()
+      mergedSegments.push(`(${s.value})`)
+    }
+  }
+  pushBuffer()
+
+  return mergedSegments.join('+')
 }

+ 29 - 4
src/server/render.js

@@ -7,7 +7,7 @@ import { RenderContext } from './render-context'
 import { compileToFunctions } from 'web/compiler/index'
 import { createComponentInstanceForVnode } from 'core/vdom/create-component'
 
-import { isDef, isUndef, isTrue } from 'shared/util'
+import { isDef, isUndef, isTrue, isObject } from 'shared/util'
 
 let warned = Object.create(null)
 const warnOnce = msg => {
@@ -152,11 +152,32 @@ function StringNode (open, close, children) {
   this.children = children
 }
 
-function createStringNode (id, children) {
-  const { open, close } = this.$options.stringRenderFns[id]
+function createStringNode (open, close, children) {
   return new StringNode(open, close, children)
 }
 
+function createSSRList (val, render) {
+  let ret = ''
+  let i, l, keys, key
+  if (Array.isArray(val) || typeof val === 'string') {
+    for (i = 0, l = val.length; i < l; i++) {
+      ret += render(val[i], i)
+    }
+  } else if (typeof val === 'number') {
+    for (i = 0; i < val; i++) {
+      ret += render(i + 1, i)
+    }
+  } else if (isObject(val)) {
+    keys = Object.keys(val)
+    ret = new Array(keys.length)
+    for (i = 0, l = keys.length; i < l; i++) {
+      key = keys[i]
+      ret += render(val[key], key, i)
+    }
+  }
+  return ret
+}
+
 function renderComponentInner (node, isRoot, context) {
   const prevActive = context.activeInstance
   // expose userContext on vnode
@@ -166,7 +187,11 @@ function renderComponentInner (node, isRoot, context) {
     context.activeInstance
   )
   normalizeRender(child)
-  child._ss = createStringNode
+
+  child._ssrNode = createStringNode
+  child._ssrEscape = escape
+  child._ssrList = createSSRList
+
   const childNode = child._render()
   childNode.parent = node
   context.renderStates.push({