异步组件的注册-加载过程
依据上节简述可知,生成组件 vnode 之前会进行判断,若 ctor 组件信息为对象类型则将其转为组件构造器 Ctor 函数,然后又进行如下图所示逻辑:
由上图可知,每个组件构造器函数都有一个静态属性 cid,若不存在则进行异步组件处理。依据异步组件的定义可知,异步组件为一个匿名工厂函数,即上图中的 asyncFactory = Ctor 就是异步组件的匿名工厂函数,而 baseCtor 为 Vue 类。
异步组件的注册方式分为如下三类:
1、普通异步组件工厂函数
此时的全局组件 asyncExample 值为一个异步组件工厂函数,该工厂函数又两个参数(resolve, reject),当组件通过网络自动下载完成后执行 resolve 函数。
在 createComponent(Ctor) 函数内尝试创建组件 vnode 时,因为 Ctor 为异步组件工厂函数并且 Ctor.cid 值不存在,则直接执行 resolveAsyncComponent(Ctor,Vue) 函数。在该函数内先进行诸多校验,然后执行 res = factory(resolve, reject); 此处的 factory 就是异步组件工厂函数 Ctor .
在执行 res = factory(resolve, reject); 时,异步组件工厂函数会先发送请求去加载异步组件并获取到组件的对象。此过程为异步过程需要一定的时间等待,所以程序继续向下同步执行,此时的 res 值为 undefined,直接 return undefined 返回到 createComponent() 函数内,然后生成异步组件占位符 vnode 并返回该 vnode,最后生成对应的占位符 dom 节点并挂载页面。
当异步组件加载成功,将获取到的组件信息对象传入并执行 resolve() 函数。组件信息如图二所示,在该 {default: {}} 对象的 default 属性内。 在 resolve() 函数内通过 Vue.extend() 函数将组件信息对象转为组件构造器 Ctor 函数并赋值于异步组件工厂函数的 factory.resolved = Ctor; 属性上,最后执行 forceRender() 函数进行重新渲染该组件,其实就是循环调用 vm.$forceUpdate() 函数重新渲染页面即使数据没有改变也会重新渲染页面。
然后,在重新渲染页面时,当前组件渲染的时候依旧会走 createComponent(ctor) => resolveAsyncComponent(Ctor,Vue) 流程生成组件 vnode 对象。在 resolveAsyncComponent() 函数内先进行诸多校验,此时的异步组件工厂函数 factory.resolved 值为该组件的构造器函数,则直接返回该构造器函数并回到 createComponent() 函数内,然后添加组件钩子函数,最后创建组件 vnode 对象,最后进行 patch 过程与同步组件逻辑一致。
2、promise异步组件工厂函数
该加载流程与普通异步组件工厂函数基本一致,后续仅指出不同地方。
当重新渲染页面执行到 res = factory(resolve, reject); 时,factory 为通过 import 实现的箭头异步工厂函数,该函数返回值 res 为 promise 类型对象。当异步组件加载成功后,执行 res.then(resolve,reject) 函数,然后执行 resolve() 函数,该函数接收通过 import 导入的组件信息对象,如图二所示,组件信息在该 {default: {}} 对象的 default 属性内 。后续流程与普通异步组件一致,看上文简述,此处省略。
3、高级异步组件
由于异步组件需要动态加载,有一定的网络延迟并且也有加载失败或者超时的情况,所以需要设计 loading 组件和 error 组件,然后依据一定的时机加载它们,所以高级异步组件应运而生。
该加载流程与普通异步组件工厂函数基本一致,后续仅指出不同地方。
当重新渲染页面执行到 res = factory(resolve, reject) 时,此时的 factory 就是高级异步组件函数,而res 就是上图所示返回的对象。接着执行 res.component.then(resolve,reject),当异步组件加载成功执行 reslove() 函数,失败则执行 reject() 函数。接着进行如下校验:
- 先判断是否定义
error组件,即res.error是否存在,若定义则将该error组件对象转为组件构造器并赋值给factory.errorComp属性。 - 接着判断是否定义
loading组件,即res.loading是否存在,若存在则生成该组件的组件构造器并赋值给factory.loadingComp属性。若res.delay === 0则设置factory.loading = true,否则通过setTimeout(func, res.delay || 200)函数进行延时操作,等待res.delay时间后,属性factory.resolved不存在,即为我们需要的组件还没有加载成功,则设置factory.loading = true并执行forceRender()函数进行重新渲染。此时会重走该组件渲染流程,但此时我们需要的组件并没有加载成功,所以会渲染loading组件。 - 然后判断
res.timeout是否存在,若存在则表示组件渲染超时时间,通过setTimeout进行延时操作。在等待res.timeout时间后若factory.resolved值不存在,即组件加载失败,则执行reject()函数。在该函数内会判断error组件是否存在,若存在则进行页面重新渲染并渲染error组件。
最后若 factory.loading 为 true 则直接返回 loading 组件构造器函数factory.loadingComp,否则就返回我们需要的组件构造器函数 factory.resolved 。
简述高级异步组件加载过程,当 factory.resolved 的值为 undefined 时,会生成组件占位符 vnode 进而生成组件占位符 dom 并挂载页面。若 delay 属性存在,则延迟 delay 秒执行 forceRender() 函数进行重新渲染并渲染 loadingComp 组件。若 timeout 属性存在,则表示在 timeout 秒之后,组件加载成功则执行重新渲染并渲染 factory.resolved 组件,若没有加载成功则执行重新渲染并渲染 error 组件。
最后,异步组件的加载过程,需要两步或者三步:
- 注释节点 =》 组件
loading组件 =》 组件- 注释节点 =》
loading组件 =》 组件