[Babel]Babel學習手記

1、Babel相關的概念

  • @babel/core: babel的核心,核心的api都在包含在這裏。
  • @babel/cli: 命令行工具,經過命令對js文件進行轉換的工具。
  • @babel/perset-env: 指定轉換的工做環境。
  • @babel/polyfill: 至關於一個填充,由於babel自己只支持轉換箭頭函數、結構賦值這些語法糖類的語法,而一些新的API或者Promise函數等是沒法轉換的。@babel/polyfill就是解決這個問題的。
  • babel-loader: webpack的加載器,用於調用@babel/core的核心API來完成編譯。

一、@babel/core與babel-core區別html

@babel/core是babel 7事後的版本標識,babel-core是之前版本的標識。node


二、.babelrc和babel.config.jswebpack

.babelrc和babel.config.js均是babel的配置文件,babel.config.js是bebel 7引入的新的方式。git


三、@babel/polyfill和@babel/plugin-transform-runtime和@babel/runtime和@babel/runtime-corejs2(都是用來轉換新Api的)es6

  • (1)@babel/polyfill:babel-polyfill則是經過改寫全局prototype的方式實現,比較適合單獨運行的項目。開啓babel-polyfill的方式,能夠直接在代碼中require,或者在webpack的entry中添加,也能夠在babel的env中設置useBuildins爲true來開啓,可是babel-polyfill會有近100K,打包後代碼冗餘量比較大,對於現代的瀏覽器,有些不須要polyfill,形成流量浪費污染了全局對象。
//使用方法一在entry中添加
module.exports = {
    entry: {
        main: ["@babel/polyfill", path.resolve(__dirname, "./src/index.js")]
    }
}
//使用方式二babel.config.js中設置
module.exports = {
    presets: [
        "@babel/preset-env", { "useBuiltIns": true }
    ]
}
複製代碼

  • (2)@babel/runtime和@babel/plugin-transform-runtime:babel-runtime和 babel-plugin-transform-runtime的區別是,至關一前者是手動擋然後者是自動擋,每當要轉譯一個api時都要手動加上require('babel-runtime'),而babel-plugin-transform-runtime會由工具自動添加,主要的功能是爲api提供沙箱的墊片方案,不會污染全局的api,所以適合用在第三方的開發產品中。@babel/runtime和@babel/plugin-transform-runtime都要結合到使用

  • (3)@babel/runtime-corejs2:plugin-transform-runtime 能夠設置成 false 或者 2,在babel.config.js的配置文件下有如下代碼:
module.exports = {
    preset: ["@babel/preset-env"],
    plugins: [
        "@babel/plugin-transform-runtime", { corejs: 2 }
    ]
}
複製代碼

一、corejs 是一個給低版本的瀏覽器提供接口的庫,如 Promise, map, set 等。在 babel 中你設置成 false 或者不設置,就是引入的是 corejs 中的庫,並且在全局中引入,也就是說侵入了全局的變量。github

二、若是你的全局有一個引入,不要讓引入的庫影響全局,那你就須要引把 corejs 設置成 2。 因此一旦你使用了2這個參數就必須引入@babel/runtime-corejs2web

三、@babel/plugin-transform-runtime是必須裝的,若是corejs設置爲2的話安裝@babel/runtime-corejs2來代替@babel/runtime,反正設置爲false的話就須要@babel/runtime,根據你的設置來安裝便可。npm

四、因此@babel/runtime和@babel/runtime-corejs2區別就是後面那個多了個corejs2的包。與@babel/runtime區別,官網是這樣描述的Difference from @babel/runtime:This can be used instead of a polyfill for any non-instance methods. It will replace things like Promise or Symbol with the library functions in core-jsjson


在babel 7下:segmentfault

  • babel.config.js 是對整個項目(父子package) 都生效的配置,但要注意babel的執行工做目錄。
  • .babelrc 是對 待編譯文件 生效的配置,子package若想加載.babelrc是須要babel配置babelrcRoots才能夠(父package自身的babelrc是默承認用的)。
  • 任何package中的babelrc尋找策略是: 只會向上尋找到本包的 package.json 那一級。
  • node_modules下面的模塊通常都是編譯好的,請剔除掉對他們的編譯。若有須要,能夠把個例加到 babelrcRoots 中。

默認狀況下.babelrc不做用於子包,那麼在babel.config.js下加入一下babelrcRoots來指定便可。

module.exports = {
      babelrcRoots: ['.', './frontend', './backend'] // 容許這兩個子 package 加載 babelrc 相對配置
    }
