Logo
一梦五千年
  • 工具箱
  • AI 平台
  • 提示词
  • 生活随笔
  • 友链
获取天气...
登录
系统状态: 在线
内核版本: 5.15.0-88-GENERIC时区: UTC+8运行时间: 计算中...
© 2026 一梦五千年
性能优化

一次前端打包优化实录:8 分钟到 3 分钟,顺便让页面快了一倍

一次极简的性能优化记录

8/3/20269 分钟读完0 次浏览
一次前端打包优化实录:8 分钟到 3 分钟,顺便让页面快了一倍

组里的前端项目,每次 build 都要等 8 分钟。等的人多了,大家就默认这是"正常的",打包的时候顺手去泡杯咖啡。产物打出来 80M,也没人细究——反正内网系统,能跑就行。

直到今天我实在受不了这 8 分钟,决定坐下来看看它到底慢在哪。

结论有点出乎意料:真正拖慢构建的和真正拖慢用户的,是两个完全不同的东西,而后者根本不在打包里。

下面是完整的排查过程。

先搞清楚:8 分钟到底花在哪

项目是个 Vue 3 + Element Plus 的管理后台,用 Vite 打包。第一反应是去看构建产物体积,但那是"结果",不是"原因"。慢的是过程,得先知道构建时间花在什么阶段。

盯着构建日志看了几轮,发现卡最久的是 SCSS 编译那一段。项目全量引入了 Element Plus 的 SCSS 源码:

// src/styles/element/index.scss @use 'element-plus/theme-chalk/src/index.scss' as *; // 80+ 个组件的完整样式源码

这一行会把 Element Plus 全部组件的 SCSS 拉进来编译,几千行变量、mixin、规则,每次打包都要重新算一遍。

但真正的问题不在"编译量大",而在用什么去编译。翻了下 package.json,装的是 sass——也就是纯 JavaScript 版的 Dart Sass。而 vite.config.ts 里明明写着:

