diff --git a/src/compiler/create-compiler.js b/src/compiler/create-compiler.js new file mode 100644 index 00000000..b6690d8a --- /dev/null +++ b/src/compiler/create-compiler.js @@ -0,0 +1,55 @@ +/* @flow */ + +import { extend } from 'shared/util' +import { detectErrors } from './error-detector' +import { createCompileToFunctionFn } from './to-function' + +export function createCompilerCreator (baseCompile: Function): Function { + return function createCompiler (baseOptions: CompilerOptions) { + function compile ( + template: string, + options?: CompilerOptions + ): CompiledResult { + const finalOptions = Object.create(baseOptions) + const errors = [] + const tips = [] + finalOptions.warn = (msg, tip) => { + (tip ? tips : errors).push(msg) + } + + if (options) { + // merge custom modules + if (options.modules) { + finalOptions.modules = + (baseOptions.modules || []).concat(options.modules) + } + // merge custom directives + if (options.directives) { + finalOptions.directives = extend( + Object.create(baseOptions.directives), + options.directives + ) + } + // copy other options + for (const key in options) { + if (key !== 'modules' && key !== 'directives') { + finalOptions[key] = options[key] + } + } + } + + const compiled = baseCompile(template, finalOptions) + if (process.env.NODE_ENV !== 'production') { + errors.push.apply(errors, detectErrors(compiled.ast)) + } + compiled.errors = errors + compiled.tips = tips + return compiled + } + + return { + compile, + compileToFunctions: createCompileToFunctionFn(compile) + } + } +} diff --git a/src/compiler/index.js b/src/compiler/index.js index 1871a64f..075917bd 100644 --- a/src/compiler/index.js +++ b/src/compiler/index.js @@ -1,13 +1,14 @@ /* @flow */ -import { extend } from 'shared/util' import { parse } from './parser/index' import { optimize } from './optimizer' import { generate } from './codegen/index' -import { detectErrors } from './error-detector' -import { createCompileToFunctionFn } from './to-function' +import { createCompilerCreator } from './create-compiler' -function baseCompile ( +// `createCompilerCreator` allows creating compilers that use alternative +// parser/optimizer/codegen, e.g the SSR optimizing compiler. +// Here we just export a default compiler using the default parts. +export const createCompiler = createCompilerCreator(function baseCompile ( template: string, options: CompilerOptions ): CompiledResult { @@ -19,51 +20,4 @@ function baseCompile ( render: code.render, staticRenderFns: code.staticRenderFns } -} - -export function createCompiler (baseOptions: CompilerOptions) { - function compile ( - template: string, - options?: CompilerOptions - ): CompiledResult { - const finalOptions = Object.create(baseOptions) - const errors = [] - const tips = [] - finalOptions.warn = (msg, tip) => { - (tip ? tips : errors).push(msg) - } - - if (options) { - // merge custom modules - if (options.modules) { - finalOptions.modules = (baseOptions.modules || []).concat(options.modules) - } - // merge custom directives - if (options.directives) { - finalOptions.directives = extend( - Object.create(baseOptions.directives), - options.directives - ) - } - // copy other options - for (const key in options) { - if (key !== 'modules' && key !== 'directives') { - finalOptions[key] = options[key] - } - } - } - - const compiled = baseCompile(template, finalOptions) - if (process.env.NODE_ENV !== 'production') { - errors.push.apply(errors, detectErrors(compiled.ast)) - } - compiled.errors = errors - compiled.tips = tips - return compiled - } - - return { - compile, - compileToFunctions: createCompileToFunctionFn(compile) - } -} +}) diff --git a/src/compiler/to-function.js b/src/compiler/to-function.js index 4509d33f..cf13e581 100644 --- a/src/compiler/to-function.js +++ b/src/compiler/to-function.js @@ -3,6 +3,12 @@ import { noop } from 'shared/util' import { warn, tip } from 'core/util/debug' +type CompiledFunctionResult = { + render: Function; + staticRenderFns: Array; + stringRenderFns?: Array; +}; + function createFunction (code, errors) { try { return new Function(code) @@ -71,10 +77,14 @@ export function createCompileToFunctionFn (compile: Function): Function { const res = {} const fnGenErrors = [] res.render = createFunction(compiled.render, fnGenErrors) - const l = compiled.staticRenderFns.length - res.staticRenderFns = new Array(l) - for (let i = 0; i < l; i++) { - res.staticRenderFns[i] = createFunction(compiled.staticRenderFns[i], fnGenErrors) + res.staticRenderFns = compiled.staticRenderFns.map(code => { + return createFunction(code, fnGenErrors) + }) + // ssr-specific + if (res.stringRenderFns) { + res.stringRenderFns = compiled.stringRenderFns.map(code => { + return createFunction(code, fnGenErrors) + }) } // check function generation errors. diff --git a/src/platforms/web/entry-compiler.js b/src/platforms/web/entry-compiler.js index c21c18b8..ee63476e 100644 --- a/src/platforms/web/entry-compiler.js +++ b/src/platforms/web/entry-compiler.js @@ -2,3 +2,4 @@ export { parseComponent } from 'sfc/parser' export { compile, compileToFunctions } from './compiler/index' +export { ssrCompile, ssrCompileToFunctions } from './server/compiler' diff --git a/src/platforms/web/server/compiler.js b/src/platforms/web/server/compiler.js new file mode 100644 index 00000000..20e57367 --- /dev/null +++ b/src/platforms/web/server/compiler.js @@ -0,0 +1,11 @@ +/* @flow */ + +import { baseOptions } from '../compiler/options' +import { createCompiler } from 'server/optimizing-compiler/index' + +const { compile, compileToFunctions } = createCompiler(baseOptions) + +export { + compile as ssrCompile, + compileToFunctions as ssrCompileToFunctions +} diff --git a/src/server/optimizing-compiler/codegen.js b/src/server/optimizing-compiler/codegen.js index fa9feb3f..72144f73 100644 --- a/src/server/optimizing-compiler/codegen.js +++ b/src/server/optimizing-compiler/codegen.js @@ -1,7 +1,11 @@ /* @flow */ +// The SSR codegen is essentially extending the default codegen to handle +// SSR-optimizable nodes and turn them into string render fns. In cases where +// a node is not optimizable it simply falls back to the default codegen. + // import * as directives from './directives' -import { CodegenState } from 'compiler/codegen/index' +import { CodegenState, genElement } from 'compiler/codegen/index' type SSRCompileResult = { render: string; @@ -23,7 +27,7 @@ export function generate ( options: CompilerOptions ): SSRCompileResult { const state = new SSRCodegenState(options) - const code = ast ? genElement(ast, state) : '_c("div")' + const code = ast ? genSSRElement(ast, state, true) : '_c("div")' return { render: `with(this){return ${code}}`, staticRenderFns: state.staticRenderFns, @@ -31,6 +35,18 @@ export function generate ( } } -function genElement (el: ASTElement, state: SSRCodegenState): string { +function genSSRElement ( + el: ASTElement, + state: SSRCodegenState, + isComponentRoot?: boolean +): string { + if (el.ssrOptimizableRoot && !isComponentRoot) { + return genStringRenderFn(el, state) + } else { + return genElement(el, state) + } +} +function genStringRenderFn (el, state) { + return '' } diff --git a/src/server/optimizing-compiler/index.js b/src/server/optimizing-compiler/index.js index 3340387d..dabe398f 100644 --- a/src/server/optimizing-compiler/index.js +++ b/src/server/optimizing-compiler/index.js @@ -1,13 +1,11 @@ /* @flow */ -import { extend } from 'shared/util' import { parse } from 'compiler/parser/index' -import { detectErrors } from 'compiler/error-detector' - import { generate } from './codegen' import { optimize } from './optimizer' +import { createCompilerCreator } from 'compiler/create-compiler' -function baseCompile ( +export const createCompiler = createCompilerCreator(function baseCompile ( template: string, options: CompilerOptions ): CompiledResult { @@ -17,48 +15,7 @@ function baseCompile ( return { ast, render: code.render, - staticRenderFns: code.staticRenderFns - } -} - -export function createCompiler (baseOptions: CompilerOptions) { - return function compile ( - template: string, - options?: CompilerOptions - ): CompiledResult { - const finalOptions = Object.create(baseOptions) - const errors = [] - const tips = [] - finalOptions.warn = (msg, tip) => { - (tip ? tips : errors).push(msg) - } - - if (options) { - // merge custom modules - if (options.modules) { - finalOptions.modules = (baseOptions.modules || []).concat(options.modules) - } - // merge custom directives - if (options.directives) { - finalOptions.directives = extend( - Object.create(baseOptions.directives), - options.directives - ) - } - // copy other options - for (const key in options) { - if (key !== 'modules' && key !== 'directives') { - finalOptions[key] = options[key] - } - } - } - - const compiled = baseCompile(template, finalOptions) - if (process.env.NODE_ENV !== 'production') { - errors.push.apply(errors, detectErrors(compiled.ast)) - } - compiled.errors = errors - compiled.tips = tips - return compiled + staticRenderFns: code.staticRenderFns, + stringRenderFns: code.stringRenderFns } -} +})