複製代碼

一文讀懂 babel7 的配置文件加載邏輯
對babel-transform-runtime,babel-polyfill的一些理解
babel7中 corejs 和 corejs2 的區別
babel preset env配置
babel學習筆記

2、Babel配置入門指南

首先須要本機安裝node.js,使用npm包管理工具來初始化目錄,本次操做學習都是在Babel 7上進行的,相較於Babel 6仍是有必定區別,單獨使用Babel 7須要CLI來完成,因此先安裝腳手架和核心@babel/core。

npm install @babel/cli @babel/core
複製代碼

安裝完成後,咱們就可使用cli提供的命令來轉換咱們的JS代碼了,好比創建一個test.js的JS文件,裏面包含ES6的內容:

let a = [1, 2, 3];

let b = () => {
	console.log('這是箭頭函數!');
}

let c = [...a];
複製代碼

而後來一波命令:

npx babel test.js --watch --out-file test-transform.js
複製代碼
  • 注意:npx是npm在5.2.0後附贈的東西,使用這個咱們能夠避免全局安裝這些命令工具,具體你們能夠百度。
  • 個人test.js是直接建在跟package.json同級目錄下的。 以後咱們能夠發現輸出的test-transform.js沒改變,沒有轉化,這是由於babel是基於插件來實現的,沒配置插件確定什麼也不會幹,因此須要配置,這裏就不介紹單獨手動引入插件的方式了。
// 轉換前
let a = [1, 2, 3];

let b = () => {
	console.log('這是箭頭函數!');
}

let c = [...a];

// 轉換後
let a = [1, 2, 3];

let b = () => {
	console.log('這是箭頭函數!');
}

let c = [...a];
複製代碼

不想每次須要什麼插件都去手動引入插件,因此咱們就須要祭出@babel/preset-env,先安裝這個工具。

npm intasll @babel/preset-env
複製代碼

而後在根目錄建一個babel.config.js來配置babel,在文件中加入如下內容:

module.exports = {
	presets: [
		["@babel/preset-env"]
	]
}
複製代碼

以後咱們再運行一次上面的轉換命令,能夠獲得如下代碼:

"use strict";

var a = [1, 2, 3];

var b = function b() {
  console.log('這是箭頭函數!');
};

var c = [].concat(a);
複製代碼

這樣ES6新的語法糖就轉換成ES5了。 固然,babel.config.js裏面還能夠指定目標,當知足什麼樣的條件纔去轉換語法,不指定targets的狀況下,默認是把全部的ES6+都轉換成ES5,好比下面的示例:

module.exports = {
  presets: [
    ["@babel/preset-env", {
      "targets": "ie >= 8"
    }]
  ]
}
複製代碼

這種只有當在大於ie 8以上的瀏覽器不支持的語法纔會轉換。

@babel/polyfill

babel只能轉換通常的語法糖,不能轉換新的API,因此就只能祭出polyfill來彌補。首先安裝下polyfill,而後引入就能夠了。

npm install @babel/polyfill
複製代碼

而後在JS文件裏面引入

import "@babel/polyfill";
複製代碼

對於使用webpack的同窗,能夠直接在main入口直接引入,打包事後直接可使用,以下:

entry: {
  main: ["@babel/polyfill", path.resolve(__dirname, "../main.js")]
}
複製代碼
  • 注意:這個地方我只是舉個栗子,實際上你這樣單獨操做出來的JS再直接引入某個HTML文件裏面實際上是沒有用的,要結合到打包工具一塊兒使用才行,好比Webpack。

@babel/preset-env 增強

上面的引入方法是徹底引入,致使包很是大,咱們能夠按需引入,這裏又要配置@babel/preset-env,修改babel.config.js代碼以下:

module.exports = {
  presets: [
    ["@babel/preset-env", {
      "targets": "ie >= 8",
      "useBulitIns": "entry"
    }]
  ]
}
複製代碼

而後運行一下會看到這些代碼:

"use strict";

require("core-js/modules/es6.array.copy-within");

require("core-js/modules/es6.array.every");

require("core-js/modules/es6.array.fill");
..........
require("core-js/modules/web.timers");

require("core-js/modules/web.immediate");

require("core-js/modules/web.dom.iterable");

require("regenerator-runtime/runtime");

var a = [1, 2, 3];

var b = function b() {
  console.log('這是箭頭函數!');
};

var c = [].concat(a);
b();
var d = new Promise(function (resolve, reject) {
  setTimeout(function () {
    resolve("ok");
  }, 2000);
});
d.then(function (res) {
  console.log(res);
});
複製代碼

