vue组件化原理

4,203 阅读2分钟

组件是如何创建和挂载的?它和父组件的创建和挂载是个怎样的过程?组件的生命周期是怎样的?

我们平时使用组件都是先进行组件注册,组件注册有两种方式,一种是注册全局组件使用Vue的全局静态方法Vue.component进行注册,还有一种就是组件内部利用components属性进行注册。注册之后我们就会在模板里进行声明使用,模板里声明使用的组件最后回被编译返回一个渲染函数rander。在未来某个时刻执行渲染函数rander得到虚拟DOM(vnode),再由vnode渲染成真实DOM进行挂载。

步骤:

  • 注册组件

    Vue.component(id, comp)

    components

  • 声明组件

<template>
    <comp></comp>
</tamplate>
  • 编译得到渲染函数 rende [template => render()]

  • 某个时刻(挂载$mount=>mountComponent)执行渲染函数render() => vnode

  • patch

模板里的组件编译后是什么样的

<div id="app">
    <h1>coboy</h1>
	<comp></comp>
</div>
<script src="https://unpkg.com/vue@2.6.14/dist/vue.js"></script>
<script>
    Vue.component('Comp', {
    template: '<div>this a comp</div>'
})
const app = new Vue({
    el: '#app'
})
console.log('render:', app.$options.render)
</script>

打印出来的渲染函数

//render: 
ƒ anonymous(
) {
with(this){
    return _c('div',{attrs:{"id":"app"}},[
            _c('h1',[
             _v("coboy")]),
        	  _v(" "),
                _c('comp')],1)
	}
}

得到的渲染函数是一个匿名函数

with(this)限定了函数里面的this都是执行当前组件的实例

我们发现渲染函数render在处理html保留标签和组件标签都是使用_c这个函数进行处理

这些下划线的函数在core\instance\render-helpers\index.js里。但_c却在core\instance\render.js里的initRender函数中定义的。其中_c是Vue内部使用的,用于编译器生成的render函数,用户编写的render函数使用的是$createElement(h函数) 。

我们发现处理原生的HTML元素和自定义组件都是使用同一个函数_c,然后在createElement里进行判断如果是HTML保留标签就创建标签,否则就进行组件实例创建。

怎么进行组件注册?

先进进行组件定义再组件实例化,组件实例化之后,组件实例里有组件挂载函数$mount

// core/global-api/index
// 元件的注册
initAssetRegisters(Vue)

// core/global-api/assets
function initAssetRegisters()
// 如果是对象,说明传入是组件配置,此时需要做转换:对象=》组件构造函数
 if (type === 'component' && isPlainObject(definition)) {}

Vue.extend()
// 构造函数的获取:Vue.extend(obj) => VueComponet
definition = this.options._base.extend(definition)
// 注册到全局配置项中
// options.components['comp'] = Ctor
// 全局注册就是添加到系统选项中,以后所有组件初始化的时候,会有一个合并的动作,这样所有的这些全局组件就放入了当前组件的components选项中
this.options[type + 's'][id] = definition

通过传入组件配置获取组件构造函数VueComponet之后,可以通过new VueComponent得到组件实例,然后执行组件实例的渲染函数得到vnode,再通过组件实例的$mount方法进行挂载,这个过程和根组件的挂载过程是一样的。

自定义组件创建和挂载的时机

在父组件创建后,未挂载前。在patch()中创建和挂载。

父子组件的生命周期关系

创建过程:先父后子。挂载过程:先子后父。 parent.created => parent.beforeMounted => child.created => child.beforeMount => child.mounted… => parent.mounted…