13.1 异步组件要解决的问题
用动态
import()把组件拆成独立 chunk 按需加载,但加载过程中带来的失败、超时、占位、闪烁、重试等问题,需要在框架层面统一封装。
一、为什么需要异步组件
静态导入会把组件直接打进首屏产物,首屏必须等它一起下载完:
import CompA from 'CompA.vue' // 同步导入:进入首屏包
异步组件要解决的就是按需加载:只有真正需要渲染某个组件时,才去下载它的代码,从而减小首屏体积、加快首屏速度。
二、手动实现一个异步组件
在 Vue.js 3 中,用动态 import() + <component :is> 就能自己动手:
<template>
<CompA />
<component :is="asyncComp" />
</template>
<script>
import { shallowRef } from 'vue'
import CompA from 'CompA.vue'
export default {
components: { CompA },
setup() {
// 用一个 ref 存「以后才会拿到的组件」
const asyncComp = shallowRef(null)
// 异步加载 CompB,拿到后赋给 ref
import('CompB.vue').then(CompB => (asyncComp.value = CompB))
return { asyncComp }
}
}
</script>
CompA是同步渲染的,而asyncComp初始为null,所以<component :is>一开始什么都不渲染;import()返回 Promise,加载成功后把组件赋给asyncComp,响应式地触发重新渲染。
这只能算「能跑」:加载失败、加载太久、Loading 该什么时候显示,全都得用户自己处理。
三、用户不得不自己处理的四件事
| 问题 | 期望能力 |
|---|---|
| 组件加载失败或加载超时 | 渲染一个 Error 组件 |
| 组件正在加载中 | 渲染占位的 Loading 组件 |
| 加载速度可能很快、也可能很慢 | 给 Loading 组件设置延迟展示时间(如 200ms 内没成功才显示),避免闪烁 |
| 加载失败之后 | 提供重试能力 |
四、框架层要提供的接口
把这些能力封装进框架,对外暴露成一组配置项:
- 允许用户指定加载出错时要渲染的组件;
- 允许用户指定 Loading 组件,以及展示该组件的延迟时间;
- 允许用户设置超时时长;
- 组件加载失败时,为用户提供重试能力。
异步组件本质上是通过封装手段实现友好的用户接口,从而降低用户层面的使用复杂度。
defineAsyncComponent就是这层封装的产物,它是一个高阶组件:接收配置,返回一个包装组件。
相关
- 13.2.1 封装 defineAsyncComponent 函数:把上面这些能力逐个落到代码里