在上面讲述中已经为data中每个属性添加了观察者 但是哪的dep dep.depend() dep.notify()收集 触发相应 我们还并不知道其是什么样的,在这之外还有更重要的东西 Wather, 继续从init 函数
Vue.prototype._init = function (options?: Object) {
// 省略...
vm._self = vm
initLifecycle(vm)
initEvents(vm)
initRender(vm)
callHook(vm, 'beforeCreate')
initInjections(vm) // resolve injections before data/props
initState(vm)
initProvide(vm) // resolve provide after data/props
callHook(vm, 'created')
// 省略...
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
}
最后一句 vm.$mount(vm.$options.el) 是_init初始化最后一行代码,之前都初始化了,我们先来了解一下$mount函数
$mount 的定义出现在两个地方,第一个地方是 platforms/web/runtime/index.js 文件,如下
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
): Component {
//接受两个参数 元素或者字符串 第二个先不用了解
el = el && inBrowser ? query(el) : undefined
query实在查找对应元素即字符串
return mountComponent(this, el, hydrating)
}
第二个在src/platforms/web/entry-runtime-with-compiler.js 文件 即完整版 其实在原功能上添加了编译模板的功能
const mount = Vue.prototype.$mount
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
): Component {
// 省略...
return mount.call(this, el, hydrating)
}
首先用mount缓存了运行版的$mount函数,然后重新定义了 $mount接受参数不变
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 or - mount to normal elements instead.`
)
return this
}
同样是先获取dom元素,这个元素为挂载点,之后判断是否为body和html元素 若是会警告,因为挂载点会被整个模板替换掉所以不能选择挂载body和html元素
const options = this.$options
// resolve template/el and convert to render function
if (!options.render) {
// 省略...
}
return mount.call(this, el, hydrating)
取得options应用 检测是否有render函数 若有就不做 直接运行运行版 其实是通过mountComponent来挂载 而if里的作用就是提供render函数给mountComponent的
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)
}
. 首先获取template,没有render优先使用template来渲染,并尝试编译为render函数 但是不一定会传template,这时会检测el是否存在,存在就使用el.outerHTML作为temlate值 其实 本质就是在选区合适内容做模板
template是有一个模板字符串, 后续就是求值的简化 和边界情况,如svg没有outerHTML 之后就到了关键阶段
if (template) {
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
mark('compile')
}
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')
}
}
使用 compileToFunctions将template编译为渲染函数 并且添加到vm.$options 而上下的 mark是在统计编译器性能的 其实mount函数作用就是将模板编译为渲染函数,然后交给 mountComponent去挂载
export function mountComponent (
vm: Component,
el: ?Element,
hydrating?: boolean
): Component {
// 省略...
接受3个参数 实例vm 挂载元素el 以及透传来的hydrating
}
这里到底是挂载在哪呢 vm.$el是id为bar的div引用, 继续向下
if (!vm.$options.render) {
vm.$options.render = createEmptyVNode
if (process.env.NODE_ENV !== 'production') {
/* istanbul ignore if */
if ((vm.$options.template && vm.$options.template.charAt(0) !== '#') ||
vm.$options.el || el) {
warn(
'You are using the runtime-only build of Vue where the template ' +
'compiler is not available. Either pre-compile the templates into ' +
'render functions, or use the compiler-included build.',
vm
)
} else {
warn(
'Failed to mount component: template or render function not defined.',
vm
)
}
}
}