一个新接手的项目。为了日后开发时的好心情,我对这个项目做了一些改造,以更适应现在的潮流。
原来的项目代码中可能也是为了兼容ie9,最多也就用到了es5的语法,看着还是挺难受的。明明map方法里面可以用诸如
xxx.map(item => item * 2)
这样的语法写的更简洁,却还是得用老语法
xxx.map(function(item){
return item * 2
})
然后代码量就多了两行了。
Babel是一个广泛使用的转码器,可以将ES6代码转为ES5代码,从而在现有环境执行。
以下是一个例子:
// 转码前
input.map(item => item + 1);
// 转码后
input.map(function (item) {
return item + 1;
});
网上关于babel介绍的博客有很多。比如阮一峰的babel入门教程,韩小平的博客的如何写好.babelrc?Babel的presets和plugins配置解析等,这些都是很好的入门教程。
这里综合以上教程和我的理解,稍微简单介绍一下
babel的配置文件名字是.babelrc,一般放在需要生效的目录下,可以直接放在根目录下起到全局设置的作用。
当使用到babel转换时,这个文件提供配置的依据,其格式如下:
{
"presets": [],
"plugins": []
}
presets字段为转码规则,常用的配置有以下几种
presets名称 | 含义 |
---|---|
es2015、es2016…es20xx | 使用es20xx的相关语法插件 |
stage-0、stage-1…stage-4 | 使用stage-x提案阶段的语法插件,数字越小,阶段越前 |
latest | 包括了es2015往后的所有插件 |
react | 允许使用flow,jsx等语法 |
env | 自动模式 |
对应的npm安装包格式为
babel-preset-${name}
其中${name}代表presets名称
plugins字段为babel的插件,可以通过插件达到很多特殊语法的处理。
以下介绍几种常见的插件
transform-runtime
options如下
*transform-runtime中的polyfill这个参数我们下面还会提到
babel提供了cli工具,用于命令行转码
npm i -g babel-cli
# 转码结果输出到标准输出
$ babel example.js
# 转码结果写入一个文件
# --out-file 或 -o 参数指定输出文件
$ babel example.js --out-file compiled.js
# 或者
$ babel example.js -o compiled.js
# 整个目录转码
# --out-dir 或 -d 参数指定输出目录
$ babel src --out-dir lib
# 或者
$ babel src -d lib
# -s 参数生成source map文件
$ babel src -d lib -s
使用npm安装gulp插件
npm i gulp-babel -D
配置gulp命令
const babel = require('gulp-babel')
gulp.task('babel', function () {
return gulp.src(path.join(jsDir, '/**/*'))
.pipe(babel({
presets: [
["latest", {
"modules": false
}]
]
}))
.pipe(gulp.dest(path.join(buildDir, jsDir)))
})
注意:这里用到的latest预设,需要下载下来对应的包
在命令行中输入gulp babel
就可以看到效果了
使用上面的配置,还有浏览器的Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise等全局api无法正常转码。这里需要引入上面介绍到的插件transform-runtime。
const babel = require('gulp-babel')
gulp.task('babel', function () {
return gulp.src(path.join(jsDir, '/**/*'))
.pipe(babel({
presets: [
["latest", {
"modules": false
}]
],
plugins: [
["transform-runtime",
{
"helpers": false,
"polyfill": true,
"regenerator": true,
"moduleName": "babel-runtime"
}]
]
}))
.pipe(gulp.dest(path.join(buildDir, jsDir)))
})
这里polyfill传了true,解决了babel的latest预设不能对那些全局api转码的问题。
使用babel,我们解决了大部分的语法问题,但是es6的import,export这种引用外部文件的语法没办法兼容。这里我们需要使用到打包工具。
说到打包,大家一般马上会想起webpack。
在上面的背景里谈到,项目结构属于angular 1.x的应用,项目中使用了angular指令、路由等很多特色语法以及非常多的html模板。我暂时没有在网上找到合适的loader去解析这些angular的语法,因此把目光放到了每个文件各自解析自己的思路上。这个想法跟webpack初衷相反,因此这里使用不了它。
rollup这个打包工具是通过和其他同事闲聊的时候得知的。抱着试一试的心态看了下文档,发现它擅长将小块的代码组合起来,正好适合这个项目使用。
使用npm安装rollup
npm install rollup –global
rollup的配置文件默认叫做rollup.config.js
以下是它的配置模板
// rollup.config.js
export default {
input: 'src/main.js',
output: {
file: 'bundle.js',
format: 'cjs'
}
}
input指定文件入口,output指定出口和转换方式。
在命令行中输入
rollup -c
就开始了文件打包
如果需要指定其他文件,可以在命令行后加上文件名,如
rollup --config rollup.config.dev.js
rollup --config rollup.config.prod.js
这里输出的格式是cjs,还有其他几种
更多参数请查阅官网
从上面的语法可以看出,rollup打包出来的文件非常精简,如果提供多个config,可以达到单个文件各自为政的效果。正好gulp插件有这么一个工具,叫做gulp-better-rollup提供了这个功能。
因此我们可以这么写在gulp文件当中
const rollup = require('gulp-better-rollup')
const resolve = require('rollup-plugin-node-resolve')
const commonjs = require('rollup-plugin-commonjs')
let rollupJsDist = function (gulpSrc) {
return gulpSrc
.pipe(rollup({
plugins: [commonjs(), resolve({
// 将自定义选项传递给解析插件
customResolveOptions: {
moduleDirectory: 'node_modules'
}
})]
}, 'umd'))
.pipe(sourcemaps.write())
}
gulp.task('rollupJs', function () {
return rollupJsDist(gulp.src(path.join(jsDir, '/**/*.js')))
.pipe(gulp.dest(path.join(buildDir, jsDir)));
})
这里给rollup传入了两个插件,rollup-plugin-node-resolve和rollup-plugin-commonjs,用来解析Node.js里面的CommonJS模块。
另外,这个gulp插件支持babel,因此结合上部分对于gulp里babel的部分,我们可以再改写一下
gulpfile.js:
const rollup = require('gulp-better-rollup')
const resolve = require('rollup-plugin-node-resolve')
const commonjs = require('rollup-plugin-commonjs')
const babel = require('rollup-plugin-babel')
let rollupJsDist = function (gulpSrc) {
return gulpSrc
.pipe(rollup({
plugins: [commonjs(), resolve({
// 将自定义选项传递给解析插件
customResolveOptions: {
moduleDirectory: 'node_modules'
}
}), babel()]
}, 'umd'))
.pipe(sourcemaps.write())
}
gulp.task('rollupJs', function () {
return rollupJsDist(gulp.src(path.join(jsDir, '/**/*.js')))
.pipe(gulp.dest(path.join(buildDir, jsDir)));
})
.babelrc:
{
"presets": [
[
"latest",
{
"es2015": {
"modules": false
}
}
]
],
"plugins": [
"external-helpers",
"lodash",
[
"transform-runtime",
{
"helpers": false,
"polyfill": true,
"regenerator": true,
"moduleName": "babel-runtime"
}
]
]
}
以上加入了babel的插件,babel的配置写在了外部文件。其中babelrc文件里面根据官方介绍babel增加了一些东西:
根据需求,我在配置里面加了lodash的插件,允许lodash按需加载项目中所使用的函数,可以参考这里对插件的介绍
rollup打包出来的文件非常精简,它只在乎那些用得到的东西,把打包前后的文件对比你能非常直观的发现它加进去的所有东西。
// the-answer.js
var index = 42;
export default index;
比如当我用import语法引入the-answer.js这个文件
// main.js
import answer from 'the-answer';
export default function () {
console.log('the answer is ' + answer);
}
最后打包的结果为
// bundle.js
'use strict';
var index = 42;
function main () {
console.log('the answer is ' + index);
}
module.exports = main;
//# sourceMappingURL=bundle.js.map
可以看到,rollup只是将其中index=42
的部分拿了出来。
这个合并的技术官方称之为Tree-shaking