亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

用了babel還需要polyfill嗎原理解析

 更新時(shí)間:2023年02月05日 15:03:49   作者:wens  
這篇文章主要為大家介紹了用了babel是否還需要polyfill的原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

引言

前兩天一個(gè)同事跟我說了這么一個(gè)面試題,面試官上來就問他:“項(xiàng)目中用了babel還需要polyfill嗎?” 開始他的內(nèi)心是懵比的,怎么還有如此不按套路出牌的問題,按照面試的基本原則,答案一定是需要的,不然還怎么往下問啊。于是他說“要的”。當(dāng)面試官深挖下去的時(shí)候他終于頂不住了。 其實(shí)平時(shí)開發(fā)的過程中用的大部分都是現(xiàn)成的腳手架,很少會(huì)仔細(xì)看babel的配置,更不會(huì)深挖babel的用法。那么今天我就來給大家好好回答一下這個(gè)問題。

首先說明:我們后面說的babel都是基于7.10.0這個(gè)版本。

啥是Babel

甩出中文官方文檔的定義

Babel 是一個(gè)工具鏈,主要用于將 ECMAScript 2015+ 版本的代碼轉(zhuǎn)換為向后兼容的 JavaScript 語法,以便能夠運(yùn)行在當(dāng)前和舊版本的瀏覽器或其他環(huán)境中。 下面列出的是 Babel 能為你做的事情:

  • 語法轉(zhuǎn)換
  • 通過 Polyfill 方式在目標(biāo)環(huán)境中添加缺失的特性 (通過 @babel/polyfill 模塊)
  • 源碼轉(zhuǎn)換 (codemods)
  • 更多! (查看這些 視頻 獲得啟發(fā))

看完官方定義之后大家是不是覺得babel一個(gè)人就能把向后兼容的事情都做完了(不得不說確實(shí)有點(diǎn)誤導(dǎo)),其實(shí)根本不是這樣的。

真實(shí)的情況是babel只是提供了一個(gè)“平臺(tái)”,讓更多有能力的plugins入駐我的平臺(tái),是這些plugins提供了將 ECMAScript 2015+ 版本的代碼轉(zhuǎn)換為向后兼容的 JavaScript 語法的能力。

那么他是咋做到的呢?這就不得不提大名鼎鼎的AST了。

Babel 工作原理

babel的工作過程分為三個(gè)階段:parsing(解析)、transforming(轉(zhuǎn)化)、printing(生成)

  • parsing階段babel內(nèi)部的 babylon 負(fù)責(zé)將es6代碼進(jìn)行語法分析和詞法分析后轉(zhuǎn)換成抽象語法樹
  • transforming階段內(nèi)部的 babel-traverse 負(fù)責(zé)對(duì)抽象語法樹進(jìn)行變換操作
  • printing階段內(nèi)部的 babel-generator 負(fù)責(zé)生成對(duì)應(yīng)的代碼

其中第二步的轉(zhuǎn)化是重中之重,babel的插件機(jī)制也是在這一步發(fā)揮作用的,plugins在這里進(jìn)行操作,轉(zhuǎn)化成新的AST,再交給第三步的babel-generator。 所以像我們上面說的如果沒有這些plugins進(jìn)駐平臺(tái),那么babel這個(gè)“平臺(tái)”是不具備任何能力的。就好像這樣:

const babel = code => code;

因此我們可以有信心的說出那個(gè)答案“需要”。不僅需要polyfill,還需要大量的plugins。

下面我們通過例子來說明,以及還需要哪些plugins才能將 ECMAScript 2015+ 版本的代碼完美的轉(zhuǎn)換為向后兼容的 JavaScript 語法。

preset-env, polyfill, plugin-transform-runtime 區(qū)別

現(xiàn)在我們通過 npm init -y 來創(chuàng)建一個(gè)例子,然后安裝 @babel/cli 和 @babel/core。 通過命令 babel index.js --out-file compiled.js 把 index 文件用 babel 編譯成compiled.js

// index.js
const fn = () => {
  console.log("wens");
};
const p = new Promise((resolve, reject) => {
  resolve("wens");
});
const list = [1, 2, 3, 4].map(item => item * 2);

不加任何plugins

為了印證上面的說法,我們首先測試不加任何plugins的情況,結(jié)果如下

//compiled.js
const fn = () => {
  console.log("wens");
};
const p = new Promise((resolve, reject) => {
  resolve("wens");
});
const list = [1, 2, 3, 4].map(item => item * 2);

