主题
模板编译入口
Introduction
对于umd
类型的打包,内部包括了模板的编译,以及其他所有核心的功能,而对于vue-cli
或者webpack
的工程,所用的 vue 是不到模板编译的内容的,这种项目中模板编译是通过vue-loader
来编译模板为 render 函数,因此我们这里分析的是功能最全的 vue
通过打包命令找到入口文件所在的位置:src/platform/web/runtime-with-compiler.ts
ts
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) {
__DEV__ &&
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 (__DEV__ && !template) {
warn(
`Template element not found or is empty: ${options.template}`,
this
);
}
}
} else if (template.nodeType) {
template = template.innerHTML;
} else {
if (__DEV__) {
warn("invalid template option:" + template, this);
}
return this;
}
} else if (el) {
// @ts-expect-error
template = getOuterHTML(el);
}
if (template) {
/* istanbul ignore if */
if (__DEV__ && config.performance && mark) {
mark("compile");
}
const { render, staticRenderFns } = compileToFunctions(
template,
{
outputSourceRange: __DEV__,
shouldDecodeNewlines, // 是否对一般html元素属性中换行符进行编码
shouldDecodeNewlinesForHref, // 是否对a标签中换行符进行编码
delimiters: options.delimiters,
comments: options.comments
},
this
);
options.render = render;
options.staticRenderFns = staticRenderFns;
/* istanbul ignore if */
if (__DEV__ && config.performance && mark) {
mark("compile end");
measure(`vue ${this._name} compile`, "compile", "compile end");
}
}
}
return mount.call(this, el, hydrating);
};
重写Vue.prototype.$mount
首先将 Vue 原型上面的$mount
方法保存下来,接着又重写了该方法
重写Vue.prototype.$mount
的逻辑如下:
兼容
el
, 最终el
为 dom 节点TIP
这就是为何再书写 vue 的时候, el 以传选择器, 也可以传真实的 DOM 节点的原因
Details
tsel = el && query(el); /** * Query an element selector if it's not an element already. */ export function query(el: string | Element): Element { if (typeof el === "string") { const selected = document.querySelector(el); if (!selected) { __DEV__ && warn("Cannot find element: " + el); return document.createElement("div"); } return selected; } else { return el; } }
拿到 options,若是没有
options.render
(render 函数),则会进入模板编译的流程用原先原型上的
$mount
进行挂载
接着进行分析没有options.render
(render 函数)时候的模板编译流程