css: { preprocessorOptions: { scss: { api: 'modern-compiler' // 声明了要用新版编译 API } } }

配置摆出了要用高性能编译器的姿态,装的却是慢的那个。等于开了跑车模式,但发动机还是拖拉机的。

第一刀:换编译器,啥代码都不用改

解决办法简单到有点好笑——把 sass 换成官方的原生实现 sass-embedded:

pnpm remove sass pnpm add -D sass-embedded

sass-embedded 是 Dart Sass 编译成的原生二进制,API 和 sass 完全兼容,@use / @forward / mixin 那些语法一个都不用改。它也是 api: 'modern-compiler' 能真正生效的前提。

就这一步,构建从 8 分钟降到了 3 分钟。一行业务代码没动,SCSS 没删一句,纯粹是把编译引擎换了。

顺带踩了个坑:这项目 node_modules 是 pnpm 装的,我一开始手滑用了 npm install,直接报了个莫名其妙的错。看 lockfile 是 pnpm-lock.yaml 才反应过来。多人协作的项目,包管理器别混用。

顺手的两刀:该按需的别全量引

既然打开了引擎盖,顺手收拾两个一眼就不对的地方。

一个是 echarts。 项目里大部分图表页都是按需引入的,统一走一个 @/utils/echarts,只注册了实际用到的图表类型。但有个页面是这么写的:

// 全量,把整个 echarts 都打进来 import * as echarts from 'echarts';

改成和其他页面一致的按需版本:

import echarts from '@/utils/echarts';

主包立马瘦了 700 多 KB。改之前我特意确认了这页只用到了饼图,而 @/utils/echarts 里已经注册了饼图——不然改完图表会直接白给。

另一个是字体。 一个中文字体,四种格式全打进了包里:

AlimamaShuHeiTi-Bold.otf    1005 KB
AlimamaShuHeiTi-Bold.ttf    1.4 MB
AlimamaShuHeiTi-Bold.woff   786 KB
AlimamaShuHeiTi-Bold.woff2  663 KB

现代浏览器早就全都支持 woff2 了(压缩率还最高),留其他三个纯属浪费。删掉,只保留 woff2:

@font-face { font-family: 'AlimamaShuHeiTi'; src: url('@/assets/fonts/AlimamaShuHeiTi-Bold.woff2') format('woff2'); font-display: swap; // 字体没加载完先用系统字体顶上,别让文字空白 }

一下省了 3MB 多。

转折:80M 其实是个伪命题

处理完这些,回头看那个"80M 产物"。我下意识想继续砍体积,但砍之前先问了自己一个问题:这 80M,用户真的会下载 80M 吗?

不会。80M 是打包产物在硬盘上占的空间,里面还包含一堆构建时生成的 .gz 预压缩副本。用户打开网页,浏览器请求的是单个文件,不是整个文件夹。

那用户到底下了多大?打开线上站点,F12 → Network,点开那个最大的 CSS,看响应头:

content-type: text/css
content-length: 6538172        # 6.38MB,原样发出去了
(没有 content-encoding 这一行)

问题一下子清楚了:响应头里根本没有 content-encoding: gzip。 服务器把 6.38MB 的 CSS 原封不动、未经压缩地丢给了每一个用户。

这才是真正拖慢用户的东西,而它压根不在打包环节——在 Nginx。

真正的大招:一段 Nginx 配置

翻了下 Nginx 配置,果然,从头到尾没有一条 gzip 指令。前端辛辛苦苦打包时生成的那些 .gz 副本,服务器一个都没用上,纯占硬盘。

补上压缩配置,放进 nginx.conf 的 http {} 块里(这样底下所有站点一起生效):

http { gzip on; gzip_static on; # 有打包好的 .gz 就直接用,省 CPU;没有则实时压缩(需模块支持) gzip_vary on; gzip_comp_level 6; gzip_min_length 1024; gzip_types text/plain text/css application/javascript application/json image/svg+xml font/ttf; # ... 原有 server 配置不动 ... }

gzip_static 依赖一个可选模块,加之前先跑 nginx -V 2>&1 | grep gzip_static 确认。 没有这个模块就把那行删掉,只留 gzip on,实时压缩效果一样,只是多花点 CPU。 内网系统这点 CPU 完全无所谓。

nginx -t 测一下语法,nginx -s reload 重载。再刷新页面看响应头:

content-encoding: gzip          # 出现了
transfer-encoding: chunked

那个 2.67MB 的 JS,传到浏览器只剩 800 多 KB。6.38MB 的 CSS 同理,压到 700KB 左右。压缩率 70% 以上,一行前端代码没改。

一个我决定不做的优化

排查时其实找到了体积的头号元凶:那个全量引入的 Element Plus SCSS,编译出来的 CSS 有 6.38MB。理论上按需引入能砍到 2MB 左右。

但我最后决定不做,原因值得说一下:

  1. 风险不对等。 按需引入要手动列出用到的每个组件的样式,漏一个,对应组件在所有页面直接变裸奔——尤其是弹窗、下拉面板、消息提示这类挂在 body 上、容易被忘的组件。
  2. 它会变成一个长期的坑。 项目是一个微前端项目,已经利用 el-config-provider namespace="ep" 前缀换成了ep,以后每次有人新增一个组件,都得记得回来补一行样式,否则就是"某个弹窗莫名其妙没样式"的诡异 bug,全局换掉不划算且浪费时间。
  3. 收益已经被 gzip 吃掉了。 6.38MB 经 gzip 压缩后到浏览器只有 ~700KB,而且加载一次就进缓存。为了把这 700KB 再压一点,去承担上面那些风险,不划算。

优化不是"能做就做",是"值不值得做"。这一项,我在文档里明确标了"暂缓",省得以后有人手痒。

最后对比一下

指标优化前优化后怎么做到的
构建时间8 分钟~3 分钟换 sass-embedded
主 JS 传输量2.67MB~800KBNginx gzip
主 CSS 传输量6.38MB~700KBNginx gzip
产物瘦身—-3.7MB字体只留 woff2 + echarts 按需

几点体会

  • 先量,再改。 别一上来就删代码。慢在哪、大在哪,日志和 F12 会告诉你,猜的往往是错的。
  • 分清"硬盘体积"和"传输体积"。 产物 80M 不代表用户下 80M。真正影响用户的是单个文件经过压缩后的大小。我差点就把力气全花在削那个跟用户没关系的数字上。
  • 最狠的优化,有时候不在你最熟的那一层。 前端折腾半天的体积问题,最后是一段 Nginx 配置解决的。视野别只框在自己的一亩三分地。
  • 优化要懂得收手。 花两小时省 700KB 的 gzip 后体积,还搭上长期维护风险,不如把这两小时用在别处。

作者信息

mml
mml
@mml

这个人很懒,什么都没写。

目录

互动

暂无可提取的标题

评论区

0 条评论
评论输入
>
* 访问被拒绝:请登录后发表评论 *