如何使用Babel实现polyfill

babel是什么

babel官方文档中对babel有明确的定义:

Babel 是一个 JavaScript 编译器

Babel 是一个工具链,主要用于将采用 ECMAScript 2015+ 语法编写的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。

当然babel已不仅仅用于转换ECMAScript 2015+ ,例如它还可以转换typescript、flow。

babel做的工作是

  1. 将源代码解析为AST

  2. 更改/转换AST

  3. 打印AST(转换为源代码)

插件和preset

babel 本身不具有任何转化功能,它把转化的功能都分解到一个个 plugin 里面。因此当我们不配置任何插件时,经过 babel 的代码和输入是相同的。

插件

插件分两种:

  • 语法插件
  • 转换插件

前端面试刷题网站灵题库,收集大厂面试真题,相关知识点详细解析。】

前面已经提到,babel的插件实际做的工作是

  1. 将源代码解析为AST

  2. 更改/转换AST

  3. 打印AST(转换为源代码)

语法插件做的工作就是第1步,将源代码解析为AST,而转换插件则是3步都要进行。因此如果已经配置了转换插件,就不需要再额外配置相应的语法插件。这一点官方文档中也有说明:

转换插件将启用相应的语法插件,因此你不必同时指定这两种插件。

预设

因为通常我们在项目中使用es2015代码转换或者react语法转换,会用到多个插件,如果一个一个配置,繁琐而且容易出错。预设就是用来解决这个问题的。

Babel 的预设(preset)可以被看作是一组 Babel 插件和/或 options 配置的可共享模块。\

常用的preset:preset-env,用来转换es2015,preset-react,用来转换jsx等,preset-typescript

插件和预设的执行顺序

如果两个转换插件都将处理“程序(Program)”的某个代码片段,则将根据转换插件或 preset 的排列顺序依次执行。

• 插件在 Presets 前运行。

• 插件顺序从前往后排列。

• Preset 顺序是颠倒的(从后往前)。

babel的使用

官方文档-使用指南

babel主要有3种使用方法

  1. 命令行
  2. node API
  3. babel-loader

命令行

npm install --save-dev @babel/core @babel/cli @babel/preset-env

注:babel7中的核心逻辑被抽离到@babel/core中,因此使用babel必须安装babel/core

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

使用babel命令行工具进行代码转换

./node_modules/.bin/babel src --out-dir lib

node API

npm install --save-dev @babel/core
const babel = require("@babel/core");

babel.transformSync("code", optionsObject);

集成(babel-loader)

{
  // ...other config
  module: {
    rules: [
      {
        test: /.m?js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
          options: {
            presets: ['@babel/preset-env']
          }
        }
      }
    ]
  }
}

可以在.babelrcpackage.jsonbabel-loaderoption中配置插件和preset.

推荐.babelrc

使用babel进行polyfill

babel的一些插件和预设可以转换新语法特性(如箭头函数),而内建的变量(如Promise、Symbol)却无法兼容。

为了兼容内建变量,需要用到polyfill的技术。

polyfill技术就是用兼容语法(ES5)实现新的内建变量、扩展内建变量的静态属性(Array.from)和实例属性([].includes)。

polyfill库

最流行的polyfill库有两个:core-jsregenerator

core-js

zloirock/core-js,它提供了 ES5、ES6 的 polyfills,包括 promise 、symbols、collections、iterators、typed arrays、ECMAScript 7+ proposals、setImmediate 等等。

regenerator

facebook/regenerator,它实现 ES6/ES7 中 generators、yield、async 及 await 等相关的语法。

babel-polyfill

babel-polyfill做的事情就是引入了core-jsregenerator,使用的时候直接import,如果使用webpack打包,直接在入口entry中引入即可。

在babel@7.4.0以后已经不推荐,可以直接引入core-jsregenerator代替。

@babel/runtime

同样是引入了core-jsregenerator,以支持polyfill的能力。

除了支持polyfill能力,@babel/runtime还提供了运行时代码复用的能力,后面会提到。

在之前的版本中都是直接引用所有的polyfill到项目中,这样有两个问题

  1. 体积过大,有些浏览器版本已经支持一些新的内建的对象和属性,不需要polyfill;项目中也不是每个polyfill的特性都用到,引用所有的polyfill会造成很大的冗余。
  2. 污染全局变量,polyfill方法是创建全局的变量以支持内建变量,给内建变量添加静态方法或者实例属性,这样会污染到全局变量。

preset-env

preset-env支持通过设置useBuildIn选项来控制polyfill的体积。

该选项有以下几个可选值

false

这是默认的值,选择该值,preset-env不做操作,需要手动引入polyfil(babel-polyfill或者core-js + regenerator

entry

需要手动引polyfill,preset-env会根据browserslist改写引用polyfill的语句(包括babel-polyfillcore-jsregenerator),引入必要的polyfill,而不会安装环境中已经支持的特性,从而达到缩减包体积的目的。

例如我们项目中手动引入了core-js

import "core-js";

preset-env会转换为

// 根据browserslist判断,只引入环境中不支持的特性
import "core-js/modules/es.string.pad-start";
import "core-js/modules/es.string.pad-end";

usage

使用该值,不需要手动引入polyfill,preset-env会自动给用到某个特性的文件添加polyfill的引入,这样就实现了按需加载。

babel-plugin-transform-runtime

插件功能

这个插件有两个功能

  • 让所有用到babel的runtime的文件,只引用一份运行时,就是@babel/runtime。

  • 自动引入core-js和regenerater,并给core-js和@babel/polyfill的内置(如Promise、Map、Set)命名别名,这样可以避免污染全局变量。

下面两种配置都可以支持按需加载,也都能支持实例属性。

第一种配置会污染全局,第二种不会。

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "useBuiltIns": "usage",
        "corejs": 3
      }
    ]
  ]
}
{
  "presets": [
    [
      "@babel/preset-env"
    ]
  ],
  "plugins": [
    ["@babel/plugin-transform-runtime", {
      "corejs": 3
    }]
  ]
}

core-js2只支持全局变量和静态属性,而core-js3已经可以支持实例属性。

babel corejs@3 是如何按需polyfill原型对象方法的

注意事项

  1. core-js3之前,如果需要实例属性的polyfill,只能用污染全局的方式。
  2. 使用@babel/plugin-transform-runtime + corejs@3,需要安装@babel/runtime-corejs3https://www.babeljs.cn/docs/babel-plugin-transform-runtime#corejs
  3. babel-plugin-transform-runtime依赖@babel/runtime,插件是dev安装,runtime要生产环境安装:https://www.babeljs.cn/docs/babel-plugin-transform-runtime#installation

参考文章

一口(很长的)气了解 Babel

Babel polyfill 知多少

Babel官网

babel corejs@3 是如何按需polyfill原型对象方法的

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,313评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,369评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,916评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,333评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,425评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,481评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,491评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,268评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,719评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,004评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,179评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,832评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,510评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,153评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,402评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,045评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,071评论 2 352

推荐阅读更多精彩内容