- 当页面需要请求大量资源时,同步组件会一次性加载所有资源后才能展示,从而造成网页首屏加载时间特别长。但是我们可以利用异步组件让浏览器仅在需要时才加载对应资源,从而达到优化性能的目的。
- 要让一个组件成为异步组件可以使用ES7的顶层await技术——即不通过async函数,直接在script标签中写上await,后面跟一个返回promise对象的函数调用语句,此时整个组件就会成为异步组件。
Card.vue(异步组件顶层await)
<template>
<div>
<div class="icon">
<img src="../assets/102439408_p1_master1200.jpg" alt="我是一张图片" />
<span>{{ result.name }}</span>
</div>
<hr />
<div class="text">{{ result.text }}</div>
</div>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue'
import { axios } from '../utils/axios'
interface Data {
name: string
text: string
url: string
}
// 从ES7开始可以在顶层使用await,这样整个组件会变成异步组件
const result = await axios.get<Data>('/api/list')
console.log(result.url)
</script>
<style scoped>
.icon {
display: flex;
align-items: center;
}
.icon img {
height: 100px;
border-radius: 50%;
}
.icon span {
font-size: 18px;
letter-spacing: 2px;
margin-left: 10px;
}
.text {
font-size: 20px;
}
</style>
- 异步组件不能用传统的import……from……导入,必须通过调用defineAsyncComponent函数来定义。该函数接收一个回调函数,其返回值是import()动态导入语句(本质上也是一个promise对象),此外该组件必须写在Suspense标签内部。Suspense标签拥有两个具名插槽default和fallback。default用于加载异步组件,fallback用于异步组件加载完成之前临时展示(骨架屏)
App.vue(骨架屏)
<template>
<Suspense>
<template #default>
// 异步组件,展示需要加载大量资源的内容
<Card></Card>
</template>
<template #fallback>
// 同步组件,展示骨架屏
<Sync></Sync>
</template>
</Suspense>
</template>
<script setup lang="ts">
import { ref, reactive, defineAsyncComponent } from 'vue'
import Sync from './components/Sync.vue';
const Card = defineAsyncComponent(() => import('./components/Card.vue'))
</script>
<style scoped></style>
- defineAsyncComponent有两种写法,第一种是直接写回调函数,第二种是传入一个对象,对象中有loadingComponent、errorComponent和timeout等属性,可以基于不同状态展示不同组件。
- 代码分包:使用异步组件后在pnpm run build时就不会将所有资源打包到一个JS文件中,当用户访问网页时仅在需要时才加载对应的资源,起到性能优化的作用
完整代码:
App.vue
<template>
<Suspense>
<template #default>
<Card></Card>
</template>
<template #fallback>
<Sync></Sync>
</template>
</Suspense>
</template>
<script setup lang="ts">
import { ref, reactive, defineAsyncComponent } from 'vue'
import Sync from './components/Sync.vue';
const Card = defineAsyncComponent(() => import('./components/Card.vue'))
</script>
<style scoped></style>
Card.vue
<template>
<div>
<div class="icon">
<img src="../assets/102439408_p1_master1200.jpg" alt="我是一张图片" />
<span>{{ result.name }}</span>
</div>
<hr />
<div class="text">{{ result.text }}</div>
</div>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue'
import { axios } from '../utils/axios'
interface Data {
name: string
text: string
url: string
}
// 从ES7开始可以在顶层使用await,这样整个组件会变成异步组件
const result = await axios.get<Data>('/api/list')
console.log(result.url)
</script>
<style scoped>
.icon {
display: flex;
align-items: center;
}
.icon img {
height: 100px;
border-radius: 50%;
}
.icon span {
font-size: 18px;
letter-spacing: 2px;
margin-left: 10px;
}
.text {
font-size: 20px;
}
</style>
Sync.vue
<template>
<div>
<div class="icon">
<img src="" alt="我是一张图片" />
<div></div>
</div>
<hr />
<div class="text">
<div></div>
<div></div>
</div>
</div>
</template>
<script setup lang='ts'>
import { ref,reactive } from 'vue'
</script>
<style scoped>
.icon {
display: flex;
align-items: center;
}
.icon img {
height: 100px;
border-radius: 50%;
}
.icon div {
width: 400px;
height: 20px;
margin-left: 10px;
background-color: rgb(61, 58, 58);
}
.text {
height: 30px;
font-size: 20px;
}
.text div{
height: 20px;
margin-top: 20px;
background-color: rgb(61, 58, 58);
}
</style>
axios.ts(基于底层Ajax)
export const axios = {
get<T>(url: string): Promise<T> {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.open('GET', url)
xhr.onreadystatechange = () => {
if (xhr.readyState == 4 && xhr.status == 200) {
setTimeout(() => {
resolve(JSON.parse(xhr.responseText))
},2000)
}
}
xhr.send(null)
})
},
}
express框架(简单的后端)
import express, { Express, Router } from 'express'
import path from 'path'
const app: Express = express()
const router: Router = express.Router()
app.use('/api', router)
router.get('/list', (req, res) => {
res.json({
name: '诺航同学',
text: '负责本群服务器运维',
url: './assets/102439408_p1_master1200.jpg',
})
})
app.listen('5555', () => {
console.log('server success http://localhost:5555')
})