// 最开始通过mount获取并缓存了Vue原型上的$mount方法,然后又重新定义了Vue.prototype.$mount // 执行到到最后,通过return mount.call(this, el, hydrating) 重新调用mount缓存下来的原型方法。 const mount = Vue.prototype.$mount Vue.prototype.$mount = function ( el?: string | Element, hydrating?: boolean ): Component { // 使用query来获取要挂载DOM元素节点 el = el && query(el)
/* istanbul ignore if */ // 判断el是否为body,或者document,如果是则返回,因为本身index.html已经含有html,body元素 // 如果el是body或者文档标签,会替换原本的html和body. 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.` ) returnthis }
const options = this.$options // resolve template/el and convert to render function /*处理模板templete,编译成render函数,render不存在的时候才会编译template,否则优先使用render*/ // 判断options中是否有render方法,有则直接调用mount方法, // 如果没有render,则需要调用compileToFunctions生成render再调用mount方法 if (!options.render) { /*template存在的时候取template,不存在的时候取el的outerHTML*/ let template = options.template // 会判断有没有写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 ) } } } elseif (template.nodeType) { /*当template为DOM节点的时候*/ template = template.innerHTML } else { if (process.env.NODE_ENV !== 'production') { warn('invalid template option:' + template, this) } returnthis } } elseif (el) { // template = getOuterHTML(el) } if (template) { /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production' && config.performance && mark) { mark('compile') }
query方法获取到 DOM 元素后,往下走,判断 el 元素如果是 body 或者 document,就报错 并且 return。 因为 index.html 里已经有了 html 和 body。
1 2 3 4 5 6 7
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.` ) returnthis }
el = el&&query(el),此操作是获取到我们定义的 DOM 节点,'#app'或document.querySelector('#app');
判断options中是否含有 render 方法。如果我们直接手写render函数,就会直接执行return mount.call(this, el, hydrating)然后就回去执行之前缓存的原型方法。
// 如果此时还是没有render方法,那就要抛出错误提示 if (!vm.$options.render) { // 判断是否有render函数,如果没有写render函数,并且template未转换成render函数。就创建一个空的VNode vm.$options.render = createEmptyVNode // 下面这部分分是:如果你没有用template,又没写render函数,在开发环境就会报此警告 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 ) } } } callHook(vm, 'beforeMount') //beforeMount
let updateComponent //构建updateComponent方法,更新组件需要用到 // 和性能埋点相关的 /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production' && config.performance && mark) { updateComponent = () => { const name = vm._name const id = vm._uid const startTag = `vue-perf-start:${id}` const endTag = `vue-perf-end:${id}`
// we set this to vm._watcher inside the watcher's constructor // since the watcher's initial patch may call $forceUpdate (e.g. inside child // component's mounted hook), which relies on vm._watcher being already defined // 渲染Watcher newWatcher( vm, updateComponent, noop, // 空function { before() { if (vm._isMounted && !vm._isDestroyed) { callHook(vm, 'beforeUpdate') } }, }, true/* isRenderWatcher */ ) hydrating = false
// manually mounted instance, call mounted on self // mounted is called for render-created child components in its inserted hook // 手动挂载实例 if (vm.$vnode == null) { vm._isMounted = true callHook(vm, 'mounted') } return vm }
首先判断render函数是否已经构建好,如果为构建好久报错;
如果为报错就构建updateComponent方法,这个方法每次更新组件的时候就会调这个方法;
然后 new Watch 是 Vue 响应式处理中的依赖收集过程,其原理采用了观察者模式
最后手动挂载实例。
总结
是否 compile 版本,判断先执行那一部分代码。
entry-runtime-with-compiler.js 会去判断是否有 render 函数,如果有就执行return mount.call(this, el, hydrating)然后就回去执行之前缓存的原型方法. 如果没有render方法.会将template做为参数,运行时调用compileToFunctions方法,转化为render函数,再去调用return mount.call(this, el, hydrating)方法。