一,前言
上篇,进行了 diff 算法阶段性梳理,主要涉及以下几个点:
- 初渲染与视图更新流程;
- diff 算法的外层更新;
- diff 算法的比对优化;
- diff 算法的乱序比对;
- 初渲染和更新渲染判断;
本篇,组件的初始化流程介绍;
二,组件使用介绍
1,组件的介绍
组件,即自定义标签,设计理念源于 WebComponent,也就是 Web 组件;原生支持自定义标签,但是兼容性并不好;
所以,Vue和React就各自实现了一套自己的组件 API;
2,组件的定义
在vue中,组件分为全局组件和自定义组件两种,定义方式如下:
2.1 全局组件
<body>
<div id="app1">
<!-- 使用 my-button 组件 -->
<my-button></my-button>
</div>
<div id="app2">
<!-- 使用 my-button 组件 -->
<my-button></my-button>
</div>
<script>
// 定义 my-button 组件
Vue.component('my-button',{
template:'<button>Hello Vue</button>'
})
new Vue({
el: "#app"
});
</script>
</body>
全局组件通过 Vue.component('xxx',{...})进行定义,能够在全局范围内使用;
2.2 局部组件
<body>
<div id="app1">
<!-- 可以使用 -->
<my-button></my-button>
</div>
<div id="app2">
<!-- 不可以使用 -->
<my-button></my-button>
</div>
<script>
new Vue({
el: "#app1",
// 声明局部组件:只能在组件声明的作用域 app1 下使用
components:{
'my-button':{
template:'<button>Hello Vue 局部组件</button>'
}
}
});
</script>
</body>
局部组件,在Vue初始化传入的Options中声明,只能在声明作用域下被使用;
3,组件的优先级
当全局声明的组件与局部声明的组件名称相同时,哪个组件生效?优先级规则怎么样?
<body>
<div id="app">
<my-button></my-button>
</div>
<script>
// 全局组件
Vue.component('my-button',{
template:'<button>Hello Vue 全局组件</button>'
})
// 局部组件
new Vue({
el: "#app",
components:{
'my-button':{
template:'<button>Hello Vue 局部组件</button>'
}
}
});
</script>
</body>
同名的全局组件和局部组件同时存在时,根据组件查找规则,优先使用局部组件;
相同名称的全局组件和局部组件定义并不会被覆盖,而是会像原型链一样,逐级向上进行查找;
组件作用域的实现应用了原型的概念:优先查找实例上的
component,如果没有找到,再通过链继续向上进行查找;
三,组件初始化流程简介
1,Vue.component
Vue.component 是一个全局 API;
在 Vue 初始化流程的 initGlobalAPI 方法中,对 Vue Global API 进行集中处理;
// Vue.component 方法定义
Vue.component = function (id, definition) { ... }
// Vue.component 的使用方式
Vue.component('my-button',{
name:'my-button',
template:'<button>全局组件</button>'
})
在早期的 Vue2 中,定义组件并不是使用 Vue.component,而是 使用 Vue.extend 来进行组件的声明;
那么,Vue.component 与 Vue.extend 有什么关系呢?
2,Vue.extend
根据官方的 API 文档,对比两个这 API:
Vue.extend 作用:使用基础 Vue 构造器,创造一个子类(即组件的构造函数);可以通过 new 这个对象创建组件实例;
在 Vue.component 中,有两个参数:
- 第一个参数
id:组件的唯一标识;优先使用name属性; - 第二个参数
definition:组件定义,可以是Function(异步组件)或Object(普通定义);- 1)
definition可以直接传入一个Vue.extend; - 2)
definition可以传入一个对象;此时,在Vue.component内部会使用Vue.extend进行一次处理;
- 1)
从官网文档的介绍中可以看出,在
Vue.component的实现中,使用了Vue.extend;
3,保存组件构造函数
将组件名 name 与构造函数 definition 的映射关系,保存到全局对象 Vue.options.components 中;
在全局组件中,需要使用全局属性;
在组件的初始化时,全局属性会被放到每个组件上
所以,全局组件也需要被注册到
Vue.options上;即保存到全局对象Vue.options.components中;同时便于后续组件合并时进行查找;
4,全局组件与局部组件合并
在 Vue 初始化时执行的 _init 方法中,会通过 mergeOptions 方法对 Options 选项进行合并;
在 mergeOptions 方法内部,会根据组件的合并策略,完成“全局组件”和“局部组件”的合并操作;
备注:此时,
mergeOptions方法的vm.constructor.options中已经包含了Vue.options.components;组件的查找规则:优先找自己,如果找不到再继续通过链向上找父亲;
5,组件的合并策略
模板编译流程:html模板 -> AST语法树 -> render函数;
在 render 函数中,会通过 _c(即 createElm 方法)处理标签和组件;
createComponent 方法:创建组件的虚拟节点 componentVnode;
6,组件的渲染和更新
根据组件的虚拟节点,创建出组件的真实节点,并将组件插入到对应的父元素中;
在组件初始化时,会为每个组件创建出一个 watcher 渲染函数;
在依赖收集阶段,属性会收集对应组件的渲染 watcher 并记录到其 dep 属性中;
当组件更新时,遍历通知 dep 数组中对应的 watcher 执行组件更新;
四,结尾
本篇,介绍了 Vue 组件与初始化流程,涉及以下几部分:
- 组件使用介绍:全局组件、局部组件的定义和优先级;
- 组件初始化流程介绍:Vue.component、Vue.extend、保存组件构造函数、组件合并、初渲染和更新;
下一篇,Vue.component 的实现;
todo:本篇,作为组件部分的整个流程介绍,后期应该着重从全局视角对整个流程进行说明,比如:描述关键步骤并画出流程图;
维护日志
- 20210808:更新文章标题为“组件部分-Vue组件与初始化流程简介”;调整“组件初始化流程简介”部分内容:体现组件初始化流程中各主要环节及对应的文章更新计划;
- 20210813:修改部分文字和单词拼写错误;
- 20230223:添加了官方文档截图;添加了内容中的代码和关键字高亮;优化了部分内容描述,使表述更加清晰易懂;更新了文章摘要;
- 20230225:调整了部分描述;添加了 todo;