它會一股腦的把全部的包所有引進來,這樣嘿不科學,因此usage參數能夠作到按需引入,它只會引入相關的包,沒使用的ES6+API不會引入相關的包,修改babel.config.js代碼:

module.exports = {
  presets: [
    ["@babel/preset-env", {
      "targets": "ie >= 8",
      "useBuiltIns": "usage"
    }]
  ]
}
複製代碼

轉換事後的代碼以下:

"use strict";

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

require("core-js/modules/es6.object.to-string");

var a = [1, 2, 3];

var b = function b() {
  console.log('這是箭頭函數!');
};

var c = [].concat(a);
b();
var d = new Promise(function (resolve, reject) {
  setTimeout(function () {
    resolve("ok");
  }, 2000);
});
d.then(function (res) {
  console.log(res);
});

複製代碼

這樣,咱們能夠看到,個人代碼使用了promise,它就只引入promise相關的代碼。

  • 注意:這兒使用usage參數後,JS文件裏面就不要單獨寫import "@babel/polyfill"這句話了,畫蛇添足。

到這兒,問題來了,在使用usage參數後使用babel的cli命令工具(也就是這個:npx babel test.js --watch --out-file test-done.js)會給一段提示,告訴咱們要指定core-js@2或者core-js@3,從babel 7.4事後官方就建議這麼作了,就是讓咱們放棄@babel/polyfill,我先卸載掉@babel/polyfill試一試,發現仍是能轉換,不科學,而後查資料和看preset-env文件夾下才發現,人家自帶了polyfill,因此這兒若是有了@babel/preset-env不須要單獨安裝@babel/polyfill了,前提應該是使用了@babel/preset-env配置了babel才行(像前面的徹底引入polyfill仍是須要單獨安裝@babel/polyfill),(注意:這兒我試了使用webpack結合babel-loader在不單獨安裝@babel/polyfill或者core-js這些打包會出問題,"useBuiltIns": "usage"時提示找不到包,使用entry參數打包不報錯,直接不會導入包,因此環境不同不能一律而論)前面有個要咱們指定corejs的提示,也只須要在babel.config.js裏面指定一下就能夠了,這兒個人@babel/perset-env版本是7.6.3,不知道其它版本有沒有集成polyfill,babel.config.js代碼以下:

module.exports = {
  presets: [
    ["@babel/preset-env", {
      "targets": "ie >= 8",
      "useBuiltIns": "usage",
      "corejs": 2
    }]
  ]
}
複製代碼

這兒我測試了下指定corejs爲2和3時引入的代碼不同,暫時沒了解到緣由-_-。

@babel/runtime、 @babel/plugin-transform-runtime

接下來走一波不污染全局的配置方式,前面的配置都是要污染全局,仍是不怎麼科學,關於這種方式的優勢和缺點你們上網查一下就能夠了,接下來先安裝好包:

npm install @babel/plugin-transform-runtime -D
複製代碼

上面這個安裝到dev依賴就能夠了,不用於生產環境

npm install @babel/runtime
複製代碼

接下來咱們配置一下babel.config.js,以下:

module.exports = {
  presets: [
    ["@babel/preset-env", {
      "targets": "ie >= 8",
    }]
  ],
  plugins: [
    ["@babel/plugin-transform-runtime"]
  ]
}
複製代碼

而後輸出一下

"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");

var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));

var a = [1, 2, 3];

var b = function b() {
  console.log('這是箭頭函數!');
};

var c = [].concat(a);
b();
var d = new Promise(function (resolve, reject) {
  setTimeout(function () {
    resolve("ok");
  }, 2000);
});
d.then(function (res) {
  console.log(res);
});

var MM = function MM() {  // 這個是我轉換前用class定義的類 class MM {constructor(){}} 這種
  (0, _classCallCheck2["default"])(this, MM);
};
複製代碼

發現輸出的代碼裏面沒得polyfill了。若是須要polyfill的話就要單獨安裝如下內容:

npm install @babel/runtime-corejs2
複製代碼

這裏安裝這個包以前把我@babel/runtime給卸載了,安裝完成後再從新配置一下babel.config.js,以下:

module.exports = {
  presets: [
    ["@babel/preset-env", {
      "targets": "ie >= 8",
    }]
  ],
  plugins: [
    ["@babel/plugin-transform-runtime", {
      "corejs": 2
    }]
  ]
}
複製代碼

而後運行一下,看結果。相比前面的@babel/runtime,這裏的polyfill回來了,多了一句這個

