babel的認識

一.簡介:

babel官網:https://www.babeljs.cn/
githup:https://github.com/babel/babel
Babel 是一個工具鏈,主要用於將 ECMAScript 2015+ 版本的代碼轉換爲向後兼容的 JavaScript 語法,以便能夠運行在當前和舊版本的瀏覽器或其他環境中。
我這裏只是介紹基本的使用方法,詳細的可以到官網查看。

1.1基本實現的效果:

// Babel 輸入: ES2015 箭頭函數
[1, 2, 3].map((n) => n + 1);

//-------------------------------------------------------------

// Babel 輸出: ES5 語法實現的同等功能
[1, 2, 3].map(function(n) {
  return n + 1;
});

1.2 整個設置過程:

運行以下命令以安裝軟件包:
(這些命令是通過的,哪些不滿足請自己安裝)

npm install --save-dev @babel/core @babel/cli @babel/preset-env
npm install --save-dev @babel/preset-stage-2
npm install --save @babel/plugin-transform-runtime
npm install --save npm install @babel-plugin-syntax-dynamic-import

.babelrc 文件(根目錄下),vue項目開發使用的配置如下:

{
  "presets": [
    [ "@babel/preset-env", {
      "modules": false
    }],
    "stage-2"
  ],
  // 下面指的是在生成的文件中,不產生註釋
  "comments": false,
  "plugins": ["transform-runtime","syntax-dynamic-import"],
  
  // 下面的代碼可以不要
  "env": {
    // test 是提前設置的環境變量,如果沒有設置BABEL_ENV則使用NODE_ENV,如果都沒有設置默認就是development
    "test": {
      "presets": [ "@babel/preset-env", "stage-2"],
      // instanbul是一個用來測試轉碼後代碼的工具
      "plugins": ["istanbul"]
    }
  }
}

.babelrc 文件(根目錄下),React項目開發使用的配置如下:

{
  "presets": [
    [ "@babel/preset-env",, { "modules": false }],
    "stage-2",
    "react"
  ]
  "plugins": ["transform-runtime"],
  "comments": false,
  "env": {
    "test": {
      "presets": ["env", "stage-2"],
      "plugins": [ "istanbul" ]
    }
  }
}

上面的瀏覽器列表只是一個任意示例。您需要進行調整以適合您要支持的瀏覽器。

1.3 babel7

@ 符號是什麼?這是 babel 7 的一大調整,原來的 babel-xx 包統一遷移到babel 域下 - 域由 @ 符號來標識,一來便於區別官方與非官方的包,二來避免可能的包命名衝突。

二.配置文件.babelrc

主要是對預設(presets)和插件(plugins)進行配置,基本格式:

{
  "presets": [],
  "plugins": []
}

2.1babel轉譯器

不同的轉譯器作用不同的配置項,大致可分爲以下三種:

2.1.2 語法轉義器

主要對javascript最新的語法糖進行編譯,並不負責轉譯javascript新增的api和全局對象。例如let/const就可以被編譯,而String.includes/Object.assign等並不能被編譯。常用到的轉譯器包有,babel-preset-envbabel-preset-es2015babel-preset-es2016babel-preset-es2017babel-preset-latest等。在實際開發中可以只選用babel-preset-env來代替餘下的,但是還需要配上javascirpt的製作規範一起使用,同時也是官方推薦

{
  "presets": 
  	[
  		"env", 
  		{
      		"modules": false
    	}
    ],
    "stage-2"
}

2.1.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()

2.1.3 jsx和flow插件

這類轉譯器用來轉譯JSX語法和移除類型聲明的,使用Rect的時候你將用到它,轉譯器名稱爲babel-preset-react

2.2預設(Presets)

2.2.1官方針對常用環境編寫了一些 preset

@babel/preset-env
@babel/preset-flow
@babel/preset-react
@babel/preset-typescript

2.2.2 Stage-X (實驗性質的 Presets)

從 Babel v7 開始,所有針對處於標準提案階段的功能所編寫的預設(stage preset)都已被棄用。
stage-x preset 中的任何語法轉換都是對語言本身的更改,而這些更改尚未被納入 JavaScript 標準(例如 ES6/ES2015)

  • Stage 0 - 設想(Strawman):只是一個想法,可能有 Babel插件。
  • Stage 1 - 建議(Proposal):這是值得跟進的。
  • Stage 2 - 草案(Draft):初始規範。
  • Stage 3 - 候選(Candidate):完成規範並在瀏覽器上初步實現。
  • Stage 4 - 完成(Finished):將添加到下一個年度版本發佈中。

處於 stage-3 之前的任何提案,請務必謹慎使用

2.2.3Preset 的路徑

如果 preset 在 npm 上,你可以輸入 preset 的名稱,babel 將檢查是否已經將其安裝到 node_modules 目錄下了

{
  "presets": ["babel-preset-myPreset"]
}

你還可以指定指向 preset 的絕對或相對路徑。

{
  "presets": ["./myProject/myPreset"]
}

2.2.4 Preset 的短名稱

