本篇圍繞gulp記錄Babel的使用,其它工具差不多
1、安裝gulp-babel
npm install gulp-babel --save-dev
babel的作用是將ES6轉(zhuǎn)換為ES5,所以得指定轉(zhuǎn)換規(guī)則是什么,這可以通過presets設(shè)置,但是之前還得安裝這些規(guī)則,如
//使用這個(gè)插件,將不再需要使用 es20xx presets 了
npm install babel-preset-env --save-dev
然后看下轉(zhuǎn)換前后的代碼
// gulp 配置項(xiàng)
gulp.task('ES6', function() {
return gulp.src([config.src + 'static/es6/**/*', '!' + config.src + 'static/es6/**/*.min.js'])
.pipe(babel({
presets: ['env']
// plugins: ['transform-runtime']
}))
.pipe(gulp.dest(config.src + 'static/js'))
});
// 轉(zhuǎn)換前
let f = () => {
console.log(123);
}
// 轉(zhuǎn)換后
var f = function f() {
console.log(123);
};
然而并不是所有的轉(zhuǎn)換都這么簡(jiǎn)單:
// 轉(zhuǎn)換前
const obj = {
a: 1,
b: 'str',
c: true
};
let [a, b] = obj;
// 轉(zhuǎn)換后
var obj = {
a: 1,
b: 'str',
c: true
};
var _obj = _slicedToArray(obj, 2),
a = _obj[0],
b = _obj[1];
var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
可見為了轉(zhuǎn)換,引入了一個(gè)_slicedToArray函數(shù),假如你有很多個(gè)JS文件都用了解構(gòu)賦值,那么這個(gè)函數(shù)就會(huì)出現(xiàn)在所有的JS文件中,這顯然是不合理的,于是你需要安裝一個(gè)插件:
npm install --save-dev babel-plugin-transform-runtime
然后上面解構(gòu)賦值的例子變?yōu)?/p>
'use strict';
var _slicedToArray2 = require('babel-runtime/helpers/slicedToArray');
var _slicedToArray3 = _interopRequireDefault(_slicedToArray2);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
// 解構(gòu)賦值
var obj = {
a: 1,
b: 'str',
c: true
};
var _obj = (0, _slicedToArray3.default)(obj, 2),
a = _obj[0],
b = _obj[1];
簡(jiǎn)單來看,這個(gè)插件將輔助轉(zhuǎn)換函數(shù)統(tǒng)一起來,使得在不同文件中使用的轉(zhuǎn)換函數(shù)都來自一個(gè)模塊,轉(zhuǎn)換后的js文件在體積上也減少了(不再是一坨輔助函數(shù),只是同一個(gè)模塊的引用)
transform-runtime的作用不止于此,它還可以按需引入ES6中新的API,如Promise,Proxy等,如下:
// 轉(zhuǎn)換前
let ajax = () => {
return new Promise(res => {
setTimeout(() => {
res()
}, 1000);
})
}
// 轉(zhuǎn)換后
"use strict";
var _promise = require("babel-runtime/core-js/promise");
var _promise2 = _interopRequireDefault(_promise);
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
var ajax = function ajax() {
return new _promise2.default(function(res) {
setTimeout(function() {
res();
}, 1000);
});
};
babel自己實(shí)現(xiàn)的Promise實(shí)際是封裝在_promise2.default上,這也說明了babel引入的墊片不會(huì)污染全局變量,比如瀏覽器自己實(shí)現(xiàn)的Promise是不會(huì)被babel污染的。但是這也導(dǎo)致了另一個(gè)問題:babel不會(huì)去修改全局變量,那么新增的一些實(shí)例方法,如'abc'.includes(),就沒法轉(zhuǎn)換了,在一些不支持該方法的環(huán)境下就會(huì)報(bào)錯(cuò)咯(諸如Array.from()等靜態(tài)方法Babel還是可以轉(zhuǎn)換的,但實(shí)例方法不行),所以還得按個(gè)包:
npm install --save-dev babel-polyfill
使用方式不是在babel配置里,而是直接在項(xiàng)目主js的開頭直接引入:
import 'babel-polyfill'
這樣引入的缺點(diǎn)是會(huì)污染瀏覽器已經(jīng)支持的某些全局API,此外全部引入會(huì)導(dǎo)致文件體積增大,且有很多用不上的polyfill.目前webpack2的tree-shaking技術(shù)似乎可以解決,但是gulp好像沒有這個(gè)功能,于是純gulp體系下就只能自己手動(dòng)引入所需模塊了,如自己新建一個(gè)es6-polyfill.js:
//es6-polyfill.js
import 'core-js/es6/array'
import 'core-js/es6/function'
import 'core-js/es6/map'
import 'core-js/es6/math'
import 'core-js/es6/number'
import 'core-js/es6/object'
import 'core-js/es6/promise'
import 'core-js/es6/regexp'
import 'core-js/es6/string'
import 'core-js/fn/array/includes'
2、Babel常用的一些模塊
- babel-preset-es2015
將ES6代碼編譯成ES5語(yǔ)法代碼 - babel-preset-stage-num
JavaScript還有一些提案,正在推進(jìn),不久的將來也可能成為標(biāo)準(zhǔn)的一部分,所以目前將這些草案提出,內(nèi)容更新直至最終成為標(biāo)準(zhǔn),添加進(jìn)標(biāo)準(zhǔn)庫(kù)的過程劃分為 5(0-4)個(gè)階段。 根據(jù)提案的狀態(tài)和內(nèi)容,將其在各個(gè)階段更新(階段0至階段3),最終在階段 4表明該提案被標(biāo)準(zhǔn)正式采納,當(dāng)然不被采納的提案不會(huì)進(jìn)入階段4。
以下是4個(gè)不同階段的打包預(yù)設(shè):
babel-preset-stage-0
babel-preset-stage-1
babel-preset-stage-2
babel-preset-stage-3
注: stage-4 預(yù)設(shè)不存在,它其實(shí)就是上文介紹的 es2015 預(yù)設(shè)。
以上每種預(yù)設(shè)都包含緊隨的后期階段預(yù)設(shè),同時(shí)還可能包含其他額外特性。例如,babel-preset-stage-0包含 babel-preset-stage-1, babel-preset-stage-2,babel-preset-stage-3,而 babel-preset-stage-1則包含 babel-preset-stage-2,babel-preset-stage-3依次后推。
選擇 babel-preset-stage-0 就能包含所有提案
babel-polyfill
Babel只能轉(zhuǎn)換語(yǔ)法糖類的語(yǔ)法,并不能轉(zhuǎn)換ES新增API,如Symbol, 'abc'.include()等,babel-polyfill就是干這個(gè)事,負(fù)責(zé)轉(zhuǎn)換新增API,在入口處import "babel-polyfill";即可babel-runtime
Babel轉(zhuǎn)換語(yǔ)法以支持ES6時(shí)會(huì)在每一個(gè)處理的文件頭部注入輔助代碼,產(chǎn)生很多冗余,重復(fù)性的內(nèi)容,導(dǎo)致代碼量暴增,所以我們需要將這些輔助代碼抽取至一個(gè)統(tǒng)一環(huán)境,babel-runtime就是干這個(gè)事,需安裝babel-plugin-transform-runtime 和 babel-runtime
小結(jié):
- npm install gulp-babel --save-dev // babel核心
- npm install babel-preset-env --save-dev //轉(zhuǎn)換規(guī)則
- npm install --save-dev babel-plugin-transform-runtime //提供ES特性的墊片和輔助轉(zhuǎn)換函數(shù),不全局污染
- npm install --save-dev babel-polyfill // 提供墊片,會(huì)全局污染,但本身包體比 babel-plugin-transform-runtime 小
3、Babel 相關(guān)模塊
- @babel/cli
命令行工具
npm install --save-dev @babel/cli
npx babel example.js -o compiled.js
- @babel/node
@babel/node模塊的babel-node命令,提供一個(gè)支持 ES6 的 REPL 環(huán)境。它支持 Node 的 REPL 環(huán)境的所有功能,而且可以直接運(yùn)行 ES6 代碼
npm install --save-dev @babel/node
npx babel-node es6.js
- @babel/register
@babel/register模塊改寫require命令,為它加上一個(gè)鉤子。此后,每當(dāng)使用require加載.js、.jsx、.es和.es6后綴名的文件,就會(huì)先用 Babel 進(jìn)行轉(zhuǎn)碼,使用時(shí),必須首先加載@babel/register
npm install --save-dev @babel/register
require('@babel/register');
require('./es6.js');
- @babel/core
如果某些代碼需要調(diào)用 Babel 的 API 進(jìn)行轉(zhuǎn)碼,就要使用@babel/core模塊
var es6Code = 'let x = n => n + 1';
var es5Code = require('@babel/core')
.transform(es6Code, {
presets: ['@babel/env']
})
.code;
console.log(es5Code);
// '"use strict";\n\nvar x = function x(n) {\n return n + 1;\n};'
- @babel/polyfill
Babel 默認(rèn)只轉(zhuǎn)換新的 JavaScript 句法(syntax),而不轉(zhuǎn)換新的 API,比如Iterator、Generator、Set、Map、Proxy、Reflect、Symbol、Promise等全局對(duì)象,以及一些定義在全局對(duì)象上的方法(比如Object.assign)都不會(huì)轉(zhuǎn)碼
4、參考:
1、babel-preset-env:你需要的唯一Babel插件
2、babel-preset-env: a preset that configures Babel for you
3、Webpack自動(dòng)化構(gòu)建實(shí)踐指南
4、不容錯(cuò)過的 Babel 7 知識(shí)匯總