在写Purer主题时用的 Gulp,在 minify JavaScript 的时候遇到了问题。花了挺长时间折腾才找到了解决方法。马后炮的说其实并不难,但是因为 Babel,Gulp 等的版本割裂。网上搜到的方法要么已经过时了或者不全浪费了不少时间。下面用一个实例分步骤地记录一下解决方案。
本文写于 2020 年 3 月 16 日
1 2 3 4 5 6
| ;(async function() { const $main = document.getElementById('main'); const resp = await fetch('https://v1.jinrishici.com/all.json'); const data = await resp.json(); $main.textContent = data.content; })()
|
实例的代码很简单。但是用到了 ES2017 的 async 语法。使用的依赖版本可以在package.json中看到就不赘述了。
我们很轻松的就可以写出类似这样的 gulpfile
1 2 3 4 5 6 7 8 9 10 11 12 13
| const gulp = require('gulp'); const rename = require('gulp-rename'); const uglify = require('gulp-uglify'); const babel = require('gulp-babel');
gulp.task('js', () => { return gulp.src('main.js') .pipe(uglify()) .pipe(rename({suffix: '.min'})) .pipe(gulp.dest('.')); })
gulp.task('default', gulp.parallel('js'));
|
一跑就会发现 UglifyJS 报错。
1 2
| [13:15:26] GulpUglifyError: unable to minify JavaScript Caused by: SyntaxError: Unexpected token: keyword «function», expected: punc «)»
|
gulp-uglify
使用的UglifyJS只支持 ES5。
有两个方法解决
- 换用gulp-uglify-es它使用terser支持 ES6 + 语法压缩。
- 先用 Babel 降级
如果不用考虑兼容性问题,使用第一种方法就不需要往下看了。如果我早点知道的话就不会花时间去折腾了
我当时很自然的想到用 Babel 降级。
引入 Babel 之后的 gulpfile 长这样。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| const gulp = require('gulp'); const rename = require('gulp-rename'); const uglify = require('gulp-uglify'); const babel = require('gulp-babel');
gulp.task('js', () => { return gulp.src('main.js') .pipe(babel({ presets: ['@babel/preset-env'], })) .pipe(uglify()) .pipe(rename({suffix: '.min'})) .pipe(gulp.dest('.')); })
gulp.task('default', gulp.parallel('js'));
|
gulp build 没有报错。但是假如你也和例子中一样使用了async
的话,运行的时候浏览器会报错。
1
| ReferenceError: regeneratorRuntime is not defined
|
网上找到的信息多半是安装babel-polyfill
, 也有说要安装transform-runtime
等等方法配置 babel。但是babel-polyfill
已经Deprecated了,为了跟得上时代我们还是得跟官方文档,在babel-preset-env 的官方文档就能找了正确的配置方法。我们要引入core-js
。
安装好 core-js
1
| npm install core-js@3 --save
|
并且更改 gulpfile 中的 babel options
1 2 3 4 5
| { presets: [ ['@babel/preset-env', { useBuiltIns: 'usage', corejs: 3 }] ], }
|
一通操作下来,浏览器仍然会在运行时报错
1
| ReferenceError: require is not defined
|
说到 require 自然会想到Browserify。于是依照文档借助 Babelify 我们很自然的写出类似这样的 gulpfile
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| const gulp = require('gulp'); const rename = require('gulp-rename'); const uglify = require('gulp-uglify'); const babel = require('gulp-babel'); const browserify = require('browserify');
gulp.task('js', () => { return browserify("main.js") .transform("babelify", { presets: [ ['@babel/preset-env', { useBuiltIns: 'usage', corejs: 3 }] ], }) .bundle() .pipe(uglify()) .pipe(rename({suffix: '.min'})) .pipe(gulp.dest('.')); })
gulp.task('default', gulp.parallel('js'));
|
build 一下遇到了这样奇怪的错误,
1
| TypeError: file.isNull is not a function
|
问题出在 Browserify 的流和 Gulp 的流不兼容。Gulp 的流使用的是Vinyl, 而 browserify 使用的 node fs 的流。我们需要额外做一些转换。
安装相关的包。
1 2
| npm i -D vinyl-source-stream npm i -D vinyl-buffer
|
最终 gulpfile
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| const gulp = require('gulp'); const rename = require('gulp-rename'); const uglify = require('gulp-uglify'); const babel = require('gulp-babel'); const browserify = require('browserify'); const source = require('vinyl-source-stream'); const buffer = require('vinyl-buffer');
gulp.task('js', () => { return browserify("main.js") .transform("babelify", { presets: [ ['@babel/preset-env', { useBuiltIns: 'usage', corejs: 3 }] ], }) .bundle() .pipe(source('main.js')) .pipe(buffer()) .pipe(uglify()) .pipe(rename({suffix: '.min'})) .pipe(gulp.dest('.')); })
gulp.task('default', gulp.parallel('js'));
|
总结
至此这么一小段 js 终于被编译成可以运行的 minify 的 ES5 了。可是这么一通操作下来引入了 core-js 做 polyfill 体积不减反增maxify。
1 2
| 4.0K main.js 36K main.min.js
|
所以说如果不考虑兼容性就直接用gulp-uglify-es
好了。
前端的构建工具大版本总是不兼容,网上的信息也很多已经过时了。这篇文章估计在不久之后也会过时的。不得不说跟上时代最好的方法还是官方文档呀。
实例放在了 GitHub,各个步骤都对应的分支。
我们还可以借助github-history
来看 gulpfile 的变化。