Browse Source

progress

dev
Evan You 8 years ago
parent
commit
85e24b3b31
  1. 9
      flow/compiler.js
  2. 30
      src/compiler/codegen/index.js
  3. 72
      src/server/optimizing-compiler/codegen.js
  4. 7
      src/server/optimizing-compiler/directives.js
  5. 86
      src/server/optimizing-compiler/optimizer.js
  6. 19
      src/server/render.js

9
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

30
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}` : ''
}`
}

72
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
src/server/optimizing-compiler/directives.js

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

86
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))
)
}

19
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)

Loading…
Cancel
Save