【vue设计与实现】异步组件与函数式组件 3 - 延迟与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)
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue 可以通过使用组件的 mounted 和 destroyed 生命周期函数实现异步加载 loading 动画,并与其他组件联动。具体实现步骤如下: 1. 创建 loading 组件,可以使用第三方库如 Spin.js 或者自定义组件。 2. 在需要异步加载的组件,使用 v-if 或者 v-show 指令来控制 loading 组件的显示和隐藏。 3. 在异步加载数据的方法,先通过 this.$emit() 发送一个事件,在 loading 组件监听该事件并显示 loading 动画。 4. 异步加载数据完成后,再通过 this.$emit() 发送一个事件,通知 loading 组件隐藏 loading 动画。 代码示例: ```html <template> <div> <loading v-if="loading"></loading> <div v-if="!loading"> <!-- 异步加载数据 --> </div> </div> </template> <script> import Loading from '@/components/Loading.vue' export default { components: { Loading }, data() { return { loading: true } }, mounted() { this.loadData() }, methods: { loadData() { this.$emit('loading', true) // 异步加载数据 this.$emit('loading', false) } } } </script> ``` 在 loading 组件监听事件并显示 loading 动画: ```html <template> <div v-show="show"> <!-- 显示 loading 动画 --> </div> </template> <script> export default { data() { return { show: false } }, mounted() { this.$parent.$on('loading', (value) => { this.show = value }) }, destroyed() { this.$parent.$off('loading') } } </script> ``` 这样就可以实现异步加载 loading 动画,并且通过事件可以与其他组件进行联动,实现更加灵活的交互效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值