vue2源码阅读之vue子组件创建过程

new Vue() 之后的过程。

Vue.component('comp1', {template: '<div>com1</div>',
      created () {console.log('子组件创建')},
      mounted () { console.log('子组件挂载')}
})
vm = new Vue({
   el: '#app',
   data: {abc: 123},
   created () {console.log('父组件创建')},
   mounted () { console.log('父组件挂载')}
})
console.log(vm)
// 创建时,从上而下
// 挂载时,从下而上。
</script>

执行构造器函数

src\core\ins
tance\index.js 中定义了构造器。
执行它的_init方法,并传入options。

_init

这个方法是在 src\core\instance\init.js 中的initMixin函数中添加到Vue的原型对象上的。而initMixin()已经在src\core\instance\index.js 中调用过了。所以要看这个方法,要去到initMixin中去找。
主要工作:

  • 一系列初始化
  • 执行钩子created
  • 如果在配置项中传入了el,就去执行$mount
// 如果有el,会自动去执行$mount
    if (vm.$options.el) {
      vm.$mount(vm.$options.el)
    }

$mount

$mount方法在不同的平台下各自不同,在web平台,它先会在
src\platforms\web\entry-runtime-with-compiler.js 中得到扩展

// 扩展 $mount方法
// 在web平台下,$mount要额外做一些工作(在.html中经常需要直接写模板)
// 把 template / el 转成render函数,并保存到options.render
const mount = Vue.prototype.$mount
Vue.prototype.$mount = function (el,hydrating){
	// 1. 把 template / el 转成render函数,并保存到options.render
	if(!options.render) {
  	// 优先级: render > template > el
  	const { render, staticRenderFns } =compileToFunctions(template)
  	options.render = render
	}
  // 2. 调用原$mount
  return mount.call(this, el, hydrating)
}

那么这里的原$mount 是在哪定义的呢?src\platforms\web\runtime\index.js
在这个文件中,主要是做两件事

// 1. 声明__patche__. diff的过程是由__patch__来完成的
// import { patch } from './patch'
Vue.prototype.__patch__ = inBrowser ? patch : noop

// public mount method

// 2. 声明 $mount方法
Vue.prototype.$mount = function (){
   // 挂载的执行
  return mountComponent(this, el, hydrating)
}

mountComponent()

它定义在 src\core\instance\lifecycle.js 中,被$mount()调用。
内容:

  • 执行钩子函数 beforeMount
  • 定义updateComponent函数
    updateComponent = () => {
     vm._update(vm._render(), hydrating)
    }
    
  • 创建watcher并传入updateComponent函数
    在watcher内部,会执行一次updateComponent, 依次执行vm_render(), vm_update()
  • 执行钩子函数 mounted

_render()

接下来执行_render()
它定义在src/core/instance/render.js中。
_render使用在$options.render 来生成虚拟dom。(在$mount时,已经附加了render函数)

function anonymous() {
	with(this){
		return _c('div',{attrs:{"id":"app"}},[_c('comp1')],1
	}
}

注意到这里的render函数中有_c 。这里的_c是在 initRender 函数(这个函数是在src/core/instance/render.js文件中,initRender是在_init函数中被调用的)中定义的。

vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)

_c的作用是创建虚拟dom。它其实是通过定义在src\core\vdom\create-element.js文件中的createElement来具体执行。

createElement

createElement定义在src\core\vdom\create-element.js中,在它内部,它会调用_createElement来做具体的工作。

所以,执行渲染函数的过程中,_c其实就是 _createElement.

在执行的过程中, _c(‘div’) 是一个普通的标签,直接处理vnode = new VNode(),而_c(‘dom1’)中的dom1将会被当作自定义组件来处理,进而去调用createComponent()

createComponent

createComponent这个函数定义在src\core\vdom\create-component.js文件中。
在createElement调用createComponent之前,先通过resolveAsset取到这个组件的构造器,然后把构造器传给createComponent函数。。

然后,在这个函数内部,它只通过定义在
src\core\instance\init.js中的
resolveConstructorOptions函数去解析出com1这个组件的构造器选项。
这个函数内部,执行
installComponentHooks,给组件安装钩子:向data上补充hook:{destory,init,insert,prepatch}
最后:

const vnode = new VNode(
    `vue-component-${Ctor.cid}${name ? `-${name}` : ''}`,
    data, undefined, undefined, undefined, context,
    { Ctor, propsData, listeners, tag, children },
    asyncFactory
  )

这样就创建出来这个组件的虚拟DOM。
至此,整个的_rend()过程结束,下面开始进入到_update()。

patch

_update()会进入到patch中去。在patch时,由于这里是初始化,会进入到 createElm,在createElm中,调用createComponent去检查是否是自定义组件。

createComponent ()
在这个函数当中,会取当前vnode.data中的hook,并执行。

if (isDef(i = i.hook) && isDef(i = i.init)) {
        i(vnode, false /* hydrating */)
      }

如果它是一个自定义组件的话,它就会去执行hook中的init。init定义在create-component.js中的componentVNodeHooks.init()

const child = vnode.componentInstance = createComponentInstanceForVnode(
   vnode,activeInstance
)
  child.$mount(hydrating ? vnode.elm : undefined, hydrating)

它通过createComponentInstanceForVnode来创建组件的实例(此时会调用this_init,由于组件上没有el选项,需手动调用$mount, 至此,进入递归模式。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值