Vue2.x源码:$mount挂载原理

<div id="testdata">
<h1>{{msg}}</h1>
  <p>{{name}}</p>

</div>

var app = new Vue({
  data() {
    return {
      msg: 'hello,weekend!',
      name: 'YanHSama'
    }
  },
  mounted() {
    console.log('vue', this);
  },
  watch: {

  },
  computed: {

  },
  methods: {
  },
})
// mount
app.$mount('#testdata')

在这里插入图片描述

Vue.prototype.$mount定义

找到入口src\platforms\web\entry-runtime-with-compiler.js

const mount = Vue.prototype.$mount缓存$mount

return mount.call(this, el, hydrating)执行的是缓存的$mount

//src\platforms\web\entry-runtime-with-compiler.js

import Vue from './runtime/index'

//缓存的$mount,来自import Vue from './runtime/index'
const mount = Vue.prototype.$mount

//重新定义的$mount
Vue.prototype.$mount = function (el ?: string | Element, hydrating ?: boolean): Component {
  // 获取el属性,挂载点
  el = el && query(el)

  // 实例不允许挂载在body或者html标签上
  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.`
    )
    return this
  }
  // options配置项
  const options = this.$options
  //  如果用户提供了 render 配置项,则直接跳过编译阶段,否则进入编译阶段, 优先级render——> template ——>el
  if (!options.render) {
    // console.log('inside', this)
    let template = options.template
    if (template) {
      // 处理 template 选项
      if (typeof template === 'string') {
        if (template.charAt(0) === '#') {
          // { template: '#app' },template 是一个 id 选择器,则获取该元素的 innerHtml 作为模版
          template = idToTemplate(template)
        }
      } else if (template.nodeType) {
        // template 是一个正常的元素,获取其 innerHtml 作为模版
        console.log('template.innerHTML', template.innerHTML)
        template = template.innerHTML
      } else {

        return this
      }
    } else if (el) {
      // 设置了 el 选项,获取 el 选择器的 outerHtml 作为模版
      template = getOuterHTML(el)
    }
    if (template) {

      // 编译模版,得到 动态渲染函数和静态渲染函数
      const {
        render,
        staticRenderFns
      } = compileToFunctions(template, {
        shouldDecodeNewlines,
        // 界定符,默认 {{}}
        delimiters: options.delimiters,
        // 是否保留注释
        comments: options.comments
      }, this)
      // 将两个渲染函数放到 this.$options 上
      options.render = render
      options.staticRenderFns = staticRenderFns
    }
  }
  // 执行挂载,执行的是缓存的$mount
  return mount.call(this, el, hydrating)
}

在这里插入图片描述

/**
 * Get outerHTML of elements, taking care
 * of SVG elements in IE as well.
 */
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
  }
}

在这里插入图片描述

//src\platforms\web\runtime\index.js

import { mountComponent } from 'core/instance/lifecycle'


Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
): Component {
  el = el && inBrowser ? query(el) : undefined
  return mountComponent(this, el, hydrating)
}

export function query (el: string | Element): Element {
  if (typeof el === 'string') {
    const selected = document.querySelector(el)
    if (!selected) {
      process.env.NODE_ENV !== 'production' && warn(
        'Cannot find element: ' + el
      )
      return document.createElement('div')
    }
    return selected
  } else {
    return el
  }
}

在这里插入图片描述

mountComponent函数

mountComponent函数

//src\core\instance\lifecycle.js
export function mountComponent (vm: Component, el: ? Element, hydrating ?: boolean): Component {
  vm.$el = el
  // 当options没有render选项时,创建render
  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
        )
      }
    }
  }
  // 执行beforeMount钩子
  callHook(vm, 'beforeMount')

  let updateComponent
  // 非生产环境-性能相关埋点,可忽略
  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}`

      mark(startTag)
      const vnode = vm._render()
      mark(endTag)
      measure(`vue ${name} render`, startTag, endTag)

      mark(startTag)
      vm._update(vnode, hydrating)
      mark(endTag)
      measure(`vue ${name} patch`, startTag, endTag)
    }
  } else {
  //  定义updateComponent函数
    updateComponent = () => {
       // 负责调用vm._update: ( arg1:VNode , arg2:SSR相关 ) => {}
      vm._update(vm._render(), hydrating)
    }
  }
  // 设置_watcher,将updateComponent放入一个新建的渲染watcher实例
  vm._watcher = new Watcher(vm, updateComponent, noop)
  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
    vm._isMounted = true
    // 执行定义的生命周期钩子函数mounted
    callHook(vm, 'mounted')
  }
  return vm
}

执行挂载完成

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值