如果 preset 名稱的前綴爲 babel-preset-,你還可以使用它的短名稱:

{
  "presets": [
  	//"babel-preset-myPreset" 
    "myPreset", 
  ]
}

2.2.5 Preset 的排列順序

Preset 是逆序排列的(從後往前)。

{
  "presets": [
    "a",
    "b",
    "c"
  ]
}

將按如下順序執行: c、b 然後是 a。
這主要是爲了確保向後兼容,由於大多數用戶將 “es2015” 放在 “stage-0” 之前。

2.2.6 Preset 的參數

插件和 preset 都可以接受參數,參數由插件名和參數對象組成一個數組,可以在配置文件中設置。

如果不指定參數,下面這幾種形式都是一樣的:

{
  "presets": [
    "presetA",
    ["presetA"],
    ["presetA", {}],
  ]
}

要指定參數,請傳遞一個以參數名作爲鍵(key)的對象。

{
  "presets": [
    ["@babel/preset-env", {
      "loose": true,
      "modules": false
    }]
  ]
}

2.3 插件(plugins)

官方文檔
Babel 是一個編譯器(輸入源碼 => 輸出編譯後的代碼)。就像其他編譯器一樣,編譯過程分爲三個階段:解析、轉換和打印輸出。
除了一個一個的添加插件,你還可以以 preset 的形式啓用一組插件。

2.4 插件和預設選項

官方文檔
官方推薦使用babel-preset-env來替代一些插件包的安裝(es2015-arrow-functions,es2015-block-scoped-functions等等),並且有如下幾種配置信息,介紹幾個常用的

2.4.1 targets

.babelrc

"presets": ["@babel/preset-env", {
      {
	    "targets": {
	        "chrome": 52,
	        "browsers": ["last 2 versions", "safari 7"],
	        "node":"6.10"
	    }
	    "modules": false
	}
 }]

targets可以制定兼容瀏覽器版本,如果設置了browsers,那麼就會覆蓋targets原本對瀏覽器的限制配置。
targets.node對node某一個版本進行編譯
modules通常都會設置爲false,因爲默認都是支持CommonJS規範

2.4.2 ignore

主要作用就是可以指定不編譯哪些代碼
.babelrc

{
  "ignore":["./module/a.js"]
}

2.4.2 minified

主要設置編譯後是否是壓縮,boolean類型,如果使用babel-cli進行打包編譯文件這個配置項能夠起到作用,但是目前大部分還是會依賴第三方打包工具,例如webpack,所以這個配置參數一般不用設置,webpack插件中的UglifyJsPlugin做了壓縮的工作。

2.4.2 comments

在生成的文件中,不產生註釋,boolean類型,webpack插件中的UglifyJsPlugin也同樣集成了這個功能。

2.4.3 env

官網
.babelrc

{
  "env": {
    // test 是提前設置的環境變量,如果沒有設置BABEL_ENV則使用NODE_ENV,如果都沒有設置默認就是development
    "test": {
      "presets": ["env", "stage-2"],
      // instanbul是一個用來測試轉碼後代碼的工具
      "plugins": ["istanbul"]
    }
  }
}

3. 常用的

babel7

3.1 @babel-core

babel代碼的核心

npm install --save-dev @babel/core

3.2 @babel/cli

babel 提供的命令行工具,用於命令行下編譯源代碼

npm install --save-dev @babel/cli

更改packaeg.json

 "scripts": {
 	// 編譯src文件夾下的index.js,並導出到lib文件夾
    "build": "babel src -d lib"
  },

運行npm run build,
發現的確是生成了lib文件夾,但是lib/index.js還是原來的代碼,沒有任何變化。說好的編譯呢?

這個調整則是在 babel 6 裏發生的。Babel 6 做了大量模塊化的工作,將原來集成一體的各種編譯功能分離出去,獨立成插件。這意味着,默認情況下,當下版本的 babel 不會編譯代碼。

3.3 @babel/plugin-transform-arrow-functions

箭頭函數編譯成 ES5 函數,需要安裝額外的 babel 插件

npm install --save-dev @babel/plugin-transform-arrow-functions

使用.babelrc:

{
  "plugins": ["@babel/plugin-transform-arrow-functions"]
}

3.4 @babel/preset-env

可以把 Preset 理解爲套餐,每個套餐裏打包了不同的插件,這樣安裝套餐就等於一次性安裝各類 babel 插件。

$ npm install --save-dev @babel/preset-env

.babelrc:

{
  "presets": ["@babel/preset-env"]
}

3.5 @babel/polyfill

Babel默認只轉換新的JavaScript句法(syntax),而不轉換新的API,比如Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise等全局對象,以及一些定義在全局對象上的方法(比如Object.assign)都不會轉碼。

舉例來說,ES6在Array對象上新增了Array.from方法。Babel就不會轉碼這個方法。如果想讓這個方法運行,必須使用babel-polyfill,爲當前環境提供一個墊片。

babel-polyfill - 讓目標瀏覽器支持所有特性,不管它是全局的,還是原型的,或是其它。這樣,通過 babel-polyfill,不同瀏覽器在特性支持上就站到同一起跑線。

 npm install --save @babel/polyfill

