如何正確的設置babel(Late 2018)

前言

之前用 Babel 轉換語法,都是靠框架和 cli轉換,沒試過從頭自己設定。直到最近,接了沒只用原生 Javascript 的小案。身爲 ES6++ 的愛好者,一定要使用 Babel 才能存活啊,最後只好自己從頭研究XD 查了一下,發現很多設定其實已經過時,因此這邊寫一份筆記整理,希望能減少其他人研究的時間。

Babel 做什麼

Babel 能把比較新的 Javascript (關鍵字: ES6,ES2015 等),轉換成目前 Browser 能看得懂的 Javascript (ES5),讓 Developers 能用新語法的同時,不會讓網頁壞掉。

Babel 的 2 種模式

  1. Babel 默認只轉換語法(Syntax), 像是:
    a. Arrow function ( () => {} )
    b. Spread ( [ …state, isOpen ] )
    c. Destructing ( const { data } = res )
    d. Default Parameters ( ( id = NaN ) => {})
  1. 而自帶的 Api 、原生內置 Methods,需要加上 polyfill 後才能使用,像是:
    a. Promise
    b. Symbol
    c. Array.from
    d. Object.assign
    e. fetch

1. 轉換語法(Syntax)

直接轉換語法是非常方便的,以下放了 Webpack 和 Gulp 的範例。

1.1 Webpack

Webpack:

1
npm install -D babel-loader @babel/core @babel/preset-env webpack

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// webpack.config.js
module: {
rules: [
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
]
}

1.2 Gulp:

1
npm install --save-dev gulp-babel @babel/core @babel/preset-env
1
2
3
4
5
6
7
8
9
10
const gulp = require('gulp')
const babel = require('gulp-babel')

gulp.task('default', () =>
gulp.src('src/app.js')
.pipe(babel({
presets: ['@babel/env']
}))
.pipe(gulp.dest('dist'))
);

想要快的話,可在 Babel 網站的 Try It Out 直接轉換。(似乎沒有比較方便?)

2. Polyfill

相較於直接轉換 Syntax,Polyfill 才是讓人頭疼的地方。有時候需要支援的 Browser 太舊(eg: IE11),龐大的 Polyfill bundle 拖累了其他不需要 Polyfill 的 Browser。儘量用只需要的 Polyfill 變成是需要優化的目標。

2.1 Polyfill:用Webpack

2.1.1 entry 加入 @babel/polyfill

這是最早期的做法,就是讓 Webpack 也 bundle polyfill的檔案:

1
2
3
4
5
// webpack.config.js

module.exports = {
entry: ["@babel/polyfill", "./app/js"],
};

2.1.2 useBuiltIns

以前的做法是轉換語法(Syntax)和 Polyfill 是分開設定,現在在設定同一處統一用 useBuiltIns 設定,還有加上target(browserlist)的功能,讓設定方便很多。

useBuiltIns 有三種設定方式:
a. entry
b. usage
c. disable(就是不要加 polyfill 咯,就不解釋了)

2.1.2 a.) useBuiltIns : ‘entry’

在你的app入口加上這個:

1
2
// app.js
import '@babel/polyfill'

根據1.1和2.1.1的基礎做更動:

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
  // webpack.config.js
module: {
- entry: ["@babel/polyfill", "./app/js"],
+ entry: ["./app/js"],
rules: [
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
- presets: ['@babel/preset-env']
+ presets: [
+ '@babel/preset-env',
+ {
+ "targets": {
+ "ie": "11"
+ },
+ "useBuiltIns": "entry"
+ }
+ ]
}
}
}
]
}
2.1.2 b.) useBuiltIns : ‘usage’

在app入口拿掉:

1
2
// app.js
- import '@babel/polyfill'

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
  // webpack.config.js
module: {
- entry: ["@babel/polyfill", "./app/js"],
+ entry: ["./app/js"],
rules: [
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
- presets: ['@babel/preset-env']
+ presets: [
+ '@babel/preset-env',
+ {
+ "targets": {
+ "ie": "11"
+ },
- "useBuiltIns": "entry"
+ "useBuiltIns": "usage"
+ }
+ ]
}
}
}
]
}

上面設定完之後,就會根據你在寫 code 的過程中,加上需要的 polyfill。

但是由於 Babel 不能判斷你 code 裏面的 type,所以不能載入正確的 Polyfill。比方說:

1
2
import "core-js/modules/es7.array.includes";
a.includes // 預設 a 是一個 Array,但實際 a 也可能是 String

2.1.3 手動引入要的 core.js

1
2
import 'core-js/modules/es6.set.js'
import 'core-js/modules/es6.array.find-index.js'

但如果需要很多 Polyfill 的話,一個個載入就會變得很麻煩。

2.2 Polyfill:不用Webpack

2.2.1 Gulp、Browser 引用打包

在引入@babel/polyfill下的dist\polyfill.min.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// gulpfile.js
const polyfill = 'node_modules/babel-polyfill/dist/polyfill.min.js'

var jsfiles = [
'js/lib/otherJsLibrary.js',
polyfill,
'js/index.js'
];
gulp.task('default', function() {
return gulp.src(jsfile)
.pipe(concat('index.min.js')) //合併檔案
.pipe(uglify())
.pipe(gulp.dest('dist/')) //輸出
});

或者直接抓出檔案,在 Browser 直接引入:

1
<script src="polyfill.js"></script>

這樣的結果當然就是 Bundle 整個會變得非常肥,不需要 Polyfill 的 Browser 也被迫下載,造成資源的浪費。

2.2.2 Polyfill.io

這個由金融時報(Financial Times)提供的服務,根據你的 UA 來判斷需要的 Polyfill(當然你可以指定,點這裏可以看文檔),用法也很簡單,在你的網頁加上:

1
<script src="https://cdn.polyfill.io/v2/polyfill.min.js"></script>

在不同的 Browser 看就會發現很回傳的東西不太一樣:

這個是 Chrome 下看到的

這是 IE 11 下看到的

當然若有安全上的考量,可以到他們的github,下載然後自己 Deploy 在自己的 Server 上吧!

雖然在特定較舊的 Browser,還是有很肥的問題,但是卻拯救了其他 90% 的 Browser!

總結

總結那麼多方法,代表說我之前活動案被折磨很慘(誤),個人覺得最好的方法,是搭配 Webpack 的 Babel,加上自己架設的 Polyfill services,但是實際效果有待確定,有空再更新使用效果。

參考資料

  1. https://babeljs.io/docs/en/babel-polyfill
  2. https://github.com/Financial-Times/polyfill-service
  3. https://polyfill.io/v2/docs/
  4. https://babeljs.io/docs/en/babel-preset-env
  5. https://www.jianshu.com/p/0dc3bddb6da8
  6. https://juejin.im/post/5c09d6d35188256d9832df9d