編譯好的文件沒有任何變化,印證了我們上面的說法。接下來我們加入 plugins。

在加入plugins測試之前我們需要知道一些前置知識(shí),babel將ECMAScript 2015+ 版本的代碼分為了兩種情況處理:

  • 語法層: let、const、class、箭頭函數(shù)等,這些需要在構(gòu)建時(shí)進(jìn)行轉(zhuǎn)譯,是指在語法層面上的轉(zhuǎn)譯
  • api方法層:Promise、includes、map等,這些是在全局或者Object、Array等的原型上新增的方法,它們可以由相應(yīng)es5的方式重新定義

babel對(duì)這兩種情況的轉(zhuǎn)譯是不一樣的,我們需要給出相應(yīng)的配置。

加入preset-env

上面的例子種const,箭頭函數(shù)屬于語法層面的,而promise和map屬于api方法層面的,現(xiàn)在我們加入 preset-env 看看效果

// babel.config.js
module.exports = {
  presets: ["@babel/env"],
  plugins: []
};

babel 官方定義的 presets 配置項(xiàng)表示的是一堆plugins的集合,省的我們一個(gè)個(gè)的寫plugins,他直接定義好了類似處理react,typescript等的preset

//compiled.js
"use strict";
var fn = function fn() {
  console.log("wens");
};
var p = new Promise(function (resolve, reject) {
  resolve("wens");
});
var list = [1, 2, 3, 4].map(function (item) {
  return item * 2;
});

果然從語法層面都降級(jí)了。那么api層面要如何處理呢? 下面我們加入 @bable/polyfill

加入polyfill

對(duì)于 polyfill 的定義,相信經(jīng)常逛 mdn 的同學(xué)一定不會(huì)陌生, 他就是把當(dāng)前瀏覽器不支持的方法通過用支持的方法重寫來獲得支持。

在項(xiàng)目中安裝 @bable/polyfill ,然后 index.js 文件中引入

// index.js
import "@bable/polyfill";
const fn = () => {
  console.log("wens");
};
const p = new Promise((resolve, reject) => {
  resolve("wens");
});
const list = [1, 2, 3, 4].map(item => item * 2);

再次編譯我們看看結(jié)果

// compiled.js
"use strict";
require("@bable/polyfill");
var fn = function fn() {
  console.log("wens");
};
var p = new Promise(function (resolve, reject) {
  resolve("wens");
});
var list = [1, 2, 3, 4].map(function (item) {
  return item * 2;
});

沒有別的變化,就多了一行require("@bable/polyfill"),其實(shí)這里就把這個(gè)庫中的所有的polyfill都引入進(jìn)來了,就好比我們項(xiàng)目中一股腦引入了全部的lodash方法。這樣就支持了Promise和map方法。

細(xì)心的同學(xué)一定會(huì)發(fā)現(xiàn)這樣有點(diǎn)“蠢”啊,lodash都提供了按需加載,你這個(gè)一下都引入進(jìn)來了,可我只需要Promise和map啊。別慌,我們接著往下看。

配置 useBuiltIns

上面我們通過 import "@bable/polyfill" 的方式來實(shí)現(xiàn)針對(duì)api層面的“抹平”。然而從 babel v7.4.0開始官方就不建議采取這樣的方式了。 因?yàn)橐?@bable/polyfill 就相當(dāng)于在代碼中引入下面兩個(gè)庫

import "core-js/stable"; 
import "regenerator-runtime/runtime";

這意味著不僅不能按需加載還有全局空間被污染的問題。因?yàn)樗峭ㄟ^向全局對(duì)象和內(nèi)置對(duì)象的prototype上添加方法來實(shí)現(xiàn)的。

因此 babel 決定把這兩個(gè)人的工作一并交給上面我們提到的@babel/env,不僅不會(huì)全局污染還支持按需加載,豈不是妙哉?,F(xiàn)在我們再來看看改造后的配置

// index.js
// 去掉了polyfill
const fn = () => {
  console.log("wens");
};
const p = new Promise((resolve, reject) => {
  resolve("wens");
});
const list = [1, 2, 3, 4].map(item => item * 2);
// webpack.config.js
module.exports = {
  presets: [
    [
      "@babel/env",
      {
        useBuiltIns: "usage", // 實(shí)現(xiàn)按需加載
        corejs: { 
          version: 3, 
          proposals: true 
        }
      }
    ]
  ],
  plugins: []
};

