Vue 源码
你想要的 vue 源码分析 · 23
编译方式 模版->真实 DOM 的渲染过程中,有把模版编译成 render 函数,这个过程就叫编译 vue 提供了两种编译方法:Runtime + Compiler 和 Runtime only Runtime + Compiler:如果在 vue 对象中用了 template,在运行中就可以编译的 Runtime only:在编译阶段把.vue 文件编译成 rener 函数 编译入口 $moun…
编译方式 模版->真实 DOM 的渲染过程中,有把模版编译成 render 函数,这个过程就叫编译 vue 提供了两种编译方法:Runtime + Compiler 和 Runtime only Runtime + Compiler:如果在 vue 对象中用了 template,在运行中就可以编译的 Runtime only:在编译阶段把.vue 文件编译成 rener 函数 编译入口 $moun…
// src/platforms/web/entry-runtime-with-compiler.js
const mount = Vue.prototype.$mount;
Vue.prototype.$mount = function(el?: string | Element, hydrating?: boolean): Component {
el = el && query(el);
/* istanbul ignore if */
if (el === document.body || el === document.documentElement) {
process.env.NODE_ENV !== 'production' && warn(`Do not mount Vue to <html> or <body> - mount to normal elements instead.`);
return this;
}
const options = this.$options;
// resolve template/el and convert to render function
if (!options.render) {
let template = options.template;
if (template) {
if (typeof template === 'string') {
if (template.charAt(0) === '#') {
template = idToTemplate(template);
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && !template) {
warn(`Template element not found or is empty: ${options.template}`, this);
}
}
} else if (template.nodeType) {
template = template.innerHTML;
} else {
if (process.env.NODE_ENV !== 'production') {
warn('invalid template option:' + template, this);
}
return this;
}
} else if (el) {
template = getOuterHTML(el);
}
if (template) {
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
mark('compile');
}
// 拿到template后
// 这个就是编译入口
const { render, staticRenderFns } = compileToFunctions(
template,
{
shouldDecodeNewlines,
shouldDecodeNewlinesForHref,
delimiters: options.delimiters,
comments: options.comments
},
this
);
options.render = render;
options.staticRenderFns = staticRenderFns;
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
mark('compile end');
measure(`vue ${this._name} compile`, 'compile', 'compile end');
}
}
}
return mount.call(this, el, hydrating);
};
// src/platforms/web/compiler/index.js
// compileToFunctions是createCompiler的返回值,该方法接收一个编译参数,
createCompiler;
import { baseOptions } from './options';
import { createCompiler } from 'compiler/index';
const { compile, compileToFunctions } = createCompiler(baseOptions);
export { compile, compileToFunctions };
// src/compiler/index.js
// createCompiler是通过调用createCompilerCreator方法返回的,这个方法传入一个函数,
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
};
});
// src/compiler/create-compiler.js
// 返回一个createCompiler函数,接收一个baseOptions
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 || null), 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;
}
// 返回compile和compileToFunctions,compileToFunctions就是$mount函数调用的compileToFunctions 方法
return {
compile,
compileToFunctions: createCompileToFunctionFn(compile)
};
};
}
// src/compiler/to-function/js
// 核心编译过程:const compiled = compile(template, options);
export function createCompileToFunctionFn(compile: Function): Function {
const cache = Object.create(null);
// compileToFunctions定义:接收三个参数:模板template,配置options,Vue实例vm
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 {
// Function可以把代码字符串变成function
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 functions.');
}
}
}
// 缓存操作,同一个模板不反复编译
const key = options.delimiters ? String(options.delimiters) + template : template;
if (cache[key]) {
return cache[key];
}
// 核心编译过程
// compile在执行createCompileToFunctionFn时传入
const compiled = compile(template, options);
// check compilation errors/tips
if (process.env.NODE_ENV !== 'production') {
if (compiled.errors && compiled.errors.length) {
warn(`Error compiling template:\n\n${template}\n\n` + compiled.errors.map((e) => `- ${e}`).join('\n') + '\n', vm);
}
if (compiled.tips && compiled.tips.length) {
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);
};
}
// src/compiler/create-compiler.js
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 || null), 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;
}
export const createCompiler = createCompilerCreator(function baseCompile(template: string, options: CompilerOptions): CompiledResult {
// 解析模板生成抽象语法树
const ast = parse(template.trim(), options);
// 优化语法树
optimize(ast, options);
// 生成代码
const code = generate(ast, options);
return {
ast,
render: code.render,
staticRenderFns: code.staticRenderFns
};
});
暂无评论
确认操作
请再次确认编辑图片
待上传图片 (剩余 0 张)
项目详情
确认评论邮箱
邀请制评论