src/platforms/web/entry-runtime-with-compiler.js
/* @flow */
import Vue from './runtime/index'
const idToTemplate = cached(id => {
const el = query(id)
return el && el.innerHTML
})
// 扩展$mount
const mount = Vue.prototype.$mount
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
): Component {
el = el && query(el)
// 选项
const options = this.$options
// resolve template/el and convert to render function
// 查找render选项
if (!options.render) {
// 获取template选项
let template = options.template
if (template) {
if (typeof template === 'string') {
if (template.charAt(0) === '#') {
template = idToTemplate(template)
}
} else if (template.nodeType) {
template = template.innerHTML
} else {
return this
}
} else if (el) {
template = getOuterHTML(el)
}
// 获取到html模板字符串之后,执行编译过程
if (template) {
// 编译template为render函数
const { render, staticRenderFns } = compileToFunctions(template, {
outputSourceRange: process.env.NODE_ENV !== 'production',
shouldDecodeNewlines,
shouldDecodeNewlinesForHref,
delimiters: options.delimiters,
comments: options.comments
}, this)
options.render = render
options.staticRenderFns = staticRenderFns
}
}
// 执行挂载
return mount.call(this, el, hydrating)
}
function getOuterHTML (el: Element): string {
if (el.outerHTML) {
return el.outerHTML
} else {
const container = document.createElement('div')
container.appendChild(el.cloneNode(true))
return container.innerHTML
}
}
Vue.compile = compileToFunctions
export default Vue
这个文件就是要扩展$mount:
因为数据选项中可能存在的template和el
先判断options中是否有render,再判断是否有template,最后才判断是否有el;因此得出的结论是render优先级大于template、template优先级大于el
在判断template的时候再判断template中是写的“#app"还是'<div>template</div>'
节点;最后把template选项处理成render
函数
如果没有render没有template最后就会执行mount.call(this, el, hydrating)
src/platforms/web/runtime/index.js
因为const mount = Vue.prototype.$mount
所以要找Vue
import Vue from 'core/index'
// install platform patch function
// 实现一个patch函数
Vue.prototype.__patch__ = inBrowser ? patch : noop
// public mount method
// 实现$mount
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
): Component {
el = el && inBrowser ? query(el) : undefined
return mountComponent(this, el, hydrating)
}
export function mountComponent (
vm: Component,
el: ?Element,
hydrating?: boolean
): Component {
vm.$el = el
callHook(vm, 'beforeMount')
let updateComponent
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
//...
} else {
updateComponent = () => {
vm._update(vm._render(), hydrating)
}
}
new Watcher(vm, updateComponent, noop, {
before () {
if (vm._isMounted && !vm._isDestroyed) {
callHook(vm, 'beforeUpdate')
}
}
}, true /* isRenderWatcher */)
return vm
}
至此$mount
的浅显功能就清楚了:
看了mountComponent的源码明白了,先执行$mount => 然后执行_render() => 最后执行 _update()(这个才是把虚拟dom变成真实dom的执行者);
另外还应该明白的是一个组件有一个watcher。
这里应该明白$mount有更新有初始化,
patch: 将vnode =》 node
patch 里边有两个分支,一个是初始化,一个是更新
真正实现$mount方法
$mount:生成真是dom,这里边一定会调用render和patch
如果想更近一步了解$mount在初始化和更新环节做了啥请看vue的diff算法