polyfill是什么?
-
Polyfill是一种JavaScript库,它提供了原生的替代实现,使得现代Web特性在老旧浏览器中得以正常工作。Polyfill的主要目的是解决浏览器兼容性问题,确保代码在多种浏览器中正常运行。
-
Pollfill 一词最早是在 nodemon 的作者 Remy Sharp 于 2010 年10 月 8 日发表的博客文章 What is a Polyfill? 中首次提到,他对 polyfill 的定义是:
A shim that mimics a future API providing fallback functionality to older browsers.
翻译过来就是:
polyfill 就是一个垫片/填充/补丁程序,用于抹平浏览器之间的 API 差异,在旧的浏览器上支持新的特性。
polyfill的作用
-
兼容性:Polyfill提供了原生的替代实现,使得现代Web特性在老旧浏览器中得以正常工作。
-
功能实现:Polyfill可以实现一些现代Web特性,如Promise、fetch API等,即使老旧浏览器不支持这些特性。
-
性能优化:Polyfill可以帮助开发者优化代码性能,例如通过实现requestAnimationFrame等API。
babel-polyfill
-
Babel默认只转换新的
Javascript语法,而不转换新的API,比如
- Iterator, Generator, Set, Maps, Proxy, Reflect,Symbol,Promise 等全局对象
- 在全局对象上的方法,比如说ES6在Array对象上新增了
Array.find方法,Babel就不会转码这个方法
-
如果想让这个方法运行,必须使用
babel-polyfill来转换等 -
Babel 7.4之后不再推荐使用@babel/polyfill
-
babel v7 推荐使用@babel/preset-env代替以往的诸多polyfill方案
安装
npm i @babel/polyfill
targets
- 配置参数
targets表示我们需要支持哪些平台和哪些版本 - browserslist
{
test: /\.js?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
targets: {
//"browsers": ["ie >= 8", "chrome >= 62"],// 推荐使用 .browserslist
"browsers": [">1%"]
}
}
}
}
useBuiltIns: false
babel-polyfill它是通过向全局对象和内置对象的prototype上添加方法来实现的。比如运行环境中不支持Array.prototype.find方法,引入polyfill, 我们就可以使用ES6方法来编写了,但是缺点就是会造成全局空间污染- @babel/preset-env为每一个环境的预设
@babel/preset-env默认只支持语法转化,需要开启useBuiltIns配置才能转化API和实例方法useBuiltIns可选值包括:"usage" | "entry" | false, 默认为 false,表示不对polyfills处理,这个配置是引入 polyfills 的关键useBuiltIns: false此时不对polyfill做操作。如果引入@babel/polyfill,则无视配置的浏览器兼容,引入所有的polyfill
src/index.js
src/index.js
import '@babel/polyfill';
let sum = (a, b) => a + b;
let promise = Promise.resolve();
console.log([1, 2, 3].find(item => item === 2));
webpack.config.js
webpack.config.js
const path = require('path');
module.exports = {
mode: 'development',
devtool: false,
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'main.js'
},
module: {
rules: [
{
test: /\.js?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
sourceType: "unambiguous",
+ presets: [["@babel/preset-env", { useBuiltIns: false }]]
}
}
}
]
},
plugins: []
};
useBuiltIns: "entry"
- 在项目入口引入一次(多次引入会报错)
- "useBuiltIns": "entry" 根据配置的浏览器兼容,引入浏览器不兼容的 polyfill。需要在入口文件手动添加
import '@babel/polyfill',会自动根据 browserslist 替换成浏览器不兼容的所有 polyfill - 这里需要指定 core-js 的版本,
corejs默认是2, - 如果配置
corejs: 3, 则import '@babel/polyfill'需要改成import 'core-js/stable';import 'regenerator-runtime/runtime'; -
corejs默认是2
- 50.8 KiB
安装
npm install --save core-js@2 npm install --save core-js@3
src\index.js
src\index.js core-js@2
import '@babel/polyfill';
let sum = (a, b) => a + b;
let promise = Promise.resolve();
console.log([1, 2, 3].find(item => item === 2));
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));
webpack.config.js
{
test: /\.js?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
+ presets: [["@babel/preset-env", { useBuiltIns: 'entry', corejs: { version: 3 } }]]
}
}
}
WARNING (@babel/preset-env): We noticed you're using the `useBuiltIns` option without declaring a core-js version. Currently, we assume version 2.x when no version is passed. Since this default version will likely change in future versions of Babel, we recommend explicitly setting the core-js version you are using via the `corejs` option.
You should also be sure that the version you pass to the `corejs` option matches the version specified in your `package.json`'s `dependencies` section. If it doesn't, you need to run one of the following commands:
npm install --save core-js@2 npm install --save core-js@3
"useBuiltIns": "usage"
- "useBuiltIns": "usage"
usage会根据配置的浏览器兼容,以及你代码中用到的 API 来进行 polyfill,实现了按需添加 - 当设置为usage时,polyfill会自动按需添加,不再需要手工引入
@babel/polyfill
src/index.js
src/index.js
console.log([1, 2, 3].find(item => item === 2));
console.log(Array.prototype.find);
console.log(Array.prototype.hasOwnProperty('find'));
webpack.config.js
webpack.config.js
{
test: /\.js?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
+ presets: [["@babel/preset-env", { useBuiltIns: 'usage', corejs: { version: 3 } }]]
}
}
}
babel-runtime
- Babel为了解决全局空间污染的问题,提供了单独的包babel-runtime用以提供编译模块的工具函数
- 简单说
babel-runtime更像是一种按需加载的实现,比如你哪里需要使用Promise,只要在这个文件头部import Promise from 'babel-runtime/core-js/promise'就行了
安装
npm i babel-runtime --save-dev
src/index.js
src/index.js
import Promise from 'babel-runtime/core-js/promise';
const p = new Promise(()=> {});
babel-plugin-transform-runtime
-
@babel/plugin-transform-runtime插件是为了解决
- 多个文件重复引用 相同helpers(帮助函数)=>提取运行时
- 新API方法全局污染 -> 局部引入
-
启用插件
babel-plugin-transform-runtime后,Babel就会使用babel-runtime下的工具函数 -
babel-plugin-transform-runtime插件能够将这些工具函数的代码转换成require语句,指向为对babel-runtime的引用 -
babel-plugin-transform-runtime就是可以在我们使用新 API 时自动 import
babel-runtime里面的
polyfill- 当我们使用
async/await时,自动引入babel-runtime/regenerator - 当我们使用 ES6 的静态事件或内置对象时,自动引入
babel-runtime/core-js - 移除内联
babel helpers并替换使用babel-runtime/helpers来替换
- 当我们使用
安装
npm i @babel/plugin-transform-runtime @babel/runtime-corejs3 --save-dev
webpack.config.js
webpack.config.js
{
test: /\.js?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
sourceType: "unambiguous",
presets: [["@babel/preset-env", { useBuiltIns: 'usage', corejs: { version: 3 } }]],
plugins: [
[
"@babel/plugin-transform-runtime",
{
corejs: 3,
helpers: true,
regenerator: true
}
],
]
}
}
}
corejs: 3
-
当我们使用 ES6 的静态事件或内置对象时自动引入 babel-runtime/core-js
//var _Promise = __webpack_require__("./node_modules/@babel/runtime-corejs3/core-js-stable/promise.js"); const p = new Promise(() => { }); console.log(p);
helpers: true
- 移除内联babel helpers并替换使用
babel-runtime/helpers来替换 - 避免内联的 helper 代码在多个文件重复出现
class A {
}
class B extends A {
}
console.log(new B());
regenerator: true
- 是否开启
generator函数转换成使用regenerator runtime来避免污染全局域
//var _regeneratorRuntime = __webpack_require__(/*! @babel/runtime-corejs3/regenerator */ "./node_modules/@babel/runtime-corejs3/regenerator/index.js");
function* gen() {
}
console.log(gen());
最佳实践
- @babel/preset-env和plugin-transform-runtime二者都可以设置使用corejs来处理polyfill
项目开发
-
useBuiltIns使用usage
-
plugin-transform-runtime只使用其移除内联复用的辅助函数的特性,减小打包体积
{ "presets": [ [ "@babel/preset-env", { "useBuiltIns": "usage", "corejs": 3 } ] ], "plugins": [ [ "@babel/plugin-transform-runtime", { "corejs": false } ] ] }
类库开发
- 类库开发尽量不使用污染全局环境的
polyfill,因此@babel/preset-env只发挥语法转换的功能 - polyfill由
@babel/plugin-transform-runtime来处理,推荐使用core-js@3
{
"presets": [
[
"@babel/preset-env"
]
],
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"corejs": {
"version": 3
}
}
]
]
}
polyfill-service
- polyfill.io自动化的 JavaScript Polyfill 服务
- polyfill.io通过分析请求头信息中的 UserAgent 实现自动加载浏览器所需的 polyfills
<script src="https://polyfill.io/v3/polyfill.min.js"></script>