var _promise = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/promise"));
複製代碼

參考資料

babel-runtime VS babel-polyfill
結合Babel 7.4.0 談一下Babel-runtime 和 Babel-polyfill

20191107更新

3、結合到webpack來使用

結合webpack的話,還須要如下幾個東西:

npm install webpack -D
npm install webpack-cli -D
npm install babel-loader -D
複製代碼

babel-loader是加載器,要結合webpack來使用的話,這個必需要,同時,在項目根目錄創建一個webpack.config.js配置文件(這裏webpack的知識就不介紹了,詳情參考官網)。

仍是藉助@babel/preset-env

首先我仍是先測試了參數爲entry的狀況,配置文件內容以下(注意:這兒我還安裝了@babel/polyfill):

// index.js,是我要轉換的文件
import '@babel/polyfill';

let a = `hello, can you hear me!`;

let func = () => {
    console.log('這是箭頭函數');
}

let b = [1, 2, 3];
let c = [...b];

let array = [1];
 
 
console.log(array);

let promise_t = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(233);
    }, 2000)
});

promise_t.then((res) => {
    console.log(res);
})

class Test {
    constructor(name) {
        this.name = name;
    }

    print() {
        console.log(this.name);
    }
}

let t_t = new Test('xiaohong');
t_t.print();
複製代碼

而後是webpack.config.js文件

const path = require('path');

module.exports = {
    // 指定模式(這個指定能夠用來區分開發模式和生產模式,可選值有:'devvlopment','production','none')
    mode: 'none',
    // 入口
    entry: {
        main: path.resolve(__dirname, './src/index.js')
    },

    // 出口
    output: {
        // 出口文件
        path: path.resolve(__dirname, './dist'),
        // 文件名
        filename: './[name].js',
    },

    // 加載器
    module: {
        rules: [
            {
                test: /\.jsx?$/,
                exclude: /node_modules/,
                use: [
                    {
                        loader: 'babel-loader'
                    }
                ]
            }
        ]
    }
}
複製代碼

以後再是babel.config.js文件

module.exports = {
    presets: [
        ['@babel/preset-env', {
            'useBuiltIns': 'entry',
        }]
    ]
}
複製代碼

這兒我發現,即便個人babel.config.js不配置'useBuiltIns': 'entry',這句,打包出來的main.js雖然內容有差異,可是仍是能運行成功,IE11親測可用上面js的promise語法。暫時我還沒把useBuiltIns這個屬性搞的特別清楚,只是知道在使用usage屬性值時能夠按需加載polyfill。 接下來使用'useBuiltIns': 'usage'來試一試,我發如今保持其餘文件不變的狀況下,(這兒有個問題是,使用usage時在index.js中仍是要寫這句import '@babel/polyfill',否則找不到包,這與我上面單獨使用babel來轉換時說不在單獨引入polyfill有點出入,還沒怎麼明白,後期我會補充)。而後我比較了兩種方式生成的main.js的代碼,使用entry屬性值時,main.js代碼八千多行,大小大概250kb,使用usage參數時,代碼一千多行,大小大概37kb,因此按需引入效果仍是明顯。並且親測在IE11上兩種方式生成的代碼都可正常運行。

使用core-js代替@babel/polyfill

官方網站上面說從babel 7.4.0後開始放棄babel/polyfill,轉而使用core-js。這兒咱們又來試一試core-js 首先卸載@babel/polyfill,而後執行如下命令安裝core-js@2:

npm install core-js@2 -S
複製代碼

這兒還有個core-js@3的版本,暫時沒有用過,不知道區別。安裝完成後修改一下babel.config.js

module.exports = {
    presets: [
        ['@babel/preset-env', {
            'useBuiltIns': 'usage',
            'corejs': 2
        }]
    ]
}
複製代碼

而後你的index.js的上面去掉import '@babel/polyfill',這時執行事後的main.js能完美運行在IE11上,並且這兒也不須要手動引入core-js@2,使用usage屬性值時自動按需引入,比較穩。若是我不寫'corejs': 2這個參數,轉換也能夠成功,可是會報warning,叫我去安裝core-js並制定版本,問題不大,仍是寫上,畢竟官方要求。 而後若是我直接把useBuiltIns的參數值改成entry,發現class這些es6語法會轉換,可是Promise的polyfill沒有引入,看來沒usage參數好使,須要手動引入才行,我在index.js手動引入import 'core-js', 而後執行webpack可是卻報錯,提示Module not found: Error: Can't resolve 'regenerator-runtime/runtime',這個就尷尬了,還要安裝regenerator-runtime/runtime,可是使用usage卻沒有出現這個問題,暫時無解,這兒我單獨安裝一次:

