Vue的$mount做了什么

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算法

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容