Vue 项目首屏加载速度优化

Vue 项目首屏加载从 5s 到 1.5s:4 步落地优化方案,附完整代码 + 数据对比

前段时间我在做一个活动时,打包加载后发现打开页面要等半天,经过几天的优化,最终将首屏加载时间从5秒压到 1.5 秒。

这篇文章会把整个优化过程拆解成「文件分析→代码解决→验证」三步,每个方案都附完整代码前后数据对比,新手也能直接复用。

1. 打包体积分析:看「哪些文件在拖后腿」

操作步骤(Vue CLI/Vite 通用):

  • Vue CLI 项目:执行 npm run build -- --report;
  • Vite 项目:执行 npm run build -- --report(需先在 vite.config.js 中开启 build.report: true);
  • 打包完成后,dist 目录会生成 report.html,打开后能看到「打包体积占比图」。

我的项目诊断结果:

|----------|------------------------------------|-----|
| 问题类型 | 具体表现 | 占比 |
| 路由未懒加载 | 所有路由组件打包进 app.js,导致初始 JS 体积过大 | 35% |
| UI 库完整引入 | Element Plus 完整引入(600KB),但仅用 5 个组件 | 30% |
| 图片未优化 | banner 图未压缩(2.3MB),小图标未转 Base64 | 25% |
| 公共代码未拆分 | Vue、lodash 等第三方库重复打包 | 10% |

明确了这 4 个核心问题,接下来的优化就有了方向 ------ 不用贪多,解决这 4 个问题,首屏加载就能提速 60% 以上。

2. 4种方法可以优化:每个方案都附代码 + 效果

方案 1:路由懒加载

原理:

默认情况下,Vue 会把所有路由组件打包进「app.js」,导致初始加载时要下载全部组件;懒加载则是「用户访问哪个路由,才下载哪个组件的 JS」,能直接减少初始 JS 体积。

代码对比:

复制代码
// 优化前:同步加载(所有组件打包进app.js)

import Home from './views/Home.vue'

import About from './views/About.vue'

const routes = [

{ path: '/', component: Home },

{ path: '/about', component: About }

]

// 优化后:懒加载(按需下载)

const routes = [

// 动态import语法,访问时才加载

{ path: '/', component: () => import('./views/Home.vue') },

{ path: '/about', component: () => import('./views/About.vue') }

]

效果对比:

|----------|-------|--------|-----|
| 指标 | 优化前 | 优化后 | 提升 |
| 初始 JS 体积 | 1.8MB | 1.08MB | 40% |
| 首屏加载时间 | 5.0s | 3.2s | 36% |

避坑提示:

  • 不要给「首屏路由」(如 Home)做懒加载,否则会增加首屏加载的请求数;
方案 2:UI 库按需引入(减少 200KB,自动生效)

原理:

完整引入 UI 库(如 Element Plus)会包含大量未使用的组件和样式(比如你只用了按钮,却加载了日历、表格等组件),按需引入能只打包「实际使用的组件」。

以 Element Plus 为例(Vite 项目):

  1. 安装按需引入插件:

    npm install unplugin-vue-components unplugin-auto-import -D

  2. 在 vite.config.js 中配置:

    import { defineConfig } from 'vite'

    import Vue from '@vitejs/plugin-vue'

    // 1. 引入按需引入插件

    import AutoImport from 'unplugin-auto-import/vite'

    import Components from 'unplugin-vue-components/vite'

    import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'

    export default defineConfig({

    plugins: [

    Vue(),

    // 2. 配置插件

    AutoImport({

    resolvers: [ElementPlusResolver()], // 自动引入Element Plus的API(如ElMessage)

    }),

    Components({

    resolvers: [ElementPlusResolver()], // 自动引入Element Plus的组件(如ElButton)

    }),

    ],

    })

  3. 在组件中直接使用,无需手动 import:

    <template>

    <el-button type="primary" @click="showMessage">提交</el-button>

    </template> <script setup>

    // 无需引入ElMessage,插件会自动引入

    const showMessage = () => {

    ElMessage.success('提交成功')

    }

    </script>

效果对比:

|-----------------|-------|-------|-------|
| 指标 | 优化前 | 优化后 | 提升 |
| Element Plus 体积 | 600KB | 400KB | 33% |
| 首屏加载时间 | 3.2s | 2.8s | 12.5% |

避坑提示:

  • 如果使用 Vue CLI,配置方式略有不同(需在 vue.config.js 中配置);
  • 部分 UI 库的「样式」需要单独按需引入(如 Ant Design Vue),需查看官方文档。
方案 3:图片优化(首屏资源减少 50%,用户感知最明显)

图片是首屏加载的「重灾区」------ 我的项目中,图片占了首屏资源体积的 50%,优化后直接减少了 1.6MB。

