https://www.jb51.net/article/135225.htm
介紹
es6特性瀏覽器還沒有全部支持,但是使用es6是大勢所趨,所以babel應運而生,用來將es6代碼轉換成瀏覽器能夠識別的代碼
babel有提供專門的命令行工具方便轉碼,可以自行去了解
什么是Babel
官方解釋,是下一代JavaScript 語法的編譯器。
既然是下一代Javascript的標准,瀏覽器因版本的不同對此會有兼容性問題,JavaScript的新的方法都不能使用,但是目前我們在項目開發一直提倡使用最新的語法糖編寫,不但能減少代碼量,而且async,await等新特性還解決了回調的編寫機制,減輕了代碼維護成本。
Babel就因此而生,它可以讓你放心使用大部分的JavaScript的新的標准的方法,然后編譯成兼容絕大多數的主流瀏覽器的代碼。在項目工程腳手架中,一般會使用.babelrc文件,通過配置一些參數配合webpack進行打包壓縮。也通過網上了解,寫法各有不同,參數也大不相同,因此,我重新整理一份資料,詳細的介紹下各個配置項的意義所在,以便清晰了解如果使用。
以下配置主要正對webpack3+寫法。
Babel轉譯器
在.babelrc配置文件中,主要是對預設(presets)和插件(plugins)進行配置,因此不同的轉譯器作用不同的配置項,大致可分為以下三項:
1.語法轉義器。主要對javascript最新的語法糖進行編譯,並不負責轉譯javascript新增的api和全局對象。例如let/const就可以被編譯,而includes/Object.assign等並不能被編譯。常用到的轉譯器包有,babel-preset-env、babel-preset-es2015、babel-preset-es2016、babel-preset-es2017、babel-preset-latest等。在實際開發中可以只選用babel-preset-env來代替余下的,但是還需要配上javascirpt的制作規范一起使用,同時也是官方推薦
1
2
3
4
5
6
|
{
"presets"
: [
"env"
, {
"modules"
:
false
}],
"stage-2"
}
|
2.補丁轉義器。主要負責轉譯javascript新增的api和全局對象,例如babel-plugin-transform-runtime這個插件能夠編譯Object.assign,同時也可以引入babel-polyfill進一步對includes這類用法保證在瀏覽器的兼容性。Object.assign 會被編譯成以下代碼:
__WEBPACK_IMPORTED_MODULE_1_babel_runtime_core_js_object_assign___default()
3.jsx和flow插件,這類轉譯器用來轉譯JSX語法和移除類型聲明的,使用Rect的時候你將用到它,轉譯器名稱為babel-preset-react
創建預設(presets)
主要通過npm安裝babel-preset-xx插件來配合使用,例如通過 npm install babel-preset-stage-2 babel-preset-env --save-dev 安裝,會有相應如下配置。
1
2
3
4
5
6
|
{
"presets"
: [
[
"env"
, options],
"stage-2"
]
}
|
stage-2配置
babel主要提供以下幾種轉義器包,括號里面是對應配置文件的配置項
1
2
3
4
|
babel-preset-stage-0(stage-0)
babel-preset-stage-1(stage-1)
babel-preset-stage-2(stage-2)
babel-preset-stage-3(stage-3)
|
不同階段的轉譯器之間是包含的關系,preset-stage-0轉譯器除了包含了preset-stage-1的所有功能還增加了transform-do-expressions插件和transform-function-bind插件,同樣preset-stage-1轉譯器除了包含preset-stage-2的全部功能外還增加了一些額外的功能。
options配置介紹
官方推薦使用babel-preset-env來替代一些插件包的安裝(es2015-arrow-functions,es2015-block-scoped-functions等等),並且有如下幾種配置信息,介紹幾個常用的,
更多配置可以參考官網https://babeljs.io/docs/plugins/preset-env/
1
2
3
4
5
6
7
8
|
{
"targets"
: {
"chrome"
: 52,
"browsers"
: [
"last 2 versions"
,
"safari 7"
],
"node"
:
"6.10"
}
"modules"
:
false
}
|
targets可以制定兼容瀏覽器版本,如果設置了browsers,那么就會覆蓋targets原本對瀏覽器的限制配置。
targets.node正對node版本進行編譯
modules通常都會設置為false,因為默認都是支持CommonJS規范,同時還有其他配置參數:"amd" | "umd" | "systemjs" | "commonjs",systemjs我還不知道規范寫法是什么,amd和umd以及commonjs相對比較熟悉,下面簡要列舉下書寫規范。
amd代碼規范,在ng1中會用到比較多,主要用於依賴注入:
1
2
3
4
5
6
7
|
define([
'jquery'
],
function
($) {
// 方法
function
myFunc(){};
// 暴露公共方法
return
myFunc;
})
|
commonjs規范,也是node環境中尊崇的一種規范:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
var
$ = require(
'jquery'
);
// 方法
function
myFunc(){};
// 暴露公共方法(一個)
module.exports = myFunc;
{% endcodeblock %}
>umd規范,兼容amd以及commonjs規范,目前在第三方插件編寫使用比較多:
{% codeblock lang:javascript %}
(
function
(root, factory) {
if
(
typeof
define ===
'function'
&& define.amd) {
// AMD
define([
'jquery'
], factory);
}
else
if
(
typeof
exports ===
'object'
) {
// Node, CommonJS之類的
module.exports = factory(require(
'jquery'
));
}
else
{
// 瀏覽器全局變量(root 即 window)
root.returnExports = factory(root.jQuery);
}
}(
this
,
function
($) {
// 方法
function
myFunc(){};
// 暴露公共方法
return
myFunc;
}));
|
插件(plugins)
插件配置項同預設配置項一樣,需要搭配babel相應的插件進行配置,可以選擇配置插件來滿足單個需求,例如早期我們會有如下配置:
1
2
3
4
5
6
7
8
|
{
"plugins"
: [
"check-es2015-constants"
,
"es2015-arrow-functions"
,
"es2015-block-scoped-functions"
,
// ...
]
}
|
但是這些插件從維護到書寫極為麻煩,后來官方統一推薦使用env,全部替代了這些單一的插件功能,可以簡化配置如下,也就是我前面提到了babel-preset-env:
1
2
3
4
5
|
{
"presets"
: [
"es2015"
]
}
|
這里主要介紹兩款常用插件,分別是babel-plugin-transform-runtime,babel-plugin-syntax-dynamic-import。
基本配置代碼如下:
1
2
3
4
5
|
{
"plugins"
: [
"syntax-dynamic-import"
,[
"transform-runtime"
]
]
}
|
transform-runtime
為了解決這種全局對象或者全局對象方法編譯不足的情況,才出現了transform-runtime這個插件,但是它只會對es6的語法進行轉換,而不會對新api進行轉換。如果需要轉換新api,也可以通過使用babel-polyfill來規避兼容性問題。
對Object.assign進行編譯,配置與未配置經過webpack編譯后的代碼片段如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
// 未設置代碼片段:
__webpack_require__(
"ez/6"
);
var
aaa = 1;
function
fna() {
var
dd = 33333;
var
cc = Object.assign({ key: 2 });
var
xx = String.prototype.repeat.call(
'b'
, 3);
if
(
"foobar"
.String.prototype.includes(
"foo"
)) {
var
vv = 1;
}
return
dd;
}
// 設置代碼片段:
/* harmony import */
var
__WEBPACK_IMPORTED_MODULE_1_babel_runtime_core_js_object_assign___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1_babel_runtime_core_js_object_assign__);
__webpack_require__(
"ez/6"
);
var
aaa = 1;
function
fna() {
var
dd = 33333;
var
cc = __WEBPACK_IMPORTED_MODULE_1_babel_runtime_core_js_object_assign___default()({ key: 2 });
var
xx = String.prototype.repeat.call(
'b'
, 3);
if
(
"foobar"
.String.prototype.includes(
"foo"
)) {
var
vv = 1;
}
return
dd;
}
|
對class定義類會進行編譯,配置與未配置經過webpack編譯后的代碼片段如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
// 未設置代碼片段:
function
_classCallCheck(instance, Constructor) {
if
(!(instance
instanceof
Constructor)) {
throw
new
TypeError(
"Cannot call a class as a function"
); } }
var
Canvas =
function
Canvas(height, width) {
_classCallCheck(
this
, Canvas);
this
.height = height;
this
.width = width;
};
var
Canvas2 =
function
Canvas2(height, width) {
_classCallCheck(
this
, Canvas2);
this
.height = height;
this
.width = width;
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
// 設置代碼片段:
/* harmony import */
var
__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_classCallCheck___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_classCallCheck__);
var
Canvas =
function
Canvas(height, width) {
__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_classCallCheck___default()(
this
, Canvas);
this
.height = height;
this
.width = width;
};
var
Canvas2 =
function
Canvas2(height, width) {
__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_classCallCheck___default()(
this
, Canvas2);
this
.height = height;
this
.width = width;
};
|
對Generator函數也有同上的編譯效果,目前項目中使用該函數較小,一般使用promise替代,以及async await所以未對該函數做測試。
官方說對promise也會產生編譯,但是實際測試結果卻沒有效果
經過webpack打包最終測試,引入transform-runtime該配置項后,打包入口js文件大小會略微增大,並不像官方說的能夠縮小文件體積
未配置的最終打包效果:
配置后的最終打包效果:
雖然文件大小會有所增大,但是解決一些兼容性的問題,同時,從以上給出的測試代碼例子來看,使用transform-runtime后,可以減少內部全局函數的定義,從結構上看尊崇了webpack的模塊化思想,所以還是建議使用該插件。
syntax-dynamic-import
這個插件主要解決動態引入模塊的問題
1
2
3
4
5
6
7
8
|
function
nDate() {
import(
'moment'
).then(
function
(moment) {
console.log(moment().format());
}).
catch
(
function
(err) {
console.log(
'Failed to load moment'
, err);
});
}
nDate();
|
如果.babelrc配置項中使用了"stage-2",也可以不實用該插件,同樣支持動態模塊引入。
不然就會報以下錯誤:
- Module build failed: SyntaxError: 'import' and 'export' may only appear at the top level, or (import 和 export只能在最外層,也就是不能用在函數或者塊中)
- Module build failed: SyntaxError: Unexpected token, expected {
其他配置項
ignore
主要作用就是可以指定不編譯那些代碼
1
2
3
|
{
"ignore"
:[
"./module/a.js"
]
}
|
let,Object.assign,class定義都未編譯,編譯效果如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
__webpack_require__(
"ez/6"
);
const aaa = 1;
function
fna () {
let dd = 33333
let cc = Object.assign({key:2})
let xx = String.prototype.repeat.call(
'b'
, 3)
if
(
"foobar"
.String.prototype.includes(
"foo"
)) {
let vv = 1
}
return
dd
}
function
fna2 () {
return
fna() + aaa + __WEBPACK_IMPORTED_MODULE_0__b__[
"a"
/* default */
]
}
class Canvas {
constructor(height, width) {
this
.height = height;
this
.width = width;
}
}
class Canvas2 {
constructor(height, width) {
this
.height = height;
this
.width = width;
}
}
|
minified
主要設置編譯后是否是壓縮,boolean類型,如果使用babel-cli進行打包編譯文件這個配置項能夠起到作用,但是目前大部分還是會依賴第三方打包工具,例如webpack,所以這個配置參數一般不用設置,webpack插件中的UglifyJsPlugin做了壓縮的工作。
comments
在生成的文件中,不產生注釋,boolean類型,webpack插件中的UglifyJsPlugin也同樣集成了這個功能。
env
基本配置如下:
1
2
3
4
5
6
7
8
9
10
|
{
"env"
: {
// test 是提前設置的環境變量,如果沒有設置BABEL_ENV則使用NODE_ENV,如果都沒有設置默認就是development
"test"
: {
"presets"
: [
"env"
,
"stage-2"
],
// instanbul是一個用來測試轉碼后代碼的工具
"plugins"
: [
"istanbul"
]
}
}
}
|
再談兼容性問題
Babel默認只轉換新的JavaScript語法,而不轉換新的API,比如Iterator、Generator、Set、Maps、Promise等等全局對象,以及一些定義在全局對象上的方法(比如Object.assign)都不會轉碼,具體的可以參考babel-plugin-transform-runtime模塊的 definitions.js 文件。
這里主要涉及到babel編譯后依然會存在瀏覽器兼容性問題,一般會使用transform-runtime和babel-polyfill配合使用,對於后者只需要在項目入口文件require引入即可。
當然在使用類似Object.assign函數功能時,可以使用lodash庫來替代,promise可以使用Q.js替代等等方案,這樣依賴可以不需要引入以上插件,具體可以根據項目具體安排
總結
.babelrc配置文件主要還是以presets和plugins組成,通過和webpack配合進行使用,分享下我們在項目中常用的配置。以上都是通過學習總結出來的,有什么不對的地方希望指出。
vue項目開發使用的配置如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
{
"presets"
: [
[
"env"
, {
"modules"
:
false
}],
"stage-2"
],
// 下面指的是在生成的文件中,不產生注釋
"comments"
:
false
,
"plugins"
: [
"transform-runtime"
,
"syntax-dynamic-import"
],
"env"
: {
// test 是提前設置的環境變量,如果沒有設置BABEL_ENV則使用NODE_ENV,如果都沒有設置默認就是development
"test"
: {
"presets"
: [
"env"
,
"stage-2"
],
// instanbul是一個用來測試轉碼后代碼的工具
"plugins"
: [
"istanbul"
]
}
}
}
|
react項目開發使用的配置如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
{
"presets"
: [
[
"env"
, {
"modules"
:
false
}],
"stage-2"
,
"react"
],
"plugins"
: [
"transform-runtime"
],
"comments"
:
false
,
"env"
: {
"test"
: {
"presets"
: [
"env"
,
"stage-2"
],
"plugins"
: [
"istanbul"
]
}
}
}
|