本文基於 babel 7 作敘述,若是以前一直使用 babel 6 的同窗能夠先看本文關於 babel 6 升級 babel 7 的相關模塊javascript
Babel 的編譯過程能夠分爲三個階段:java
使用 @babel/core 中的 parse API 來進行詞法分析(分詞+語法分析),轉化成 AST,爲 Transform 準備。
Tip:感興趣的同窗能夠閱讀這篇文章深刻了解下:www.alloyteam.com/2017/04/ana… 本文重在講述 babel 在項目中的實踐呀。node
使用 @bable/core 中的 transform API 來進行轉義;咱們熟知的插件就是應用在這一過程,若是這一階段不使用任何插件,Babel 將在生成階段輸出原樣的代碼;react
// react + typescript
"presets": [
[
"@babel/preset-env",
{
"targets": "cover 95%, safari >= 7", # 根據項目實際狀況配置
"modules": "cjs",
"useBuiltIns": "usage"
}
],
"@babel/react",
"@babel/preset-typescript", # 若是項目使用 typescript 開發須要安裝這個預設
]
複製代碼
用 babel-generator 經過 AST 生成 ES5 代碼; TIP:若是在 babel 的配置中,不使用任何 presets 和 plugins,那麼生成的代碼和原代碼無異;webpack
@babel-polyfill 的不足,在平常業務開發中,影響不是很大的,可是若是在通用的第三方庫的開發中引入 @babel-polyfill,就會帶來潛在的問題:好比咱們項目中自定義了一份新的 API 的實現,可是引入的第三方通用庫使用了polyfill,而且實現了一樣的方法,就會將咱們原有的方法覆蓋(至少會有存在這樣的風險),爲了不這樣的問題,babel 推出了 @babel-runtime 來實現 @babel-polyfill 的絕大部分的功能,它將開發者依賴的全局內置 對象等,單獨抽成模塊,經過模塊導入的方式,避免了對全局做用域的污染。 因此,若是是開發庫、工具,能夠考慮使用 @babel-runtime;git
# babel-plugin-transform-runtime 用於構建過程的代碼轉換
npm install --save-dev babel-plugin-transform-runtime
npm install --save babel-runtime
複製代碼
{
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"absoluteRuntime": false,
"corejs": false,
"helpers": true,
"regenerator": true,
"useESModules": false
}
]
]
複製代碼
@babel/plugin-transform-runtime 插件主要作了以下三件事情:es6
# js
var sym = Symbol();
var promise = new Promise();
console.log(arr[Symbol.iterator]());
# 轉化後
"use strict";
var _getIterator2 = require("@babel/runtime-corejs2/core-js/get-iterator");
var _getIterator3 = _interopRequireDefault(_getIterator2);
var _promise = require("@babel/runtime-corejs2/core-js/promise");
var _promise2 = _interopRequireDefault(_promise);
var _symbol = require("@babel/runtime-corejs2/core-js/symbol");
var _symbol2 = _interopRequireDefault(_symbol);
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : { default: obj };
}
var sym = (0, _symbol2.default)();
var promise = new _promise2.default();
console.log((0, _getIterator3.default)(arr));
複製代碼
# js
class Person() {}
# 轉化後
"use strict";
var _classCallCheck2 = require("@babel/runtime/helpers/classCallCheck");
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : { default: obj };
}
var Person = function Person() {
(0, _classCallCheck3.default)(this, Person);
};
複製代碼
# js
function* foo() {}
# 轉化後
"use strict";
var _regenerator = require("@babel/runtime/regenerator");
var _regenerator2 = _interopRequireDefault(_regenerator);
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : { default: obj };
}
var _marked = [foo].map(_regenerator2.default.mark);
function foo() {
return _regenerator2.default.wrap(
function foo$(_context) {
while (1) {
switch ((_context.prev = _context.next)) {
case 0:
case "end":
return _context.stop();
}
}
},
_marked[0],
this
);
}
複製代碼
因爲 @babel-runtime 和 @babel/plugin-transform-runtime 實現方式致使它不會對實例方法進行擴展(例如 Array.prototype.includes()
),因此在使用中,存在很大的隱患,簡單的說,若是你使用的是 @babel-runtime 實現的polyfill,那麼,就會出現你使用的部分原型方法得不到支持而報錯,這是不能接受的,因此我推薦在項目開發中,仍是使用 @babel-polyfill 比較穩妥。github
TIP: 關於 @babel/preset-env 的詳細內容能夠查看下一小節的內容 經過在 presets 裏使用 @babel/preset-env preset,設置 useBuiltInts 爲 usage 或者或者 entry 來實現按需引入; 與@babel/preset-env一塊兒使用時(注意,仍然須要安裝@ babel-polyfill。)web
從 2018-9-17 發佈至今,每週的下載量都在穩步提高,推薦小夥伴們在項目中使用基於 babel 7的 @babel/preset-env 插件預設;chrome
@babel-preset-env 將基於你的實際瀏覽器及運行環境,自動的肯定 babel 插件及 polyfill,編譯ES2015 及此版本以上的語言,在沒有配置項的狀況下,@babel-preset-env 表現的同 babel-preset-latest同樣(或者能夠說同babel-preset-es201五、babel-preset-es201六、 babel-preset-es2017 結合到一塊兒表現的一致)。
對於實驗屬性(babel-preset-latest 不支持的)須要手動安裝配置相應的 plugins 或者 presets; 能夠經過 babeljs.io/docs/en/plu… 來查詢相關的實驗屬性對應的插件,來引入支持業務開發;
"babel": {
"presets": [
[
"env",
{
"targets": {
"browsers": ["last 2 versions", "ie >= 7"]
}
}
]
]
},
複製代碼
"targets": {
"browsers": "> 5%"
}
複製代碼
"targets": {
"chrome": 56
}
複製代碼
@babel-preset-env 默認只支持對語法的轉化,須要開啓useBuiltIns配置才能轉化 API 和實例方法, 來爲標準庫中的新功能提供了 polyfill,爲內置對象,靜態方法,實例方法,生成器函數提供支持。 @babel-preset-env 能夠實現基於特定環境引入須要的polyfill。
若是你經過Babel編譯你的Node.js代碼,babel-preset-env 頗有用,設置 "targets.node" 是 "current",支持的是當前運行版本的nodejs:
const presets = [
[
"@babel/env",
{
node: 'current',
},
],];
module.exports = { presets };
複製代碼
在遷移以前建議熟悉 babel 7 具體帶來的改動,這樣有助於咱們在項目中進行具體的優化和進行鍼對性的配置修改
熟悉相關重要改動,能夠幫助咱們更好的去配置項目,使用最新支持的特性,提升編碼效率
# right
{
"presets": ["@babel/preset-env", "@babel/preset-react"]
}
# wrong
{
"presets": "@babel/preset-env, @babel/preset-react"
}
複製代碼
render() {
return (
<>
<ChildA />
<ChildB />
</>
);
}
// output 👇
render() {
return React.createElement(
React.Fragment,
null,
React.createElement(ChildA, null),
React.createElement(ChildB, null)
);
}
複製代碼
interface Person {
firstName: string;
lastName: string;
}
function greeter(person : Person) {
return "Hello, " + person.firstName + " " + person.lastName;
}
複製代碼
使用後(移除類型聲明)
function greeter(person) {
return "Hello, " + person.firstName + " " + person.lastName;
}
複製代碼
import "@babel/polyfill";
複製代碼
但它包括整個 polyfill,若是瀏覽器已經支持,你可能不須要導入全部內容。這與@babel/preset-env 試圖用語法解決的問題相同,因此咱們在這裏將它應用於polyfill。選項 useBuiltins:「entry」 經過將原始導入僅拆分爲必要的導入來實現此目的。 可是咱們能夠經過僅導入代碼庫中使用的 polyfill 來作得更好。選項「useBuiltIns:」usage「是咱們第一次嘗試啓用相似的東西(詳細說明)。 關於 @babel/preset-env 詳細的說明,能夠參考這篇文檔:@babel-preset-env 配置說明 ,理解其中每個配置,來利用 babel 新增的 api 來結合項目自身特色進行配置,進行優化。
# 不安裝到本地而是直接運行命令,npm 的新功能
npx babel-upgrade --write
# 或者常規方式
npm i babel-upgrade -g
babel-upgrade --write
複製代碼
以一個依賴 babel 6 的項目爲例:經過 babel 官方提供的升級工具,輕鬆搞定依賴相關的問題:
["@babel/plugin-proposal-pipeline-operator", {
# 因爲項目中並無使用管道符,因此我這裏就直接給一個 minimal 的屬性值,其餘項目能夠根據具體須要作調整
"proposal": "minimal"
}]
複製代碼
babel 7 帶來了@babel/preset-env 這個插件,它擴展了對 polyfill 的配置,讓使用者能夠優化墊片的體積,作到按需加載,具體細節能夠參考 @babel-preset-env 配置說明
能夠參考我這篇 JavaScript 項目遷移 TypeScript 實踐分享 文章來作遷移(在餘下時間我會從新整理髮布到掘金)
# .babelrc
# yarn add @babel/preset-typescript
"presets": [
[
"@babel/env",
{
"targets": "cover 95%, safari >= 7",
"modules": "cjs",
"corejs": 2,
"useBuiltIns": "usage",
}
],
"@babel/react",
"@babel/typescript"
],
複製代碼
{
"compilerOptions": {
"module": "esnext",
"target": "es6",
"allowSyntheticDefaultImports": true,
"baseUrl": ".",
"sourceMap": true,
"outDir": "../client-dist",
"allowJs": true,
"jsx": "react",
"noEmit": true,
"moduleResolution": "node",
"isolatedModules": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
// `vs code`所須要的,在開發時找到對應的路徑,真實的引用是在`webpack`中配置的`alias`
"paths": {
}
},
# 這裏這麼設計,是由於咱們項目歷史緣由,js 的業務代碼比較多,在跑 ci tsc 作類型檢查的時候,只走 tsx? 類型文件
"include": ["src/**/*.ts", "src/**/*.tsx"],
"exclude": [
"node_modules"
]
}
複製代碼
# package.json
{
"scripts": {
"tsc": "tsc --noEmit"
}
}
複製代碼
本人想經過本文和你們分享一下本身關於 Babel 在項目中使用的一些心得,但願能夠給小夥伴們帶來一些幫助,Babel 不是一個黑盒子,而是一個給開發者提供了各類各樣選擇的工具集,術業有專攻,只須要稍微花一點時間來理解它,就能夠在項目中很安全地使用它,利用 Babel 團隊不斷努力給咱們帶來的新的語言特性的支持; 你們有什麼疑問能夠直接給我留言評論。
gitissue.com/issues/5c18… juejin.im/post/5d0373… juejin.im/post/5d74d4… segmentfault.com/q/101000000… www.jianshu.com/p/d078b5f30… jsweibo.github.io/2019/08/05/… juejin.im/post/5c03a4… blog.hhking.cn/2019/04/02/… github.com/sorrycc/blo… github.com/Kehao/Blog/… juejin.im/post/5b07e7… jsweibo.github.io/2019/08/09/… github.com/SunshowerC/… zhuanlan.zhihu.com/p/43249121 pdsuwwz.github.io/2018/09/29/… - babel 升級踩坑 juejin.im/post/5b174f… juejin.im/entry/5b108… github.com/lmk123/blog… segmentfault.com/a/119000001… segmentfault.com/a/119000001… www.zcfy.cc/article/bab… juejin.im/entry/5b108…