使用 babel-polyfill

在程序入口文件的頂部引用 @babel-polyfill:

require('@babel/polyfill')
或者
import '@babel/polyfill'

需要注意的是,babel-polyfill 不能多次引用。如果我們的代碼中有多個 require(’@babel/polyfill’),則執行時會報告錯誤:

only one instance of @babel/polyfill is allowed

這是因爲引入的 babel-polyfill 會在全局寫入一個 _babelPolyfill 變量。第二次引入時,會檢測該變量是否已存在,如果已存在,則拋出錯誤。

注意事項
如前面所說的,babel-polyfill 其實包含 regenerator runtime、core-js,如果你的代碼只需要其中一部分 polyfill,那麼你可以考慮直接引入 core-js 下的特定 polyfill,不必使用 babel-polyfill 這樣的龐然大物。

另一種辦法,是配合 @babel/preset-env 的 useBuiltIns 配置。

3.6 babel-runtime

3.6.1abel-runtimebabel-polyfill 的區別究竟是什麼?

babel-polyfillbabel-runtime 的一大區別,前者改造目標瀏覽器,讓你的瀏覽器擁有本來不支持的特性;後者改造你的代碼,讓你的代碼能在所有目標瀏覽器上運行,但不改造瀏覽器。
例如:
引入 babel-polyfill 後的 IE 11,你可以在 console 下執行 Object.assign({}, {})。引入 babel-runtime 後的 IE 11,console 下執行 Object.assign({}, {})仍然提示報錯

我們拿 Object.assign 爲例,剖析下 babel-polyfillbabel-runtime 的異同。
我們知道,IE 11 不支持 Object.assign,此時,我們有倆種候選方案:

  1. 引入 babel-polyfill,補丁一打,Object.assign 就被創造出來
  2. 配置 @babel/plugin-transform-object-assign

第二種方案中,babel 會將所有的 Object.assign 替換成 _extends 這樣一個輔助函數。如下所示:

Object.assign({}, {})

編譯成:

function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }

_extends({}, {});

問題是,如果你的項目裏有 100 個文件,其中有 50 個文件裏寫了 Object.assign,那麼,壞消息來了,_extends 輔助函數會出現 50 次。

怎麼辦?我們自然而然會想到把 _extends 分離出去,然後在每個文件中引入 - 這正是 @babel/runtime 的作用:

var _extends = require("@babel/runtime/helpers/extends");

_extends({}, {});

babel 提供了 @babel/plugin-transform-runtime 來替我們做這些轉換。

3.6.2 使用

$ npm install --save-dev @babel/plugin-transform-runtime
npm install @babel/runtime

.babelrc 中配置:

{
  "plugins": [
    "@babel/plugin-transform-object-assign",
    "@babel/plugin-transform-runtime"
  ]
}

這樣,我們不需要 babel-polyfill 也一樣可以在程序中使用 Object.assign,編譯後的代碼最終能夠正常運行在 IE 11 下。

3.7@babel/register

經過 babel 的編譯後,我們的源代碼與運行在生產下的代碼是不一樣的。

babel-register 則提供了動態編譯。換句話說,我們的源代碼能夠真正運行在生產環境下,不需要 babel 編譯這一環節。

我們先在項目下安裝 babel-register:

npm install --save-dev @babel/register

然後在入口文件中 require:

require("babel-register");
require("./index.js");

然後,就不需要手動對index.js轉碼了。

需要注意的是,babel-register只會對require命令加載的文件轉碼,而不會對當前文件轉碼。另外,由於它是實時轉碼,所以只適合在開發環境使用

3.8@babel-node

babel-cli工具自帶一個babel-node命令,提供一個支持ES6的REPL環境。它支持Node的REPL環境的所有功能,而且可以直接運行ES6代碼。

它不用單獨安裝,而是隨babel-cli一起安裝。然後,執行babel-node就進入PEPL環境。

$ babel-node
> (x => x * 2)(1)
2

babel-node命令可以直接運行ES6腳本。將上面的代碼放入腳本文件es6.js,然後直接運行。

$ babel-node es6.js
2

babel-node也可以安裝在項目中。

$ npm install --save-dev babel-cli

然後,改寫package.json

{
  "scripts": {
    "script-name": "babel-node script.js"
  }
}

請不要在生產環境中使用 babel-node,因爲它是動態編譯源代碼,應用啓動速度非常慢。

3.9syntax-dynamic-import

這個插件主要解決動態引入模塊的問題

function nDate() {
  import('moment').then(function(moment) {
    console.log(moment.default().format());
  }).catch(function(err) {
    console.log('Failed to load moment', err);
  });
}

nDate();

不然就會報以下錯誤:

Module build failed: SyntaxError: 'import' and 'export' may only appear at the top level, 
or (import 和 export只能在最外層,也就是不能用在函數或者塊中)

安裝:

npm install @babel-plugin-syntax-dynamic-import

使用:.babelrc


{
  "plugins": ["syntax-dynamic-import"]
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章