从创建实例到模板编译前Vue都做了些什么?

本文详细阐述了Vue构造函数创建实例时的内部步骤,从Vue._init方法开始,涵盖了初始化组件、合并构造器选项、处理props、inject、directives等,直至beforeCreate和created生命周期,揭示了Vue实例生成的全貌。
摘要由CSDN通过智能技术生成

写在前面

Vue构造函数的创建过程 一文中介绍了 Vue构造函数 的创建过程,其中第一个对 Vue构造函数 进行成员添加的就是 initMixin(Vue),该调用内创建了 _init 方法。可以说,Vue实例 的大门就是 _init 方法,因此,我们从这个方法入手,一步一步剖析 vm 是如何生成的。

/*
<div id="app">
  <div>
    <h1>{
   {msg}}</h1>
    <div id="extendUse"></div>
    <child-component :msg="msg" />
  </div>
</div>
*/
// Vue.extend 手动挂载组件
const ExtendUse = Vue.extend({
   
  props: ['msg'],
  template: `<h2>ExtendUse said: {
    {msg}}</h2>`,
})
const ChildExtend = new ExtendUse({
   
  propsData: {
   
    msg: 'hello Extend'
  }
}).$mount('#extendUse')
// Vue.component 自动挂载组件
const ChildComponent = Vue.extend({
   
  props: ['msg'],
  template: `<h2>child's father said: {
    {msg}}</h2>`,
})
Vue.component('child-component', ChildComponent)
// Vue 实例
const vm = new Vue({
   
  el: '#app',
  data: {
   
    msg: 'hello Vue',
  }
})

按照惯例,我先将 _init 的源码简写一下,并且划分一下步骤:

Vue.prototype._init = function (options) {
   
  // 步骤 - 1
  const vm = this
  vm._uid = uid++
  vm._isVue = true
  // 步骤 - 2
  if (options && options._isComponent) {
   
    initInternalComponent(vm, options)
  } else {
   
    vm.$options = mergeOptions(
      resolveConstructorOptions(vm.constructor),
      options || {
   },
      vm
    )
  }
  // 步骤 - 3
  if (process.env.NODE_ENV !== 'production') {
   
    initProxy(vm)
  } else {
   
    vm._renderProxy = vm
  }
  vm._self = vm
  // 步骤 - 4
  initLifecycle(vm)
  initEvents(vm)
  initRender(vm)
  callHook(vm, 'beforeCreate')
  initInjections(vm)
  initState(vm)
  initProvide(vm)
  callHook(vm, 'created')
  // 步骤 - 5
  if (vm.$options.el) {
   
    vm.$mount(vm.$options.el)
  }
}

步骤 - 1

在这段代码中,我们首要了解的是 vm 指向了谁?
从它的调用 function Vue(options) { this._init(options) } 中我们可以了解到,Vue 是一个构造函数,而 _init 方法又不是该构造函数的静态方法,因此 this 指向了实例,所以 const vm = this 这一步其实就是将实例本身赋值给了 vm。

再具体点?const app = new Vue(options) 这里的 app 就是 vm 的值。

接着,在实例身上添加上 _uid 和 _isVue 的标识,记录当前实例是第 (_uid + 1) 个 Vue 实例对象。

步骤 - 2

这段代码是一个条件判断,判断的是是否是一个组件。那怎样的存在算是一个组件呢?Vue.extend 手动挂载上去的算不算一个组件呢?

我就先兜个底,只有存在于 vm.options.components 中的才算组件,且能进入 if 判断之中。这就意味着,要么 Vue.component(id, definition) 定义的对象,要么 new Vue({ components: { id: definition } }) 定义的对象,其他方式都不具备 _isComponent 属性。因此,即使是 Vue.component 方法内同样也调用了的 Vue.extend 方法,在手动挂载时也不算作组件,不具备 _isComponent 属性。

那这段代码的意义是什么呢?简而言之,就是将 Vue构造函数 中的成员变量 options 和我们传入的 options 合并然后挂载到实例的 $options 属性上。

initInternalComponent

不知道大家想没想过,组件为什么会进到 _init 中来,哪儿定义了它也可以进来进行初始化操作?

如果你没有忘记 详解Vue.component和Vue.extend 一文中讲过,Vue.component 中会调用一次 Vue.extend,将生成的实例存入 vm.options.components 之中。而在 Vue.extend 中我们曾定义过一个 VueComponent构造函数,这个构造函数继承了 Vue构造函数,因此也有 _init 方法,在 VueComponent构造函数 的 constructor 中又恰好调用了 this._init(options),所以套了一层又一层,剥丝抽茧后你应该就能明白为什么组件可以进来了吧。

那么,这个方法内做了些什么?当你进入函数体你会发现,options 中哪来的这么多属性???我丢,见都没见过啊,一脸懵逼逐渐变成N脸懵逼。所以我们先按下不表,因为其中涉及到模板编译部分,扯得太远回不来就麻烦了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值