Vue DOM挂载

Vue如何渲染DOM (第二章)

当我们new Vue时发生了什么?

接下来我们就来探究Vue是如何将该页面进行渲染的。在研究之前,我们必须了解Vue构造函数。但是在执行Vue函数前,当我们import Vue from 'vue'的时候,该框架做了这样的几个初始化:

initMixin(Vue)
//传入Vue,向其原型上添加_init函数
stateMixin(Vue)
//传入Vue,向其原型上添加 $set,$delete,$watch函数 并做其他初始化定义
eventsMixin(Vue)
//传入Vue,向其原型上添加 $on,$once,$off,$emit函数,并做其他初始化定义
lifecycleMixin(Vue)
//传入Vue,向其原型上添加 _update,$forceUpdate,$destroy函数,并做其他初始化定义
renderMixin(Vue)
//renderMixin函数向Vue原型添加 $nextTick,_render,在此之前执行了installRenderHelpers(Vue.prototype) 这个函数的执行也向Vue,.prototype上添加了很多内置的函数
//以上的5个函数的作用是向Vue.prototype上添加属性和方法
//对Vue进行包装
export default Vue
//当我们new Vue({})时就进入了上面定义的Vue函数了 

其实上面的函数是在我们导入Vue时,该框架本身做的一些初始化,这和我们执行Vue构造函数时内部的初始化不一样。这些函数主要是在我们使用Vue前,向Vue构造函数的原型上添加一些函数。而当我们执行Vue构造函数时,也会在内部进行一些初始化,例如响应式的完成和数据代理。所以我们需要区分这两种初始化

在我们import Vue from 'vue’的时候,主要做的是向Vue构造函数的原型上添加一些函数或属性。

然后我们就可以执行Vue构造函数了。当我们执行Vue构造函数的时候,本质上其实就是执行Vue.prototype._init()方法。

function Vue (options) {
  //options是我们传入的配置项
  //这个就是大名鼎鼎的vue构造函数,所有的vue项目的开始的地方
  if (process.env.NODE_ENV !== 'production' &&
    !(this instanceof Vue)
    //这里的this 其实使 vm实例,看是否用了new Vue
  ) {
    warn('Vue is a constructor and should be called with the `new` keyword')
  }
  //这里的this是我们执行new Vue 时构造函数内部生成的实例对象,也可以理解为vm也就是组件的实例对象,这里是根组件的实例对象
  this._init(options)
  //从这个函数进入我们用initMixin(Vue)初始化添加的_init函数
} 

这个this._init()是在initMixin()函数中声明的。this._init()函数中做了很多事情,比如合并options,执行一些函数:

 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') 

不过这些暂时不是我们关注的重点,重点是后面的代码,即关于渲染的代码。不过需要注意的是,这里的渲染指的是Vue已经完成了绝大部分事情,只剩下将浏览器上的视图重新渲染。

 if (vm.$options.el) {
      vm.$mount(vm.$options.el)
    } 

如果我们在vue构造函数的配置项中传入了el参数,那么将会执行vm.$mount函数,而传入的参数是vm,$options.el。也即是我们的#app字符串。

vm.$mount

vm.$mount的挂载其实是在./src/platforms/web/entry-runtime-with-complier.js当中,如果我们只在./src/core/的文件中其实是找不到的。vue的编译一共有两个版本:第一是runtime-only版本,另一个就是runtime-complier。在这里我们需要说明的是,什么是运行时+编译,其实当我们写如下代码:

var vm = new Vue({
    el:"#app",
    data(){
        return{
            message:123
        }
    }
}) 

Vue在进行模板渲染的时候其实是有一个对#app进行编译的一个过程,会将模板编译成一个render函数。从某种意义上来讲其实是对性能的一种消耗。假如我们写这样的一段代码:

var vm = new Vue({
    el:'#app',
    data(){
        return{
            message:123
        }
    },
    render:h(createElement)=>{
        return createElement({
            tag:'div'
        })
    }
}) 

此时我们添加了一项render。这个函数的作用是返回一个由createElement函数生成的一个vdom。用来代替我们的el。这样有什么好处呢。好处就是Vue不需要把模板编译成render函数。因为我们直接就提供给Vuerender函数。这样就省去了模板 ---> render函数的编译时间。对于runtime + complier版本的根组件我们不论写的是render/template,我们必须有一个前提,那就是我们需要写el。原因在于Vue需知道要我们最后的模板渲染到文档的那个位置,所以必须有el,但是对于组件来说,可能就不需要了

接下来我们来详细的讲解vm.$mount这个方法。该方法代码如下:

Vue.prototype.$mount = function ( el?: string | Element,//我们传入的el类型有两种,一种是字符串'#app',另一种是一个元素对象,例如document.getElementById('app')
  hydrating?: boolean ): Component {  
  el = el && query(el)//query函数主要是返回一个元素对象,如果我们传入的el存在,那么就返回该元素的对象形式(也就是真正的元素节点),如果不存在,那么就会默认是一个div元素对象
​
  /* istanbul ignore if */
  if (el === document.body || el === document.documentElement) {
    //这里告诉我们el不能是body和html。原因是它会发生覆盖,这样就会将原来的模板完全覆盖掉。
    process.env.NODE_ENV !== 'production' && warn(
      `Do not mount Vue to <html> or <body> - mount to normal elements instead.`
    )
    return this
  }
​
  const options = this.$options//这里的this指向的是vm实例对象
  // resolve template/el and convert to render function
  if (!options.render) {
    //如果我们没有render函数。那么会进入该区域代码。
    let template = options.template //获取模板
    if (template) {
      //如果存在template配置项,
      if (typeof template === 'string') {//如果配置项的类型为字符串。
        if (template.charAt(0) === '#') {//这里我们只处理template为#xxx的格式的模板,也就是类似于template:'#app'这种
          template = idToTemplate(template)//该函数返回的是template模板内部的节点的字符串形式。
          /* istanbul ignore if */
          //这里是template的错误处理
          if (process.env.NODE_ENV !== 'production' && !template) {
            warn(
              `Template element not found or is empty: ${options.template}`,
              this
            )
          }
        }
      } else if (template.nodeType) {
        //如果我们传入的template是一个节点对象,那么获取该节点对象中的innerHTML,然会的也是字符串形式
        template = template.innerHTML
      } else {
        //不是以上两种格式,那么抛出错误
        if (process.env.NODE_ENV !== 'production') {
          warn('invalid template option:' + template, this)
        }
        return this
      }
    } else if (el) {
      //如果template配置项不存在,那么获取el.outerHTML当作我们的template。返回的也是字符串类型
      template = getOuterHTML(e
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值