你刷网页时有没有这种感觉:图片都压缩了,CDN也上了,可页面还是“卡”了一下才出来?
别急着怀疑服务器——很可能,是浏览器在那儿反复敲门,一次只问一句“文件呢?”,来回折腾几十次。

为什么一个请求比十个请求快那么多?

HTTP/2确实支持多路复用,但浏览器对连接数、队列深度、头部解析都有实际限制。每个请求都要走一遍TCP握手、TLS协商、HTTP头解析、响应接收……这些加起来,不是“理论并行”,而是真实排队。

我优化过一个技术博客,首页原来加载47个CSS和JS文件。合并成3个后,首屏可交互时间缩短了不少。不是因为单个请求变快了,而是浏览器少跑了四十多趟——用户感受到的,就是“点开就出内容”,而不是“转圈再出字”。

代码合并的3个核心原则

别把所有代码塞进一个文件

首页加载整个后台管理系统的JS?没必要。合并不是为了“少几个文件名”,而是让浏览器只下载它当下需要的部分。

比如电商站:导航栏、购物车图标、登录态这些通用逻辑,可以打包成common.js;商品详情页的放大镜、库存倒计时、评价折叠组件,单独打包成product.js。用户从首页跳到详情页,只新增下载后者,前面的还能继续用缓存。

静态资源和动态资源分开处理

CSS、JS、字体、图标这些不常变的,适合合并。但API接口、用户头像、实时价格浮层?千万别硬塞进去。
我见过一个项目,把所有CSS合并成一个,结果某个轮播图样式写错了,整站按钮全歪了——因为所有页面都引用了同一个文件。合并前先确认:这个文件本身是稳定的,改了不会牵一发而动全身。

合并后必须更新文件名或版本标识

否则用户浏览器还在用上周缓存的app.js,你上线的新功能永远不生效。
推荐用文件哈希命名,比如app.a1b2c3.js。Webpack、Vite这些工具默认就支持。只要代码有改动,文件名自动变,浏览器自然重新拉取——不用清缓存,也不用求用户手动刷新。

代码合并后,请求真的减少了吗?

有个企业官网首页,原来要发23个请求:5个CSS、8个JS、10张小图。我们做了三件事:

  • 所有CSS合并成1个;
  • JS按优先级拆成2个(首屏必需的 + 滚动后才用的);
  • 小图标用CSS Sprite拼成1张图,通过background-position切换显示。

最终请求数压到6个。不只是数字变小了——浏览器主线程不用频繁中断渲染去处理新请求,页面绘制更连贯,用户说“好像没等就出来了”。

合并代码时,这3个坑最容易踩

坑一:合并完文件太大,首屏卡住

把全部JS塞进一个200KB的文件里,用户得等它下完才能点按钮。这不是提速,是堵路。
解决方法很简单:用动态导入。比如Vue里写import('./modules/search.js'),Webpack会自动把它抽成独立chunk;React里用lazy(() => import('./Chart'))也一样。首屏只载核心,其余按需加载。

坑二:缓存策略没跟上,每次更新都重下整包

jQuery、Lodash、Vue这些库半年都不怎么变,但你的业务代码天天改。如果混在一起打包,用户每次更新都得重新下载几百KB。
正确做法:把第三方库单独打一个vendor.js,设置一年缓存;业务代码打成app.js,用哈希命名+短缓存(比如1小时)。这样大部分时候只更新几KB,又省流量又快。

坑三:合并顺序乱了,页面直接白屏

CSS必须在HTML解析前加载完,否则文字先出来、样式后套,闪一下才正常;JS如果放在<head>里又没加defer,会直接阻塞HTML解析。
合并后顺序不能丢:CSS保持<head>内引入,JS尽量放</body>前,或者明确加上defer。曾经有团队把所有JS合并后一股脑扔进<head>,结果首页渲染延迟了整整3秒。

合并代码的工具与实战技巧

Webpack的SplitChunks插件

这是目前最稳妥的方案。它能自动识别node_modules里的依赖,拎出来打成vendor.js;再按路由或模块动态分割业务代码。配置不复杂,加几行就行:

optimization: {
  splitChunks: {
    chunks: 'all',
    cacheGroups: {
      vendor: {
        test: /[\\/]node_modules[\\/]/,
        name: 'vendor',
        chunks: 'all',
      },
    },
  },
}

改完保存,重新构建,你会发现dist目录里多出了vendor.js和按需加载的xxx.chunk.js

Gulp的gulp-concat

适合没用Webpack的小项目,比如纯静态页或老版WordPress主题。装好插件后,一行命令就能合并CSS:

gulp.src('./src/css/*.css')
  .pipe(concat('main.css'))
  .pipe(gulp.dest('./dist/css'));

注意:手动合并JS时,一定要检查变量名是否冲突(比如两个文件都定义了const utils = {...}),建议先在本地开个HTML测试下再上线。

手动合并的2个场景

  • WordPress主题里,style.css里一堆@import,你可以把它们对应的内容复制粘贴进一个文件,删掉@import,保留原有顺序;
  • 纯HTML站点,多个<script src="xxx.js">,把内容依次粘进一个all.js,上传覆盖即可。
    操作前记得备份原文件,改完用浏览器开发者工具看下控制台有没有报错。

结尾:今天就能做的1个操作步骤

打开你的网站,按F12调出开发者工具 → 切到“网络”(Network)标签 → 刷新页面 → 看左上角的请求数。
如果超过15个,找其中体积最大、类型相同的CSS或JS文件(比如header.cssutils.js),把同目录下其他同类文件内容复制进来,保存为main.cssbundle.js,上传覆盖原文件。
再刷新一次,对比请求数和加载时间——你会立刻看到变化。现在就去试。