根本原因是 gulp-uglify v3 及更早版本依赖仅支持 ES5 的 uglify-js@2,遇 const、箭头函数等 ES6+ 语法会抛 SyntaxError;解法为升级至 gulp-terser 或先用 gulp-babel 编译。
根本原因通常是源码含 ES6+ 语法,而 gulp-uglify(v3 及更早版本)底层依赖 uglify-js@2,只支持到 ES5。遇到 const、箭头函数、解构等语法会直接抛出 SyntaxError: Unexpected token。
npm list gulp-uglify,若显示 [email protected],基本就是这个原因mangle: false, compress: false 可避免语法解析失败,但失去压缩意义[email protected]+ 并配合 uglify-es(已废弃)或直接换用 gulp-terser(推荐)——它基于现代 Terser 引擎,原生支持 ES2022+gulp-babel 把 JS 编译成 ES5,再 pipe 给 gulp-uglify
gulp-useref 负责提取并合并 HTML 中的 <script src="..."> 和 <script>...</script>,但它默认**不处理内联脚本(inline script)的压缩**,直接丢给 gulp-uglify 会因缺少文件路径上下文而失败。
gulp-useref 的 build block 标注正确,例如:<!-- build:js js/bundle.js --><script>console.log(1)</script><!-- endbuild -->
useref() 后,流中会生成一个虚拟文件(.contents 是字符串),需用 gulp-if 或条件判断区分 JS 文件类型gulp-htmlmin 的 minifyJS: true 选项统一处理,而非塞进 gulp-uglify 流程gulp-uglify 处理 inline,需配合 vinyl 手动构造文件对象,并设置 .path 属性,否则 gulp-uglify 无法生成有意义的 sourcemap 或错误定位mangle 开启后,默认会重命名所有作用域内变量和函数,但第三方库的 API 名、全局钩子、webpack 注入的 __webpack_require__ 等必须保留。靠白名单控制最稳妥。
mangle: { reserved: ['require', 'exports', 'module', 'MyLib', 'init'] },数组中为字符串,大小写敏感reserved 只影响顶层作用域声明,嵌套函数名仍可能被混淆,如需完全保留整个模块结构,设 mangle: false
compress: { drop_console: true },它不会删掉 console.table 或 console.group,只删 console.log/debug 类调用;要删全部,得配 drop_console: /./(正则匹配任意字符)output: { comments: /@license/i } 可保留 LICENSE 注释,其他注释全删——别用 false,否则连关键版权信息也丢了用 .pipe() 链式调用时,一旦 gulp-uglify 报错,Node 的异步错误堆栈常丢失原始文件路径,只显示 gulpfile.js:12:3 这类无意义位置。这是流错误未被正确冒泡导致的。
立即学习“前端免费学习笔记(深入)”;
readable-stream 的 pipeline(Node ≥ 10.0.0 原生支持)或 pump 模块,它们会在任一环节出错时立即终止流,并把原始错误(含 fileName、lineNumber)透传出来const { pipeline } = require('stream');,然后 return pipeline(src, uglify(opts), dest, cb);
pipe 链里加空的 .on('error', handler),这会吞掉原始错误,让调试变成盲猜gulp-sourcemaps,确保 .pipe(sourcemaps.init()) 在 uglify() 之前,且 sourcemaps.write() 在之后——顺序反了 sourcemap 就失效,报错定位就彻底失灵实际项目里最易被忽略的,是 gulp-uglify 对 source map 的路径处理:它默认把 sources 字段写成相对路径,一旦你把压缩后文件挪到 CDN 或子目录,浏览器 devtools 就找不到原始文件。必须显式配 sourceMap: { includeSources: true } 或手动修正 sources 值。