组件(
Component
)是Vue
一个重要的功能。一般用于包装可重用代码,或者是定义高级组件。对于小型项目,(可能)会增强代码的结构性,但是并非必须。对于稍大型的项目,它则是模块化的基础,也是代码拆分常用的形式。组件有很多内容,比如组件消息传递、数据传递、
slot
、异步组件等等,作为一个有品味的博主,以上内容本篇统统不涉及。本篇只从一个注册+创建角度说一下组件是怎么一步一步放到页面上的。基础示例代码(来自
Vue
官方文档)// 注册
Vue.component('my-component', {
template: '
A custom component!
'
})
// 创建根实例
new Vue({
el: '#example'
})
整体来说,组件使用需要经过两个步骤:声明和实例化。声明即把组件的显式声明内容,处理为配置项,挂载到实例上。接着根据模板解析的流程,在不同的地方根据配置初始化为一个一个的组件实例。
组件声明
组件通常使用全局注册或者局部注册,二者没有本质区别(吧)。我们从全局注册角度说一下
Vue.component(name, definition) // name为'my-component',definition是配置项
-Sub = Vue.extend(definition)
-Vue.options['components']['my-component'] = Sub
Sub structure
子组件(Sub)结构
组件渲染(实例化)
重述一下前面的流程
-initRender
-Vue._mount
-vm._render
-createElm // 生成VNode节点
createElm
这里会分为组件、浏览器tag
、comment
、文本分别处理对应的element
。Vue挂载的根组件只能为组件或者tag
,不能为comment
或者文本。另外,在
Vue
模板解析(initRender
)时,不区分是否为组件的。所有元素都在createElement
函数内处理,最后都会生成一个模板函数_h(tagName, {...})
实际区分在从组件的父节点调用
createElm
,来生成VNode
的时候。为不打断流程,组件VNode
相关放在最后说。如果是组件,会进行组件初始化(
init
)流程:init
负责会按层次处理好子节点-vnode.child = createComponentInstanceForVnode
-vnode.child.$mount
=Vue.prototype._mount
按照最外层vm实例初始化的步骤,再走一边组件的初始化
vm._render // 这里vm是vnode.child,即VueComponent实例
-vm._update
-vm.$el = vm.__patch__
基本流程图如下

component init
组件渲染过程
这是一个递归调用,直到节点不为组件(浏览器
tag
、comments
、text
三种),返回创建的基础elm元素。当节点的children
为空时,会返回父级创建的浏览器tag
元素。生成的元素被放入
vnode.elm
中。组件
VNode
相关-createElm
-createElement
-createComponent
如果是组件,会先返回一个占位
vnode
,当将组件的所有子节点按层次处理完之后,再把child
(此时已经为可用的tag
)赋值给vnode.elm
,这样这个组件就可以使用了。vnode structure
VNode结构
这个占位
vnode
,除默认字段外,只设置了tag、data、context、componentOptions
// tag
("vue-component-" + (Ctor.cid) + (name ? ("-" + name) : '')) // 如:vue-component-1-button-counter
// data
{hook: init, insert, prepatch, destroy} // 组件操作入口
// componentOptions
{ Ctor: Ctor, propsData: propsData, listeners: listeners, tag: tag, children: children }
createElement
判断定义了data.hook
函数,就执行组件实例初始化和挂载,这是个基础流程,和Vue
初始化的一致。