通過給 @babel/env 配置 useBuiltIns 和 corejs 屬性,我們實(shí)現(xiàn)了polyfill方法的按需加載。關(guān)于全部配置項(xiàng),參加官方文檔

// compiled.js
"use strict";
require("core-js/modules/es.array.map");
require("core-js/modules/es.object.to-string");
require("core-js/modules/es.promise");
var fn = function fn() {
  console.log("wens");
};
var p = new Promise(function (resolve, reject) {
  resolve("wens");
});
var list = [1, 2, 3, 4].map(function (item) {
  return item * 2;
});

編譯后的js文件只require了需要的方法,完美。那么我們還有可以優(yōu)化的空間嗎?有的有的,我們接著往下看。

加入 @babel/plugin-transform-runtime

改造上面的例子

// index.js
class Person {
  constructor(name) {
    this.name = name;
  }
  say() {
    console.log(this.name);
  }
}

只轉(zhuǎn)換一個(gè) Person 類,我們看看轉(zhuǎn)換后的文件長啥樣

// compiled.js 
"use strict";
require("core-js/modules/es.function.name");
require("core-js/modules/es.object.define-property");
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
var Person = /*#__PURE__*/function () {
  function Person(name) {
    _classCallCheck(this, Person);
    this.name = name;
  }
  _createClass(Person, [{
    key: "say",
    value: function say() {
      console.log(this.name);
    }
  }]);
  return Person;
}();

除了require的部分,還多了好多自定義的函數(shù)。同學(xué)們想一想,現(xiàn)在只有一個(gè)index文件需要轉(zhuǎn)換,然而實(shí)際項(xiàng)目開發(fā)中會(huì)有大量的需要轉(zhuǎn)換的文件,如果每一個(gè)轉(zhuǎn)換后的文件中都存在相同的函數(shù),那豈不是浪費(fèi)了,怎么才能把重復(fù)的函數(shù)去掉呢?

plugin-transform-runtime 閃亮登場。

上面出現(xiàn)的_classCallCheck,_defineProperties,_createClass三個(gè)函數(shù)叫做輔助函數(shù),是在編譯階段輔助 Babel 的函數(shù)。

當(dāng)使用了plugin-transform-runtime插件后,就可以將babel轉(zhuǎn)譯時(shí)添加到文件中的內(nèi)聯(lián)輔助函數(shù)統(tǒng)一隔離到babel-runtime提供的helper模塊中,編譯時(shí),直接從helper模塊加載,不在每個(gè)文件中重復(fù)的定義輔助函數(shù),從而減少包的尺寸,下面我們看下效果:

// webpack.config.js
module.exports = {
  presets: [
    [
      "@babel/env",
      {
        useBuiltIns: "usage",
        corejs: { version: 3, proposals: true }
      }
    ]
  ],
  plugins: ["@babel/plugin-transform-runtime"]
};
// compiled.js
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
require("core-js/modules/es.function.name");
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var Person = /*#__PURE__*/function () {
  function Person(name) {
    (0, _classCallCheck2["default"])(this, Person);
    this.name = name;
  }
  (0, _createClass2["default"])(Person, [{
    key: "say",
    value: function say() {
      console.log(this.name);
    }
  }]);
  return Person;
}();

完美的解決了代碼冗余的問題。 你們以為這就結(jié)束了嗎,還沒有。仔細(xì)看到這里的同學(xué)應(yīng)該注意到了雖然上面使用 useBuiltIns 配置項(xiàng)實(shí)現(xiàn)了poilyfill的按需引用,可是他還存在全局變量污染的情況,就好比這句代碼,重寫了array的prototype方法,造成了全局污染。

require("core-js/modules/es.array.map");

最后再改造一次babel的配置文件

// webpack.config.js
module.exports = {
  presets: ["@babel/env"],
  plugins: [
    [
      "@babel/plugin-transform-runtime",
      {
        corejs: { version: 3 }
      }
    ]
  ]
};

我們看到去掉了 @babel/env 的相關(guān)參數(shù),而給 plugin-transform-runtime 添加了corejs參數(shù),最終轉(zhuǎn)換后的文件不會(huì)再出現(xiàn)polyfill的require的方法了。

// compiled.js
"use strict";
var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault");
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime-corejs3/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime-corejs3/helpers/createClass"));
var Person = /*#__PURE__*/function () {
  function Person(name) {
    (0, _classCallCheck2["default"])(this, Person);
    this.name = name;
  }
  (0, _createClass2["default"])(Person, [{
    key: "say",
    value: function say() {
      console.log(this.name);
    }
  }]);
  return Person;
}();

