你真的了解了babel-polyfill吗?

532 阅读3分钟

「这是我参与2022首次更文挑战的第2天,活动详情查看:2022首次更文挑战」。


  • babel只进行JS语法的语法转换,对于JS新的API是无法进行转换的,比如Map, Set, Promise等全局对象
  • 在全局对象的上方法,比如ES6 新增的 Array.find方法,babel就无法转换

1. targets

  • 配置参数targets表示我们要支持那些平台和那些版本
  • 也可以配置浏览器的版本号
    • 浏览器的版本号越低,引入的polyfill越多
    • 版本号越高,引入的越少
{
    test: /.js$/,
    use: {
        loader: "babel-loader",
        options: {
            targets: {
            "browsers": ["last 2 versions"] // ["IE 6", "chrome 90"]
          }
        }
    }
}

2. babel-polyfill

babel-polyfill是通过向全局对象或者内置对象上的prototyoe上添加方法来实现的,缺点是会造成全局空间污染

3. useBuiltIns

@babel/preset-env是转换语法的插件预设,默认是只转换语法,不转换API的,如果要转换API,需要开启useBuiltIns配置才能转换API和实例方法

useBuiltIns有三个选项

  • false: 默认是false,表示不对polyfill做处理,全量引入polyfill; 如果引入@bebel/polyfill,则无视配置浏览器的兼容,引入所有的polyfill
  • "entry": 入口文件引入@bebel/polyfill
  • "useage": 自动判断代码中用到了什么API来按需加载

3.1 useBuiltIns: false

{
  test: /.js$/,
  use: {
    loader: "babel-loader",
    options: {
      presets: [
      	["@babel/preset-env", {
            useBuiltIns: false
        }]
      ]
    }
  }
}

3.2 useBuiltIns: "entry"

  • 在项目入口文件引入一次(多次会报错)
  • 需要在项目入口文件引入 import "@babel/polyfill
  • 会根据配置的浏览器的兼容性来引入浏览器不兼容的polyfill
  • 需要指定core-js的版本,默认是2,需要在入口文件配置 import "@babel/polyfill
  • 如果core-js指定的版本是3,则需要在入口文件把import "@babel/polyfill" 改成 import "core-js/stable"; import "regenerator-runtime/runtime"
  • core-js就是polyfill方法的实现
  • 缺点是只会根据浏览器的版本来引入,如果代码中没有使用新的API也会引入当前浏览器不兼容的polyfill

npm install --save core-js@2 npm install --save core-js@3

/src/index.js

// core-js: 2
import "@babel/polyfill";

// core-js: 3
import 'core-js/stable';
import 'regenerator-runtime/runtime';

let sum = (a, b) => a + b;
let promise = Promise.resolve();
console.log([1, 2, 3].find(item => item === 2));
{
  test: /.js$/,
  use: {
    loader: "babel-loader",
    options: {
      presets: [
      	["@babel/preset-env", {
        	useBuiltIns: "entry",
          corejs: 3 // corejs: { version: 3 } 
        }]
      ]
    }
  }
}

3.3 useBuiltIns: "usage"

  • 如果配置成usage之后,usage会根据浏览器兼容的配置和你代码中用到的API来按需加载
  • 当设置成usage之后,不需要在引入@babel/polyfill
{
  test: /.js$/,
  use: {
   loader: "babel-loader",
   options: {
      presets: [
      	["@babel/preset-env", {
          useBuiltIns: "usage",
          corejs: 3 // corejs: { version: 3 } 
        }]
      ]
    }
  }
}

4. babel-runtime

  • babel-runtime为了解决全局空间污染的问题单独提供的的包,用于解决编译模块的工具函数
  • 它更像是一种按需加载,需要用到什么模块就引入什么模块
  • 缺点就是用什么都需要手动引入,非常麻烦且繁琐

npm i babel-runtime --save-dev

import Promise from '@babel-runtime/core-js/promise';

let promise = Promise.resolve();

5. babel-plugin-transform-runtime

  • 是为了解决多个文件引用了相同的 helper(帮助函数) 提取到运行时
  • 新的API全局方法变成局部引用,会生成多个方法的文件导出来使用

npm i @babel/plugin-transform-runtime @babel/runtime-corejs3 --save-dev

{
  test: /.js$/,
  use: {
    loader: "babel-loader",
    options: {
      presets: [
      	["@babel/preset-env", {
          useBuiltIns: "usage",
          corejs: 3 // corejs: { version: 3 } 
        }]
      ]
    },
    plugins: [
    	["@babel/plugin-transform-runtime", {
      	corejs: 3,
        helpers: true
      }]
    ]
  }
}

5.1 corejs: 3

  • 当我们使用ES6的静态事件 或者 内置对象的方法时,会自动引用babel-runtime/core-js

5.2 helpers: true

  • 移除内联的babel helpers并替换使用babel-runtime/helpers
  • 避免内联的helper在多个文件中重复出现

6. 总结

平时项目开发

  • 对于平时项目开发的时候,采用@babel/preset-env来处理兼容问题,缺点是污染全局变量
  • useBuiltIns: "usage",来判断浏览器的版本和自动按需引入
  • 通过配置@babel-plugin-transform-runtime的helpers: true来减少公共文件的重复生成,提取成公共文件

对于包和库开发的时候

  • 尽量减少全局空间的污染
  • 可以采用@babel-plugin-transform-runtime的corejs:3 来做polyfill的兼容处理