Vue2里的async component到底怎么用?能解决哪些实际问题?
做Vue项目的时候,有没有遇到过“组件太大导致页面加载慢”“首屏一堆组件同时加载卡成PPT”这些情况?这时候Vue2的async component(异步组件)就能派上大用场,但很多同学刚接触时,总会疑惑它到底怎么用、能解决啥实际问题,今天就从概念、用法到实战场景,把async component聊透,帮你在项目里合理用上这项性能优化技能~
先搞懂:Vue2的async component是什么?
简单说,async component是把组件的加载过程改成异步的技术,举个对比例子:普通同步组件在项目打包时,会被合并到主JS文件里,页面初始化时就会加载所有同步组件的代码;而异步组件会被单独拆分成一个“代码块(chunk)”,只有当这个组件被实际需要的时候(比如路由切换到对应页面、用户点击触发组件渲染),才会去加载它的代码。
打个生活化的比方:同步组件像“搬家时把所有东西一股脑塞进货车”,车重载启动慢;异步组件像“用快递分批发货”,先送急需的,不急的等要用了再送,货车(首屏加载)就轻快多了。
为什么要搞异步组件?解决的核心问题是啥?
异步组件不是“为了用新特性而用”,而是解决实际开发中性能和资源浪费的痛点,这些场景你一定碰到过:
- 首屏性能优化:首页只需要头部、轮播图这些组件,但如果把弹窗、二级路由页面这些“非首屏组件”也同步加载,会让首屏JS文件体积暴增,加载时间变长,用户看到白屏的时间也跟着变久,用异步组件把这些非首屏组件拆分出去,首屏加载速度能明显提升。
- 组件按需加载:有些组件用户可能永远不会触发(导出Excel”按钮对应的弹窗组件),如果同步加载,不管用户用不用,代码都要加载,这就是资源浪费,异步组件让这些“低频组件”用到再加载,节省用户流量和浏览器资源。
- 代码分割与打包优化:配合Webpack这类打包工具,每个异步组件会生成独立的JS文件(chunk),当多个异步组件同时需要加载时,浏览器能并行请求这些小文件,比加载一个超大文件更快完成。
Vue2里怎么实现async component?基础用法先掌握
Vue2里实现异步组件的核心是“工厂函数”——用一个函数返回组件的Promise(Promise resolve后得到组件定义),下面分场景讲写法:
最基础的异步组件(配合动态import)
如果项目用了Webpack(或Vue CLI、Vite),可以用ES6的import()
语法动态导入组件,写法分全局注册和局部注册:
全局注册(在main.js里):
Vue.component('AsyncButton', function(resolve) {
// import()返回Promise,resolve传入组件定义
import('./components/Button.vue').then(module => {
resolve(module.default)
})
})
局部注册(在某个页面组件的components里):
export default {
name: 'HomePage',
components: {
// 箭头函数直接返回import()的Promise
AsyncModal: () => import('./components/Modal.vue')
}
}
这里的关键是:工厂函数要返回一个Promise,Vue会等这个Promise resolve后,拿到真正的组件去渲染。
处理“加载中、加载失败”的状态
如果异步组件加载需要时间(比如网络慢),用户盯着空白区域会很懵,这时候可以给异步组件配置加载中组件和加载失败组件,让体验更友好,写法是让工厂函数返回一个对象,包含这些配置:
// 先定义加载中、失败的组件(普通Vue组件)
import Loading from './components/Loading.vue'
import Error from './components/Error.vue'
const AsyncChart = () => ({
// 需要加载的目标组件(Promise)
component: import('./components/Chart.vue'),
// 加载过程中显示的组件
loading: Loading,
// 加载失败(比如网络错误)显示的组件
error: Error,
// 延迟多少毫秒后显示loading(防止组件加载太快导致闪烁)
delay: 200,
// 加载超时时间(超过这个时间算失败)
timeout: 3000
})
// 然后在components里使用AsyncChart
export default {
components: { AsyncChart }
}
这样用户体验就完整了:组件加载时显示Loading,加载成功显示Chart,加载超时/失败显示Error。
实际项目里,async component能应对哪些场景?举几个例子
光讲语法不够,得看实际怎么用,这几个高频场景,你一定能用上:
路由级异步组件(配合Vue Router)
很多项目的页面组件(比如订单页、个人中心页)体积很大,把这些页面做成异步组件,访问对应路由时才加载,能大幅减少首屏加载的JS体积,写法像这样:
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
// 每个路由组件都用异步方式定义
const OrderPage = () => import('./views/Order.vue')
const ProfilePage = () => import('./views/Profile.vue')
export default new Router({
routes: [
{ path: '/order', component: OrderPage },
{ path: '/profile', component: ProfilePage }
]
})
这样用户打开首页时,Order和Profile的代码不会加载,只有点击对应路由时才会请求,首屏加载速度直接起飞~
弹窗类组件(用户触发才加载)
确认弹窗”“图片预览弹窗”这类组件,用户不点击按钮就不会出现,把它们做成异步组件,页面初始化时不加载弹窗代码,点击时再加载,能省不少资源,举个局部注册的例子:
<template>
<button @click="showModal = true">打开弹窗</button>
<AsyncModal v-if="showModal" @close="showModal = false" />
</template>
<script>
export default {
data() {
return { showModal: false }
},
components: {
// 点击按钮时才会触发import,加载Modal组件
AsyncModal: () => import('./components/Modal.vue')
}
}
</script>
复杂第三方组件(体积大,按需加载)
像富文本编辑器(比如Quill)、图表库(比如ECharts)这类第三方组件,打包后体积经常超过100KB,把它们封装成异步组件,用户需要编辑/查看图表时再加载,能避免首屏被大体积组件拖慢,比如封装ECharts组件:
const AsyncEChart = () => ({
component: import('./components/EChart.vue'),
loading: Loading,
error: Error,
delay: 200,
timeout: 5000
})
// 在页面中:用户点击“显示图表”按钮后,渲染AsyncEChart
进阶:async component的优化和注意事项
会用基础功能还不够,这些进阶技巧能让异步组件更高效:
结合Webpack的Prefetch/Preload
如果某个异步组件用户大概率会用到(比如路由切换前的下一个页面),可以让浏览器空闲时预加载这个组件,提升后续加载速度,写法是在import()里加注释:
// webpackPrefetch: true 表示让浏览器空闲时预加载
const NextPage = () => import(/* webpackPrefetch: true */ './views/NextPage.vue')
// webpackPreload: true 表示优先加载(适合高优先级组件)
const CriticalComp = () => import(/ webpackPreload: true / './components/Critical.vue')
注意:Prefetch和Preload要根据组件优先级用,别滥用,否则会占用过多带宽。
缓存异步组件,避免重复加载
默认情况下,每次触发异步组件加载都会重新请求JS文件,如果组件会被多次使用(比如弹窗被多次打开),可以缓存已加载的组件,减少重复请求,自己实现缓存的思路:
const componentCache = {}
const AsyncCachedComp = () => {
// 如果缓存里有,直接返回
if (componentCache['MyComp']) {
return componentCache['MyComp']
}
// 否则发起请求,并存入缓存
const promise = import('./components/MyComp.vue').then(module => {
componentCache['MyComp'] = module.default
return module.default
})
return promise
}
错误处理要友好
网络不好时,异步组件可能加载失败,这时候除了用error组件显示提示,还可以给用户重试的机会,比如在Error组件里加个“重试”按钮,点击时重新触发组件加载(需要结合Vue的响应式重新生成Promise)。
别滥用!小组件适合同步加载
异步组件虽好,但也有额外开销(比如发起HTTP请求的时间),如果组件体积很小(比如几KB),异步加载的“请求开销”可能比“同步加载的体积开销”更大,这时候同步加载反而更高效,所以要根据组件体积、使用频率来判断是否用异步。
对比Vue3的异步组件,Vue2的有啥不一样?
如果以后要升级到Vue3,得知道两者的区别:Vue3里异步组件需要用defineAsyncComponent
显式包裹,写法更规范:
import { defineAsyncComponent } from 'vue'
const AsyncComp = defineAsyncComponent(() => import('./MyComp.vue'))
Vue3的defineAsyncComponent
把“加载中、错误、延迟、超时”这些配置都整合到选项里,结构更清晰,但Vue2的异步组件写法在现有项目中依然稳定,特别是维护 legacy 项目时,掌握Vue2的用法还是很有必要的。
Vue2的async component核心是“按需加载”,从首屏优化到资源管理,都是围绕“让页面更快、更省资源”,实际项目里,结合路由、弹窗、第三方组件这些场景,合理用异步组件能明显提升性能,但记住别为了用而用,小组件同步、大组件/低频组件异步,再配合Webpack的代码分割和预加载,才能把异步组件的优势拉满~要是你之前对async component懵懵懂懂,现在可以动手在项目里试试路由级异步加载,感受下首屏速度的变化~
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。