Babel使用指南

本篇圍繞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í)匯總

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容