【vue设计与实现】异步组件与函数式组件 3 - 延迟与Loading组件

本文探讨了如何在异步组件加载过程中避免闪烁问题,通过设置延迟展示Loading组件和合理设计用户接口。关键在于控制何时显示Loading组件,以及如何实现延迟和错误处理机制。

异步组件加载过程可能很慢,所以可以通过展示Loading组件来提供更好的用户体验,这样用户就不会有“卡死”的感觉了。
但是展示Loading组件的时机是一个需要仔细考虑的问题。如果从加载开始的一开始就展示Loading组件,那么在网络状况好的情况下,异步组件的加载速度会非常快,这会导致Loading组件刚完成渲染就立即进入卸载阶段,于是出现闪烁的情况。这样体验特别不好,因此要为Loading组件设置一个延迟展示的时间。例如,当超过200ms没有完成加载,才展示Loading组件。这样就能避免闪烁问题。
但是,首先要考虑的仍然是用户接口的设计,如下面代码所示:

defineAsyncComponent({
	loader: ()=>new Promise(r=>{/* ... */}),
	// 延迟200ms 展示Loading组件
	delay:200,
	// loading组件
	loadingComponent:{
		setup(){
			return ()=>{
				return {type:'h2',children:'Loading...'}
			}
		}
	}
})

然后可以着手实现延迟时间与Loading组件,代码如下:

function defineAsyncComponent(options){
	if(typeof options === 'function'){
		options = {
			loader: options
		}
	}
	const {loader} = options

	let InnerComp = null

	return {
		name: 'AsyncComponentWrapper',
		setup(){
			const loaded = ref(false)
			const error = shallowRef(null)
			// 标志是否正在加载,默认为false
			const loading = ref(false)
	
			let loadingTimer = null
			// 如果配置项中存在delay,则开启一个定时器计时,当延迟到时候将loading.value设置为true
			if(options.delay){
				loadingTimer = setTimeout(()=>{
					loading.value = true
				}, options.delay);
			}else{
				// 如果配置中没有delay,则直接标记为加载中
				loading.value = true
			}
			loader()
				.then(c=>{
					InnerComp = c
					loader.value = true
				})
				.catch((err)=>{error.value = err}
				.finally(()=>{
					loading.value = false
					// 加载完毕后,无论是否成功都要清除定时器
					clearTimeout(loadingTimer)
				})
			let timer = null
			if(option.timeout){
				timer = setTimeout(()=>{
					const err = new Error(`Async component timed out after ${options.timeout}ms.`)
					error.value = err
				}, options.timeout)
			}
			
			const placeholder = {type: Text, children:''}
			return ()=>{
				if(loaded.value){
					return {type: InnerComp }
				}else if(error.value && options.errorComponent){
					return {type:options.errorComponent, props:{error:error.value}}
				}else if(loading.value && options.loadingComponent){
					// 如果异步组件正在加载,并且用户指定了Loading组件,则渲染Loading组件
					return {type:options.loadingComponent}
				}
				return placeholder
			}
		}
	}
}

要注意的是,在渲染函数中,如果组件正在加载,并且用户指定了Loading组件,则渲染该Loading组件

另外要注意,当异步组件加载成功后,会卸载Loading组件并渲染异步加载的组件。为了支持Loading组件的卸载,需要修改unmount函数,如下代码所示:

function unmount(vnode){
	if(vnode.type === Fragment){
		vnode.children.forEach(c => unmount(c))
		return
	}else if(typeof vnode.type === 'object'){
		// 对于组件的卸载,本质上是要卸载组件所渲染的内容,即subTree
		unmount(vnode.component.subTree)
		return
	}
	const parent = vnode.el.parentNode
	if(parent){
		parent.removeChild(vnode.el)
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值