推荐 3 个必做的图片优化手段,覆盖 90% 的场景:

1. 小图转 Base64(减少 HTTP 请求)

  • 原理:小于 10KB 的图片(如图标、小按钮)转为 Base64 后,会嵌入 HTML/JS 中,无需额外发请求;
  • 实现(Vite 自动处理):

    // vite.config.js
    export default defineConfig({
    build: {
    assetsInlineLimit: 10240, // 10KB以下图片自动转Base64(默认是4KB)
    },
    })

2. 大图用 WebP 格式(体积减少 50%)

  • 原理:WebP 格式比 JPG/PNG 体积小 30%~50%,且支持透明和动图;
  • 实现:
  1. 用工具将图片转为 WebP(推荐 TinyPNG、Squoosh);

  2. 在 Vue 中使用,同时兼容不支持 WebP 的浏览器:

    <template> <picture> <source srcset="/banner.webp" type="image/webp"> </picture> </template>

3. 首屏外图片懒加载(延迟加载,减少首屏压力)

  • 原理:首屏外的图片(如下方的商品列表),等用户滚动到可视区域再加载;
  • 实现(用 vue-lazyload):
  1. 安装插件:

    npm install vue-lazyload

  2. 在 main.js 中配置:

    import { createApp } from 'vue'

    import App from './App.vue'

    import VueLazyload from 'vue-lazyload'

    const app = createApp(App)

    // 配置懒加载:加载中显示占位图

    app.use(VueLazyload, {

    loading: '/loading.gif', // 加载中占位图(建议小于1KB)

    error: '/error.png' // 加载失败占位图

    })

    app.mount('#app')

  3. 在组件中使用:

    <template> 商品图片 </template>

效果对比:

|--------|-------|-------|-------|
| 指标 | 优化前 | 优化后 | 提升 |
| 首屏图片体积 | 2.3MB | 1.1MB | 52% |
| 首屏加载时间 | 2.8s | 2.0s | 28.6% |

避坑提示:

  • 首屏图片不要懒加载,否则会导致首屏空白;
  • WebP 格式在 IE 浏览器不支持,需用 <picture> 标签做兼容。
方案 4:打包优化(最后一步,再挤 10% 体积)

前面 3 个方案解决了核心问题,最后通过打包优化,进一步压缩体积。

1. 拆分公共代码(避免重复打包)

  • 原理:Vue、lodash 等第三方库,会被多个组件引用,拆分后只打包一次,后续组件复用;
  • 实现(Vite 配置):

    // vite.config.js

    export default defineConfig({

    build: {

    rollupOptions: {

    output: {

    // 手动拆分公共代码

    manualChunks: {

    vue: ['vue'], // Vue相关代码拆成一个chunk

    lodash: ['lodash'], // lodash拆成一个chunk

    element: ['element-plus'] // Element Plus拆成一个chunk

    }

    }

    }

    }

    })

2. JS/CSS 压缩(默认开启,无需额外配置)

  • Vite 默认使用 Terser 压缩 JS,CSSNano 压缩 CSS,无需手动配置;
  • 若需加强压缩(如去掉 console.log),可在 vite.config.js 中配置:

    export default defineConfig({

    build: {

    terserOptions: {

    compress: {

    drop_console: true, // 打包时去掉所有console.log

    drop_debugger: true // 去掉debugger

    }

    }

    }

    })

效果对比:

|--------|-------|-------|-----|
| 指标 | 优化前 | 优化后 | 提升 |
| 总打包体积 | 2.5MB | 1.0MB | 60% |
| 首屏加载时间 | 2.0s | 1.5s | 25% |

相关推荐
MediaTea2 小时前
Python 第三方库:lxml(高性能 XML/HTML 解析与处理)
xml·开发语言·前端·python·html
西陵2 小时前
Nx带来极致的前端开发体验——使用MF进行增量构建
前端·javascript·架构
Nicholas682 小时前
flutter滚动视图之ProxyWidget、ProxyElement、NotifiableElementMixin源码解析(九)
前端
JackieDYH2 小时前
vue3中reactive和ref如何使用和区别
前端·javascript·vue.js
伍哥的传说2 小时前
解密 Vue 3 shallowRef:浅层响应式 vs 深度响应式的性能对决
javascript·vue.js·ecmascript·vue3.js·大数据处理·响应式系统·shallowref
ZZHow10243 小时前
React前端开发_Day4
前端·笔记·react.js·前端框架·web
前端开发爱好者3 小时前
弃用 html2canvas!快 93 倍的截图神器
前端·javascript·vue.js
ss2734 小时前
手写MyBatis第39弹:深入MyBatis BatchExecutor实现原理与最佳实践
前端·javascript·html
leon_teacher4 小时前
HarmonyOS权限管理应用
android·服务器·前端·javascript·华为·harmonyos