超时与Error组件
在弱网环境下,加载一个组件可能需要很长时间,因此,要为用户提供指定超时时长的能力,当加载组件的时间超过了指定时长后,会触发超时错误。这时如果用户配置了Error组件,则会渲染该组件
首先设计用户接口,为了让用户能够指定超时时长,defineAsyncComponent函数需要接受一个配置对象作为参数:
const AsyncComp = defineAsyncComponent({
loader: ()=>import('CompA.vue'),
timeout: 2000, // 超时时长,其单位是ms
errorComponent: MyErrorComp // 指定出错时要渲染的组件
})
设计好用户接口后,就可以给出具体实现了,如下面代码所示:
function defineAsyncComponent(options){
// options可以是配置项,也可以是加载器
if(typeof options = 'function'){
// 如果options是加载器,则将其格式化为配置项形式
options = {
loader: options
}
}
const {loader} = options
let InnerComp = null
return {
name: 'AsyncComponentWrapper',
setup(){
const loaded = ref(false)
// 代表是否超时,默认为false,即没有超时
const timeout = ref(false)
loader().then(c => {
InnerComp = c;
loader.value = true
})
let timer = null
if(option.timeout){
// 如果制定了超时时长,则开启一个定时器计时
timer = setTimeout(()=>{
// 超时后将 timeout 设置为true
timeout.value = true
}, options.timeout)
}
// 包装组件被卸载时清除定时器
onUnmounted(()=>{
clearTimeout(timer)
})
// 占位内容
const placeholder = {type:Text, children: ''}
return ()=>{
if(loaded.value){
return {type: InnerComp }
}else if(timeout.value){
// 如果加载超时,并且用户指定了Error组件,则渲染该组件
return options.errorComponent ? {type: options.errorComponent}: placeholder
}
return placeholder
}
}
}
}
要注意的点:
- 需要一个标志变量来标识是否已经超时
- 当包装组件被卸载时,需要清除定时器
- 根据loaded变量的值以及timeout变量的值来决定具体的渲染内容
这样就实现了对加载超时的兼容,以及对Error组件的支持。除此之外,还要为用户提供以下能力:
- 当错误发生时,把错误对象作为Error组件的props传递过去,以便用户后续能自行进行更细粒度的处理
- 除了超时之外,还有有能力处理其他原因导致的加载错误,例如网络失败等
对代码做如下调整:
function defineAsyncComponent(options){
if(typeof options = 'function'){
options = {
loader: options
}
}
const {loader} = options
let InnerComp = null
return {
name: 'AsyncComponentWrapper',
setup(){
const loaded = ref(false)
// 定义error,当错误发生时,用来存储错误对象
const error = shallowRef(null)
loader().then(c => {
InnerComp = c;
loader.value = true
})// 添加catch语句来捕获加载过程中的错误
.catch((err)=>{error.value = err})
let timer = null
if(option.timeout){
// 如果制定了超时时长,则开启一个定时器计时
timer = setTimeout(()=>{
// 超时后创建一个错误对象,并复制给error.value
const err = new Error(`Async component timed out after ${options.timeout}ms.`)
error.value = err
}, options.timeout)
}
// 包装组件被卸载时清除定时器
onUnmounted(()=>{
clearTimeout(timer)
})
// 占位内容
const placeholder = {type:Text, children: ''}
return ()=>{
if(loaded.value){
return {type: InnerComp }
}else if(error.value && options.errorComponent){
// 只有当错误存在且用户配置了errorComponent时才展示Error组件,同时将error作为props传递
return {type:options.errorComponent, props:{error:error.value}}
}
return placeholder
}
}
}
}
在组件渲染时,只要error.value的值存在,且用户配置了errorComponent组件,就直接渲染errorComponent组件并将error.value的值作为该组件的props传递,这样用户就可以在自己的Error组件上,通过定义名为error的props来接收错误对象,从而实现细粒度的控制