文章目录
什么是懒加载
懒加载,也称为延迟加载,是一种将资源(如图片、组件、代码等)推迟到需要的时候再加载的策略。在 Vue 中,懒加载通常涉及到组件和图片的加载。
优点:
减少初始加载时间
:只加载当前可见内容,减少首屏资源量按需加载避免浪费
:减少了带宽浪费缓解服务器压力
组件懒加载
1.异步组件函数实现(推荐)
defineAsyncComponent
typescript
import { h, defineAsyncComponent } from 'vue';
//简单
const async1 = defineAsyncComponent(() => import('./components/MyComponent.vue'))
const async2 = defineAsyncComponent({
loader: //必须是一个promise类型(动态import()就返回promise对象)
,
loadingComponent: {//异步执行未完成时展示组件(模拟一个组件,组件调用时会调用render函数)
//h虚拟dom函数用于创建虚拟dom节点(h(tag, props, children))
render() {
return h('div', '加载中')
}
},
errorComponent: {//超出timeout后展示
render() {
return h('div', '加载失败')
}
},
delay: 200,//延迟时间结束后展示加载中
timeout: 3000
})
//使用(对应vue文件)
<async1></async1>
<async2></async2>
此处没有对async进行渲染控制,也就是直接触发渲染,进行组件获取,
仅实现了代码分割
只需要给组件标签加上v-if渲染条件,即实现了按需加载(当组件渲染时才会执行对应的函数)实现真正的懒加载
内置component组件自己实现
组件对象不能用响应式对象来接收
一般使用shallowRef()的实例对象避免对组件对象的响应式化
原因(组件对象包含大量的vue的静态属性和方法等执行中从不改变 故不应使用响应式)
如下应该初始化
const loadedComponent=shallowRef()
而对 shallowRef对象接收后对.value 进行响应式追踪
也是为何后续赋值
loadedComponent.value = module.default
的原因module.default是因为
<script setup>
块会被编译为export default(modulde是导入的组件)来自vue2 的设计
html
<component
:is="loadedComponent"
v-bind="$attrs"
/>
:is
渲染对应属性值的组件,若该值不是组件不渲染,也就是可以修改值实现动态组件
v-bind"$attrs"
将父组件的属性 不属于本组件props部分的传给动态组件
在下例中
app.vue->中间层->被导入的组件
传了name1和2 (name2被拦截) 默认值是3
故展示1和3
举例
直接导入组件
typescript
//studyC.vue(可传入一个组件)
<template>
<div>
<component
:is="will"
v-bind="$attrs"></component>
</div>
</template>
<script setup lang="ts">
import {ref,onMounted,shallowRef} from 'vue'
interface Props {
//html不分大小写用-模拟小驼峰命名
willLoader:Promise<any>,//import()返回的是promise类型
//会对应will-loader属性
name2:number//去掉这里定义 展示就会是1,2
}
const props=defineProps<Props>()
//响应式will用于储存提取出的组件
const will=shallowRef()
//组件挂载时直接执行 相当于立即执行
onMounted(async () => {
try {
//promise异步 解析后的default属性就是组件
const module = await props.willLoader
will.value = module.default
} catch (error) {
console.error('组件加载失败:', error)
}
})
</script>
//被导入的组件
<template>
<div>{{ name1 }}</div>
<div>{{ name2 }}</div>
</template>
<script setup lang="ts">
interface Props{
name1:number
name2?:number
}
const props=withDefaults(defineProps<Props>(),{
name2:3
})
</script>
<style scoped>
</style>
//使用app.vue
<studyC :name1="1" :name2="2" :will-loader="import('./components/willloader.vue')"></studyC>
//import()先编译 也就是可以使用相对位置,
//此处直接import并不是懒加载,在第一次就会直接加载
//展示1,3
//2被中间层拦截了

利用此实现组件懒加载
- 改写导入方式
typescript
<studyC :name1="1" :name2="2" :will-loader="()=>import('./components/willloader.vue')">
- 需要时调用此函数(自行加入条件)
如下改成利用按钮控制触发条件的懒加载
typescript
<template>
<div class="lazy-component-wrapper">
<!-- 按钮触发加载 -->
<button
@click="loadComponent"
:disabled="loading || loaded"
>
{{ getButtonText }}
</button>
<!-- 加载状态 -->
<div v-if="loading">
组件加载中...
</div>
<!-- 渲染加载的组件 -->
<component
:is="loadedComponent"
v-if="loadedComponent && !loading"
v-bind="$attrs"
/>
</div>
</template>
<script setup lang="ts">
import { ref, computed,shallowRef } from 'vue'
interface Props {
// 接收组件导入函数
componentLoader: () => Promise<any>
//传入方式:component-loader="() => import('./components/MyComponent.vue')
buttonText?: string
loadingText?: string
}
const props = withDefaults(defineProps<Props>(), {
buttonText: '加载组件',
loadingText: '加载中...'
})
// 响应式数据
const loading = ref(false)
const loaded = ref(false)
const loadedComponent = shallowRef()
const getButtonText = computed(() => {
if (loading.value) return props.loadingText
if (loaded.value) return '组件已加载'
return props.buttonText
})
// 加载组件
const loadComponent = async () => {
if (loading.value || loaded.value) return
loading.value = true
try {
// 真正开始导入组件
const module = await props.componentLoader()
loadedComponent.value = module.default
loaded.value = true
} catch (error) {
console.error('组件加载失败:', error)
} finally {
loading.value = false
}
}
defineExpose({
loadedComponent,
loadComponent,
getButtonText
})
</script>
//使用
<buttonlazy :name1="1" :name2="2" :component-loader="() => import('./components/willloader.vue')"></buttonlazy>
//此处没有拦截name1和name2 故展示 1,2

图片懒加载
方案一:
利用了src被img识别时才导入的效果实现懒加载
一般使用自定义指令实现 这里用组件模拟
typescript
//myimg
<template>
<div class="lazy-img">
<button
v-if="!loaded"
@click="loadImage"
:disabled="loading"
>
{{ loading ? '加载中...' : '点击加载图片' }}
</button>
<img
v-else
:src="props.src"
:alt="props.alt"
/>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
// 定义 props
interface Props {
src: string
alt?: string
}
const props = withDefaults(defineProps<Props>(), {
src: '',
alt: '图片'
})
// 响应式数据
const loaded = ref(false)
const loading = ref(false)
// 加载图片
const loadImage = async () => {
if (loading.value || loaded.value) return
loading.value = true
// 模拟网络延迟
await new Promise(resolve => setTimeout(resolve, 500))
loaded.value = true
loading.value = false
}
</script>
//使用
<myimg src="/src/img/120baf1265ee54cb9cb1fa17c2dcae33.png"></myimg>
//此处src是字符串 不会自动对应
更多使用Vanilla Lazyload或lozad库实现
问题1
这是懒加载吗
typescript
//使用
<async1></async1>
//定义
const async1 = defineAsyncComponent(() => import('./components/MyComponent.vue'))
问题2
为何组件对象要使用shallowRef()
实例对象接收而不使用ref()
实例对象