我们知道,组件是
Vue
体系的核心,熟练使用组件是掌握Vue
进行开发的基础。上一节中,我们深入了解了Vue
组件注册到使用渲染的完整流程。这一节我们会在上一节的基础上介绍组件的两个高级用法:异步组件和函数式组件。
6.1 异步组件
6.1.1 使用场景
Vue
作为单页面应用遇到最棘手的问题是首屏加载时间的问题,单页面应用会把页面脚本打包成一个文件,这个文件包含着所有业务和非业务的代码,而脚本文件过大也是造成首页渲染速度缓慢的原因。因此作为首屏性能优化的课题,最常用的处理方法是对文件的拆分和代码的分离。按需加载的概念也是在这个前提下引入的。我们往往会把一些非首屏的组件设计成异步组件,部分不影响初次视觉体验的组件也可以设计为异步组件。这个思想就是按需加载。通俗点理解,按需加载的思想让应用在需要使用某个组件时才去请求加载组件代码。我们借助webpack
打包后的结果会更加直观。
webpack
遇到异步组件,会将其从主脚本中分离,减少脚本体积,加快首屏加载时间。当遇到场景需要使用该组件时,才会去加载组件脚本。
6.1.2 工厂函数
Vue
中允许用户通过工厂函数的形式定义组件,这个工厂函数会异步解析组件定义,组件需要渲染的时候才会触发该工厂函数,加载结果会进行缓存,以供下一次调用组件时使用。
具体使用:
// 全局注册:
Vue.component('asyncComponent', function(resolve, reject) {
require(['./test.vue'], resolve)
})
// 局部注册:
var vm = new Vue({
el: '#app',
template: '<div id="app"><asyncComponent></asyncComponent></div>',
components: {
asyncComponent: (resolve, reject) => require(['./test.vue'], resolve),
// 另外写法
asyncComponent: () => import('./test.vue'),
}
})
6.1.3 流程分析
有了上一节组件注册的基础,我们来分析异步组件的实现逻辑。简单回忆一下上一节的流程,实例的挂载流程分为根据渲染函数创建Vnode
和根据Vnode
产生真实节点的过程。期间创建Vnode
过程,如果遇到子的占位符节点会调用creatComponent
,这里会为子组件做选项合并和钩子挂载的操作,并创建一个以vue-component-
为标记的子Vnode
,而异步组件的处理逻辑也是在这个阶段处理。参考Vue3源码视频讲解:进入学习
// 创建子组件过程
function createComponent (
Ctor, // 子类构造器
data,
context, // vm实例
children, // 子节点
tag // 子组件占位符
) {
···
// 针对局部注册组件创建子类构造器
if (isObject(Ctor)) {
Ctor = baseCtor.extend(Ctor);
}
// 异步组件分支
var asyncFactory;
if (isUndef(Ctor.cid)) {
// 异步工厂函数
asyncFactory = Ctor;
// 创建异步组件函数
Ctor = resolveAsyncComponent(asyncFactory, baseCtor);
if (Ctor === undefined) {
return createAsyncPlaceholder(
asyncFactory,
data,
context,
children,
tag
)
}
}
···
// 创建子组件vnode
var vnode = new VNode(
("vue-component-" + (Ctor.cid) + (name ? ("-" + name) : '')),
data, undefined, undefined, undefined, context,
{
Ctor: Ctor, propsData: propsData, listeners: listeners, tag: tag, children: children },
asyncFactory
);
return vnode
}
**工厂函数的用法使得Vue.component(name, options)
的第二个参数不是一个对象,因此不论是全局注册还是局部注册,都不会执行Vue.extend
生成一个子组件的构造器,**所以Ctor.cid
不会存在,代码会进入异步组件的分支。
异步组件分支的核心是resolveAsyncComponent
,它的处理逻辑分支众多,我们先关心工厂函数处理部分。
function resolveAsyncComponent (
factory, baseCtor ) {
if (!isDef(factory.owners)) {
// 异步请求成功处理
var resolve