建議改爲: 讀完這篇你還不懂Babel我給你寄口罩

建議改爲: 讀完這篇你還不懂Babel我給你寄口罩

前言

最近在學習webpack, 發現了webpack中一個重要的功能點babel-loader, 因而就想着學習瞭解一波Babel.前端

咱們在作一件事, 學習一個知識點的時候, 都應該是抱有一個目的去作的.node

在你花了大把時間大把精力去學習這個知識的時候, 它能帶給你什麼 🤔️ ? 能幫助到你什麼🤔️ ?webpack

你到底有什麼軟用.jpg
你到底有什麼軟用.jpg

就像我學習Babel同樣, 以前一直只知道它是一個JS編譯器, 大概功能是能幫咱們在舊的瀏覽器環境中將ES6+代碼轉換成向後兼容版本的JS代碼, 可是其中重要的轉換功能是靠什麼實現, 以及裏面到底有個什麼學問是我沒深刻了解的, 它對我學習webpack有什麼幫助?es6

在這一篇文章中我並無介紹過於深刻的內容, 可是若是把它當成一個入門Babel的教材來看那我相信它對你是有必定幫助的. 不信若是你讀完了它以後再去看看官方的文檔, 必定以爲均可以看懂了. 否則的話請評論區留下你的地址, 看我給不給你寄口罩...web

不拐彎抹角了, 嘻嘻 😁, 讓咱們看看經過這一章節的閱讀你能學習到什麼:chrome

  • @babel/cli
  • plugins
  • presets
  • 配置Babel
  • polyfill

前期準備

學習一個新的知識, 我仍是偏向於用案例的的方式來打開講解它.npm

因此在正式開始閱讀以前, 讓咱們先來準備一個這樣的案例項目:json

mkdir babel-basic && cd babel-basic
npm init -y
mkdir src && cd src
touch index.js
複製代碼

一頓操做以後, 咱們新建的項目目錄爲:promise

/babel-basic
 |- /src
   |- index.js
 |- package.json
複製代碼

如今package.json是最原始的配置, 而index.js暫時沒有寫內容.瀏覽器

package.json:

{
  "name": "babel-basic",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {}
}

複製代碼

下面我都將圍繞這個babel-basic項目來進行講解, 我但願你也能在本地準備一個這樣的項目案例, 以便你更好的理解我接下來要說的內容.

@babel/core

咱們學習Babel, 首先要了解一個叫@babel/core 的東西, 它是Babel的核心模塊.

固然要使用它, 咱們得先安裝:

$ npm i --save-dev @babel/core
複製代碼

安裝成功以後就能夠在咱們的代碼中使用了, 你能夠採用CommonJS的引用方式:

const babel = require('@babel/core');
babel.transform("code", options);
複製代碼

這裏的知識點有不少, 不過你不用急於的掌握它, 只須要知道它是Babel的核心, 讓咱們接着往下看.

@babel/cli

再而後就是@babel/cli, 它是一個終端運行工具, 內置的插件,運行你從終端使用babel的工具.

一樣, 它也須要先安裝:

$ npm i --save-dev @babel/cli @babel/core
複製代碼

讓咱們安裝@babel/cli的同時再來安裝一下@babel/core,

如今, 讓我先在src/index.js中寫上一段簡單的代碼, 並來看看它的基本用法.

src/index.js:

const fn = () => 1; // 箭頭函數, 返回值爲1
console.log(fn());
複製代碼

用法一: 命令行的形式(在項目根目錄執行語句):

$ ./node_modules/.bin/babel src --out-dir lib
複製代碼

這段語句的意思是: 它使用咱們設置的解析方式來解析src目錄下的全部JS文件, 並將轉換後的每一個文件都輸出到lib目錄下.

可是注意了, 因爲咱們如今沒有設置任何的解析方式, 因此你在執行了這段語句以後, 能看到項目中多了一個lib目錄, 並且裏面的JS代碼和src中的是同樣的. 至於我說的解析方式, 就是後面我要介紹的plugins和presets.

另外, 若是你是npm@5.2.0附帶的npm包運行器的話, 就能夠用npx babel來代替./node_modules/.bin/babel:

$ npx babel src --out-dir lib
複製代碼

用法二: 給package.json中配置一段腳本命令:

{
    "name": "babel-basic",
    "version": "1.0.0",
    "description": "",
    "main": "index.js",
    "scripts": {
+ "build": "babel src -d lib"
    },
    "keywords": [],
    "author": "",
    "license": "ISC",
    "devDependencies": {
+ "@babel/cli": "^7.8.4",
+ "@babel/core": "^7.8.4"
    }
}
複製代碼

如今運行npm run build效果也是同樣的, -d--out-dir的縮寫...

(咱們使用上面的 --out-dir 選項。你能夠經過使用 --help 運行它來查看 cli 工具接受的其他選項。但對咱們來講最重要的是 --plugins--presets。)

$ npx babel --help
複製代碼

插件plugins

基本概念

知道了Babel的基本用法以後, 讓咱們來看看具體的代碼轉換.

如今要介紹的是插件plugins, 它的本質就是一個JS程序, 指示着Babel如何對代碼進行轉換.

因此你也能夠編寫本身的插件來應用你想要的任何代碼轉換.

插件案例(箭頭函數插件)

可是首先讓咱們來學習一些基本的插件.

若是你是要將ES6+轉成ES5, 能夠依賴官方插件, 例如:

@babel/plugin-transform-arrow-functions:

$ cnpm i --save-dev @babel/plugin-transform-arrow-functions
$ npx babel src --out-dir lib --plugins=@babel/plugin-transform-arrow-functions
複製代碼

這個插件的做用是將箭頭函數轉換爲ES5兼容的函數:

還記得咱們以前的src/index.js嗎:

const fn = () => 1; // 箭頭函數, 返回值爲1
console.log(fn());
複製代碼

如今編譯以後, 你再打開lib/index.js來看看.

它是否是被轉換爲ES5的代碼了呢? 😁

const fn = function () {
  return 1;
}; // 箭頭函數, 返回值爲1


console.log(fn());
複製代碼

搗鼓了這麼久, 終於看到了一點實際的效果, 此時有點小興奮啊😄

表情包開心

雖然咱們已經實現了箭頭函數轉換的功能, 可是ES6+其它的語法(比求冪運算符**)卻並不能轉換, 這是由於咱們只使用了@babel/plugin-transform-arrow-functions這個功能插件, 沒有使用其它的了.

Presets:

基本概念

若是想要轉換ES6+的其它代碼爲ES5, 咱們可使用"preset"來代替預先設定的一組插件, 而不是逐一添加咱們想要的全部插件.

這裏能夠理解爲一個preset就是一組插件的集合.

presets和plugins同樣, 也能夠建立本身的preset, 分享你須要的任何插件組合.

@babel/preset-env

例如, 咱們使用envpreset:

cnpm i --save-dev @babel/preset-env
複製代碼

envpreset這個preset包括支持現代JavaScript(ES6+)的全部插件.

因此也就是說你安裝使用了envpreset以後, 就能夠看到其它ES6+語法的轉換了.

如今讓咱們來用用ES7中的求冪運算符函數參數支持尾部逗號這兩個功能吧:

src/index.js:

const fn = () => 1; // ES6箭頭函數, 返回值爲1
let num = 3 ** 2; // ES7求冪運算符
let foo = function(a, b, c, ) { // ES7參數支持尾部逗號
    console.log('a:', a)
    console.log('b:', b)
    console.log('c:', c)
}
foo(1, 3, 4)
console.log(fn());
console.log(num);
複製代碼

而後在命令行裏使用這個preset:

npx babel src --out-dir lib --presets=@babel/preset-env
複製代碼

如今打開lib/src看看:

"use strict";

var fn = function fn() { return 1; }; // 箭頭函數, 返回值爲1

var num = Math.pow(3, 2);

var foo = function foo(a, b, c) { console.log('a:', a); console.log('b:', b); console.log('c:', c); };

複製代碼foo(1, 3, 4); console.log(fn()); console.log(num); 複製代碼

求冪運算符被轉換爲成Math.pow()

函數參數的最後一個逗號也被去掉了.

截止到如今, 看完了@babel/core@babel/clipluginspresets, 相信你對Babel的功能有必定了解了吧, 可是真正使用起來咱們不可能都是靠命令行的形式吧, 沒錯, 接下來我要將這些功能作成配置項.

配置

上面👆介紹的都是一些終端傳入CLI的方式, 在實際使用上, 咱們更加偏向於配置文件.

例如咱們在項目的根目錄下建立一個babel.config.js文件:

const presets = [
	[
    "@babel/env",
    {
      targets: {
        edge: "17",
        chrome: "64",
        firefox: "60",
        safari: "11.1"
      }
    }
  ]	
]

module.exports = { presets };
複製代碼

加上這個配置的做用是:

  • 使用了 envpreset這個preset
  • envpreset只會爲目標瀏覽器中沒有的功能加載轉換插件

如今你要使用這個配置就很簡單了, 直接用咱們前面package.json配置的命令行語句:

{
	"scripts": {
		"build": "babel src -d lib"
	}
}
複製代碼

執行npm run build就能夠了.

這個命令行語句看起來並無修改, 那是由於它默認會去尋找跟根目錄下的一個名爲babel.config.js的文件(或者babelrc.js也能夠, 這個在以後的使用babel的幾種方式中會說到), 因此其實就至關於如下這個配置:

{
	"scripts": {
		"build": "babel src -d lib --config-file ./babel.config.js"
	}
}
複製代碼

所以若是你的Babel配置文件是babel.config.js的話, 這兩種效果是同樣的.

(--config-file指令就相似於webpack中的--config, 用於指定以哪一個配置文件構建)

這裏我重點要說一下只會爲目標瀏覽器中沒有的功能加載轉換插件這句話的意思.

例如我這裏配置的其中一項是edge: "17", 那就表示它轉換以後的代碼支持到edge17.

因此你會發現, 若是你用了我上面babel.config.js的配置以後生成的lib文件夾下的代碼好像並無發生什麼改變, 也就是它並無被轉換成ES5的代碼:

src/index.js:

const fn = () => 1; // ES6箭頭函數, 返回值爲1
let num = 3 ** 2; // ES7求冪運算符
let foo = function(a, b, c, ) { // ES7參數支持尾部逗號
    console.log('a:', a)
    console.log('b:', b)
    console.log('c:', c)
}
foo(1, 3, 4)
console.log(fn());
console.log(num);
複製代碼

使用babel.config.js配置以後構建的lib/index.js:

"use strict";

const fn = () => 1; // ES6箭頭函數, 返回值爲1


let num = 3 ** 2; // ES7求冪運算符

let foo = function foo(a, b, c) {
  // ES7參數支持尾部逗號
  console.log('a:', a);
  console.log('b:', b);
  console.log('c:', c);
};

foo(1, 3, 4);
console.log(fn());
console.log(num);
複製代碼

箭頭函數依舊是箭頭函數, 求冪運算符依舊是求冪運算符.

這是由於在Edge17瀏覽器中支持ES7的這些功能, 因此它就沒有必要將其轉換了, 它只會爲目標瀏覽器中沒有的功能加載轉換插件!!!

若是咱們將edge17改爲edge10看看 🤔️?

babel.config.js:

const presets = [
    [
        "@babel/env",
        {
            targets: {
- edge: "17",
+ edge: "10",
                firefox: "60",
                chrome: "67",
                safari: "11.1",
            },
        },
    ],
];

module.exports = { presets };
複製代碼

保存從新運行npm run build, 你就會發現lib/index.js如今有所改變了:

"use strict";

var fn = function fn() {
  return 1;
}; // ES6箭頭函數, 返回值爲1


var num = Math.pow(3, 2); // ES7求冪運算符

var foo = function foo(a, b, c) {
  // ES7參數支持尾部逗號
  console.log('a:', a);
  console.log('b:', b);
  console.log('c:', c);
};

foo(1, 3, 4);
console.log(fn());
console.log(num);
複製代碼

Polyfill

Plugins是提供的插件, 例如箭頭函數轉普通函數@babel/plugin-transform-arrow-functions

Presets是一組Plugins的集合.

而Polyfill是對執行環境或者其它功能的一個補充.

什麼意思呢 🤔️?

就像如今你想在edge10瀏覽器中使用ES7中的方法includes(), 可是咱們知道這個版本的瀏覽器環境是不支持你使用這個方法的, 因此若是你強行使用並不能達到預期的效果.

polyfill的做用正是如此, 知道你的環境不容許, 那就幫你引用一個這個環境, 也就是說此時編譯後的代碼就會變成這樣:

// 原來的代碼
var hasTwo = [1, 2, 3].includes(2);

// 加了polyfill以後的代碼
require("core-js/modules/es7.array.includes");
require("core-js/modules/es6.string.includes");
var hasTwo = [1, 2, 3].includes(2);
複製代碼