綜上所述,plugin-transform-runtime 插件借助babel-runtime實(shí)現(xiàn)了下面兩個(gè)重要的功能

  • 對(duì)輔助函數(shù)的復(fù)用,解決轉(zhuǎn)譯語法層時(shí)出現(xiàn)的代碼冗余
  • 解決轉(zhuǎn)譯api層出現(xiàn)的全局變量污染

結(jié)束

終于寫完了,從翻閱文檔到產(chǎn)出文章足足耗費(fèi)了兩天的時(shí)間。希望看到文章的同學(xué)也能和我一樣有收獲~~

更多關(guān)于babel polyfill原理的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 部屬vue項(xiàng)目,訪問路徑設(shè)置非根,顯示白屏的解決方案

    部屬vue項(xiàng)目,訪問路徑設(shè)置非根,顯示白屏的解決方案

    這篇文章主要介紹了部屬vue項(xiàng)目,訪問路徑設(shè)置非根,顯示白屏的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-04-04
  • vue ssr 實(shí)現(xiàn)方式(學(xué)習(xí)筆記)

    vue ssr 實(shí)現(xiàn)方式(學(xué)習(xí)筆記)

    這篇文章主要介紹了vue ssr 實(shí)現(xiàn)方式(學(xué)習(xí)筆記),本文不涉及到源碼解析,主要講解如何實(shí)現(xiàn) vue 的服務(wù)端渲染,比較適合 vue-ssr 小白閱讀,感興趣的小伙伴們可以參考一下
    2019-01-01
  • Vue組件封裝方案實(shí)現(xiàn)淺析

    Vue組件封裝方案實(shí)現(xiàn)淺析

    這篇文章主要介紹了Vue組件封裝方案實(shí)現(xiàn),我們將從分析組件封裝的優(yōu)勢開始,然后依次介紹 vue.js 的基本概念,以及如何創(chuàng)建、封裝和使用自定義組件
    2023-03-03
  • Vue指令指令大全

    Vue指令指令大全

    本文為大家介紹了VUE中內(nèi)置指令包括:v-text,v-html,v-show,v-if,v-else,v-else-if,v-for,v-on,v-bind,v-model,v-pre,v-cloak,v-once
    2019-02-02
  • vue實(shí)現(xiàn)購物車選擇功能

    vue實(shí)現(xiàn)購物車選擇功能

    這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)購物車選擇功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-01-01
  • 前端Vue如何獲取登錄的用戶名或用戶id代碼實(shí)例

    前端Vue如何獲取登錄的用戶名或用戶id代碼實(shí)例

    在前端開發(fā)中,獲取登錄用戶的用戶名是一項(xiàng)常見的需求,這篇文章主要給大家介紹了關(guān)于前端Vue如何獲取登錄的用戶名或用戶id的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-07-07
  • 一文帶你了解vite對(duì)瀏覽器的請(qǐng)求做了什么

    一文帶你了解vite對(duì)瀏覽器的請(qǐng)求做了什么

    Vite是一種新型前端構(gòu)建工具,能夠顯著提升前端開發(fā)體驗(yàn),下面這篇文章主要給大家介紹了關(guān)于vite對(duì)瀏覽器的請(qǐng)求做了什么的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2021-12-12
  • antd vue表格可編輯單元格以及求和實(shí)現(xiàn)方式

    antd vue表格可編輯單元格以及求和實(shí)現(xiàn)方式

    這篇文章主要介紹了antd vue表格可編輯單元格以及求和實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-04-04
  • 解決vue多層彈框時(shí)存在遮擋問題

    解決vue多層彈框時(shí)存在遮擋問題

    本文給大家介紹vue多層彈框時(shí)存在遮擋問題,解決思路首先想到的是找到對(duì)應(yīng)的遮擋層的css標(biāo)簽,然后修改z-index值,但是本思路只能解決首次問題,再次打開還會(huì)存在相同的問題,故該思路錯(cuò)誤,下面給大家?guī)硪环N正確的思路,一起看看吧
    2022-03-03
  • Vue+Element實(shí)現(xiàn)動(dòng)態(tài)生成新表單并添加驗(yàn)證功能

    Vue+Element實(shí)現(xiàn)動(dòng)態(tài)生成新表單并添加驗(yàn)證功能

    這篇文章主要介紹了Vue+Element實(shí)現(xiàn)動(dòng)態(tài)生成新表單并添加驗(yàn)證功能,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-05-05

最新評(píng)論