npm install regenerator-runtime -D
複製代碼

這兒我單獨安裝一次regenerator-runtime就不會報錯了,暫時沒找到緣由,試着在安裝了regenerator-runtime的狀況下使用usage參數值,同時去除index.js裏面的import 'core-js'這句話,否則會報警告。內容像這樣:When setting useBuiltIns: 'usage', polyfills are automatically imported when needed. Please remove the import '@babel/polyfill' call or use useBuiltIns: 'entry' instead.。我試了下,使用webpack打包沒得問題,能夠運行。這兒建議把regenerator-runtime安裝一次,避免沒必要要的麻煩。

  • 這裏我總結下吧(這兒總結的地方我都沒有顯示的指定corejs參數,也就是上面代碼的‘corejs’: 2這句話) 一、不安裝core-js和regenerator-runtime,不論你的@babel/preset-env的useBuiltIns設置爲哪一個參數值都不能打包成功,usage參數值提示找不到core-js的包,entry直接不會引入相關的東西,由於包都沒有,會提示你去下載corejs。false效果跟entry同樣。 二、只安裝了core-js(這兒安裝的core-js@2版本),usage參數能夠打包成功,可是提示指定core-js版本,這個正常,由於我沒寫那句'corejs: 2'了,測試在IE11上正常運行。entry參數須要手動在index.js中引入core-js,打包提示Can't resolve 'regenerator-runtime/runtime',打包失敗,false參數打包成功,可是文件巨大,代碼九千多行-_-,IE11運行成功。 三、安裝core-js和regenerator-runtime,usage參數能夠打包成功,可是提示指定core-js版本,這個正常,由於我沒寫那句'corejs: 2'了,測試在IE11上正常運行。entry參數能夠打包成功,可是提示指定core-js版本,這個正常,由於我沒寫那句'corejs: 2'了,測試在IE11上正常運行,打包事後文件巨大,我安裝了regenerator-runtime,沒有在index.js裏面手動引入,只引入了core-js,此次打包沒報上面找不到包的錯誤。false參數能夠打包成功,可是提示指定core-js版本,這個正常,由於我沒寫那句'corejs: 2'了,測試在IE11上正常運行,打包事後文件巨大。

因此我得出一個配置結果 在babel.config.js裏面這樣寫:

module.exports = {
   presets: [
       ['@babel/preset-env', {
           'useBuiltIns': 'usage',
           'corejs': 2
       }]
   ]
}
複製代碼

package.json裏面包含這些包:

{
  "name": "webpack-babel",
  "version": "1.0.0",
  "description": "practice",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack --config webpack.config.js"
  },
  "author": "lee",
  "license": "MIT",
  "devDependencies": {
    "@babel/core": "^7.7.0",
    "@babel/preset-env": "^7.7.1",
    "babel-loader": "^8.0.6",
    "regenerator-runtime": "^0.13.3",
    "webpack": "^4.41.2",
    "webpack-cli": "^3.3.9"
  },
  "dependencies": {
    "core-js": "^2.6.10"
  }
}
複製代碼

core-js和regenerator-runtime(這個爲了不entry參數時包錯找不到包)都安裝起。這樣就能夠了。

使用@babel/plugin-transform-runtime和@babel/runtime-corejs2來實現API的polyfill

前面使用了@babel/preset-env的useBuiltIns來實現API填充,這兒試一試另外一種方案,首先卸載掉core-js和regenerator-runtime,而後安裝一下兩個包

npm install @babel/plugin-transform-runtime -D
複製代碼
npm install @babel/runtime-corejs2 -S
複製代碼

安裝完成後配置一下babel.config.js

module.exports = {
    presets: [
        ['@babel/preset-env']
    ],
    plugins: [
        ['@babel/plugin-transform-runtime', {
            'corejs': 2
        }]
    ]
}
複製代碼

注意:這兒我是安裝的@babel/runtime-corejs2,因此「'corejs': 2」這個參數不能少,否者會報 Can't resolve '@babel/runtime/helpers/createClass'這種錯誤,上面這種方式不會污染全局,適合第三方開發。

4、總結

暫時babel就總結到這兒了,順便熟悉了一下webpack,若有不許確的地方還請各位多多指正,文章裏面還有幾個沒理解清楚的地方,後期再琢磨琢磨!這裏附上github的地址供你們參考:地址

相關文章
相關標籤/搜索