這樣說你應該就能看懂它的做用了吧 😁

表情包裝逼

如今就讓咱們來學習一個重要的polyfill, 它就是babel/polyfill.

babel/polyfill用來模擬完成ES6+環境:

  • 可使用像 Promise或者 WeakMap這樣的新內置函數
  • 可使用像 Array.from或者 Object.assign這樣的靜態方法
  • 可使用像 Array.prototype.includes這樣的實例方法
  • 還有 generator函數

爲了實現這一點, Polyfill增長了全局範圍以及像String這樣的原生原型.

@babel/polyfill模塊包括了core-js和自定義regenerator runtime

對於庫/工具來講, 若是你不須要像Array.prototype.includes這樣的實例方法, 可使用transform runtime插件, 而不是使用污染全局的@babel/polyfill.

對於應用程序, 咱們建議安裝使用@babel/polyfill

cnpm i --save @babel/polyfill
複製代碼

(注意 --save 選項而不是 --save-dev,由於這是一個須要在源代碼以前運行的 polyfill。)

可是因爲咱們使用的是envpreset, 這裏個配置中有一個叫作 "useBuiltIns"的選項

若是將這個選擇設置爲"usage", 就只包括你須要的polyfill

此時的babel.config.js調整爲:

const presets = [
	[
		"@babel/env",
		{
			targets: {
				edge: "17",
				chrome: "64",
				firefox: "67",
				safari: '11.1'
			},
+ useBuiltIns: "usage"
		}
	]
]

module.exports = { presets }
複製代碼

安裝配置了@babel/polyfill, Babel將檢查你的全部代碼, 而後查找目標環境中缺乏的功能, 並引入僅包含所需的polyfill

(若是咱們沒有將 env preset 的 "useBuiltIns" 選項的設置爲 "usage" ,就必須在其餘代碼以前 require 一次完整的 polyfill。)

仍是上面👆的那個例子, 咱們來改造一下, 使用Edge17中沒有的Promise.prototype.finally:

src/index.js:

const fn = () => 1; // ES6箭頭函數, 返回值爲1
let num = 3 ** 2; // ES7求冪運算符
let hasTwo = [1, 2, 3].includes(2)
let foo = function(a, b, c, ) { // ES7參數支持尾部逗號
    console.log('a:', a)
    console.log('b:', b)
    console.log('c:', c)
}
foo(1, 3, 4)
Promise.resolve().finally();
console.log(fn());
console.log(num);
console.log(hasTwo);
複製代碼

如今執行npm run build以後生成的lib/index.js變成了:

"use strict";

require("core-js/modules/es7.promise.finally");

const fn = () => 1; // ES6箭頭函數, 返回值爲1


let num = 3 ** 2; // ES7求冪運算符

let hasTwo = [1, 2, 3].includes(2);

let foo = function foo(a, b, c) {
  // ES7參數支持尾部逗號
  console.log('a:', a);
  console.log('b:', b);
  console.log('c:', c);
};

foo(1, 3, 4);
Promise.resolve().finally();
console.log(fn());
console.log(num);
console.log(hasTwo);
複製代碼

@babel/polyfill幫咱們引入了Edge17 環境中沒有的promise.finally()

小結

  • babel/cli 容許咱們從終端運行Babel
  • envpreset 只包含咱們使用的功能的轉換,實現咱們的目標瀏覽器中缺乏的功能
  • @babel/polyfill實現全部新的 JS功能, 爲目標瀏覽器引入缺乏的環境

後語

哈哈😄, 很差意思開頭騙了你們...寄口罩不存在的 😂 我本身也是被關在家裏不敢出門...

看我爲了能讓你們老實呆家學習多費心啊 😂 (不要臉了一波)

最後...

喜歡霖呆呆的小夥還但願能夠關注霖呆呆的公衆號 LinDaiDai 或者掃一掃下面的二維碼👇👇👇.

LinDaiDai公衆號二維碼.png
LinDaiDai公衆號二維碼.png

我會不定時的更新一些前端方面的知識內容以及本身的原創文章🎉

你的鼓勵就是我持續創做的主要動力 😊.

相關推薦:

《JavaScript進階-執行上下文(理解執行上下文一篇就夠了)》

《全網最詳bpmn.js教材》

《霖呆呆你來講說瀏覽器緩存吧》

《怎樣讓後臺小哥哥快速對接你的前端頁面》

相關文章
相關標籤/搜索