Compile the entrance
There are two ways for a Vue to render a virtual DOM, one is to write the render function by hand, and the other is to compile the template into the render function by vue compilation. The most common way in development is to write the template. Template compilation in Vue mainly has three core functions: generating AST tree, optimizing AST tree, and generating code, as follows:
export const createCompiler = createCompilerCreator(function baseCompile ( template: string, options: CompilerOptions ): CompiledResult {const AST = parse(template.trim(), options) // Optimize the AST tree if (options.optimize! > > optimize(ast, options)} const code = generate(ast, options) return {ast, render: code.render, staticRenderFns: code.staticRenderFns } })Copy the code
Here’s a look at the main process line that executes the code:
Platforms /web/entry-runtime-with-compiler.js, obtain function render via deconstruction. (How specific characters are encoded in different browsers)
import { compileToFunctions } from './compiler/index'
...
const { render, staticRenderFns} = compileToFunctions(template, {
outputSourceRange,
shouldDecodeNewlines,
shouldDecodeNewlinesForHref,
delimiters: options.delimiters,
comments: options.comments
}, this)
Copy the code
2. Platforms/web/compiler/index. Js, get function compileToFunctions through deconstruction. (Mainly passing in function parameters)
import { baseOptions } from './options'
import { createCompiler } from 'compiler/index'
const { compile, compileToFunctions } = createCompiler(baseOptions)
Copy the code
3. In the file SRC /compile/index.js, destruct the createCompiler function. (Mainly compilation core)
import { createCompilerCreator } from './create-compiler' export const createCompiler = createCompilerCreator(function baseCompile ( template: string, options: CompilerOptions ): CompiledResult { const ast = parse(template.trim(), options) if (options.optimize ! == false) { optimize(ast, options) } const code = generate(ast, options) return { ast, render: code.render, staticRenderFns: code.staticRenderFns } })Copy the code
4. Obtain createCompilerCreator from the SRC /compile/create-compiler.js file.
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 = [] let warn = (msg, range, tip) => { (tip ? tips : errors).push(msg) } if (options) { if (process.env.NODE_ENV ! == 'production' && options.outputSourceRange) { // $flow-disable-line const leadingSpaceLength = template.match(/^\s*/)[0].length warn = (msg, range, tip) => { const data: WarningMessage = { msg } if (range) { if (range.start ! = null) { data.start = range.start + leadingSpaceLength } if (range.end ! = null) { data.end = range.end + leadingSpaceLength } } (tip ? tips : errors).push(data) } } // 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 || null), options.directives ) } // copy other options for (const key in options) { if (key ! == 'modules' && key ! == 'directives') { finalOptions[key] = options[key] } } } finalOptions.warn = warn const compiled = baseCompile(template.trim(), finalOptions) if (process.env.NODE_ENV ! == 'production') { detectErrors(compiled.ast, warn) } compiled.errors = errors compiled.tips = tips return compiled } return { compile, compileToFunctions: createCompileToFunctionFn(compile) } } }Copy the code
CreateCompilerCreator return values contained in the compile and compileToFunctions compileToFunctions by createCompileToFunctionFn (compile) the compile of ginseng The number passed as a parameter to createCompileToFunctionFn execution. The next see createCompileToFunctionFn function:
5. In the SRC/compile/document to – function. The createCompileToFunctionFn js.
import { noop, extend } from 'shared/util' import { warn as baseWarn, tip } from 'core/util/debug' import { generateCodeFrame } from './codeframe' type CompiledFunctionResult = { render: Function; staticRenderFns: Array<Function>; }; function createFunction (code, errors) { try { return new Function(code) } catch (err) { errors.push({ err, code }) return noop } } export function createCompileToFunctionFn (compile: Function): Function { const cache = Object.create(null) return function compileToFunctions ( template: string, options? : CompilerOptions, vm? : Component ): CompiledFunctionResult { options = extend({}, options) const warn = options.warn || baseWarn delete options.warn /* istanbul ignore if */ if (process.env.NODE_ENV ! == 'production') { // detect possible CSP restriction try { new Function('return 1') } catch (e) { if (e.toString().match(/unsafe-eval|CSP/)) { warn( 'It seems you are using the standalone build of Vue.js in an ' + 'environment with Content Security Policy that prohibits unsafe-eval. ' + 'The template compiler cannot work in this environment. Consider ' + 'relaxing the policy to allow unsafe-eval or pre-compiling your ' + 'templates into render Function.')}}} // check whether the current template exists. Delimiters? String(options.delimiters) + template: const key = options.delimiters? String(options.delimiters) + template: template if (cache[key]) { return cache[key] } // compile const compiled = compile(template, options) // check compilation errors/tips if (process.env.NODE_ENV ! == 'production') { if (compiled.errors && compiled.errors.length) { if (options.outputSourceRange) { compiled.errors.forEach(e => { warn( `Error compiling template:\n\n${e.msg}\n\n` + generateCodeFrame(template, e.start, e.end), vm ) }) } else { warn( `Error compiling template:\n\n${template}\n\n` + compiled.errors.map(e => `- ${e}`).join('\n') + '\n', vm ) } } if (compiled.tips && compiled.tips.length) { if (options.outputSourceRange) { compiled.tips.forEach(e => tip(e.msg, vm)) } else { compiled.tips.forEach(msg => tip(msg, vm)) } } } // turn code into functions const res = {} const fnGenErrors = [] res.render = createFunction(compiled.render, fnGenErrors) res.staticRenderFns = compiled.staticRenderFns.map(code => { return createFunction(code, fnGenErrors) }) // check function generation errors. // this should only happen if there is a bug in the compiler itself. // mostly for codegen development use /* istanbul ignore if */ if (process.env.NODE_ENV ! == 'production') { if ((! compiled.errors || ! compiled.errors.length) && fnGenErrors.length) { warn( `Failed to generate render function:\n\n` + fnGenErrors.map(({ err, code }) => `${err.toString()} in\n\n${code}\n`).join('\n'), vm ) } } return (cache[key] = res) } }Copy the code
Compiled = compile(template, options) const compiled = compile(template, options) is the function argument passed by the parent function, BaseCompile (template.trim(), finalOptions));}} The final return value to compileToFunctions, res.render, is converted from baseCompile’s render to an executable code function using createFunction(compiled. Render, fnGenErrors). Juejin. Cn/post / 696924… Platforms /web/ entry-Runtime-with-compiler. js file createFunction is now found.
Summary: the compiler entry does a lot of things, such as merging platform parameters, function parameters, error messages, warning messages and so on, through the way of file association management.