Babel-polyfill
[[toc]]
@babel/preset-env、@babel/polyfill和@babel/plugin-transform-runtime
@babel/preset-env:转换一般语法,以确保代码在指定的旧版环境中运行. 示例:将 let 转换为 var,将箭头函数转换为传统函数等。
@babel/plugin-transform-runtime:优化和转换需要辅助函数的特定语法(如 async/await),避免代码重复和全局污染。
目标环境:根据你指定的目标环境(如浏览器版本、Node.js 版本等),自动选择和加载适当的 Babel 插件来转换你的代码。
转换现代语法:将 ES6+ 的现代 JavaScript 语法转换为兼容指定目标环境的旧版本 JavaScript 语法。
@babel/preset-env,这可以说是babel官方的得意之作,最早的时候没有这个包,有的是babel-preset-es2015这样的包,后来每次新标准发布之后,就要新加一个包。
babel顺应民意,发布了babel-preset-env这个包,它一次性囊括了已发布的所有标准包。
首先我们需要明确一下,preset-env的首要作用,不是帮我们把ES6+代码转成ES5.它的首要作用是认读ES6+代码。
在使用preset-env之前,babel是无法认识ES6+代码的,运行时会报Token错误。在使用preset-env之后,babel才能认识这些代码语法,并将它们抽象出AST树。
preset-env本身包含了一大堆plugin,并通过配置来控制插件,从而控制转码效果
polyfill了解下
babel 编译时只转换语法
,几乎可以编译所有时新的 JavaScript 语法,以确保在不支持这些新语法的环境中代码仍能正常运行,但并不会转化BOM里面不兼容的API
比如 Promise,Set,Symbol,Array.from,Array.is,async 等等的一些API
这时候就需要 polyfill 来转转化这些API
babel 转译语法需要一些plugin
如babel-preset-es2015,stage-0,stage-1等等
其中的 es2015 表示 babel会加载 es6 相关的编译模块,然后 stage-0 表示的是什么呢?
stage 系列集合了一些对 es7 的草案支持的插件,由于是草案,所以作为插件的形式提供。
stage-0 - Strawman: just an idea, possible Babel plugin. |
stage 是向下兼容 0>1>2>3>4 所包含的插件数量依次减少
polyfill 有三种:
babel-runtime |
因为babel编译es6到es5的过程中,babel-plugin-transform-runtime这个插件会自动polyfill es5不支持的特性,
这些polyfill包就是在babel-runtime这个包里 core-js 、regenerator等 polyfill。
babel-runtime和 babel-plugin-transform-runtime的区别是,相当一前者是手动挡而后者是自动挡,每当要转译一个api时都要手动加上require(‘babel-runtime’),
而babel-plugin-transform-runtime会由工具自动添加,主要的功能是为api提供沙箱的垫片方案,不会污染全局的api,因此适合用在第三方的开发产品中。
@babel/preset-env
targets
控制目标浏览器的版本
modules
默认为commonjs,设置为false时,不会转码模块加载,import from 语法不会转码
useBuiltIns
是否自动加载polyfill。它有三个值可选:false(默认), entry, usage。
corejs
承接useBuiltIns,当useBuiltIns值为entry或usage时,有效。它可以设置为:2,3,{ version:2, proposals:true }
@babel/polyfill
这个包是一个纯运行时的包,不是babel插件。它的作用是直接改写全局变量,从而让运行环境支持经过present-env转码后的代码
[ |
function test() { |
直接重写了 Promise ,污染全局
@babel/plugin-transform-runtime
@babel/plugin-transform-runtime 是一个 Babel 插件,主要用于优化代码的运行时辅助函数(如 async、generator 等)的重复导入和全局污染问题。其主要功能是:
减少冗余:避免在每个文件中重复引入辅助函数,通过引用共享的运行时库来减少代码冗余。
避免全局污染:避免在全局范围内添加新的变量,从而避免命名冲突。
在webpack中,babel-plugin-transform-runtime 实际上是依赖babel-runtime
- 它有两个作用:
- 将preset-env所产生的helpers函数提出到一个独立文件中,从而减少代码量
- 建立运行时沙盒,避免全局污染
corejs
helpers
是否要将所有helper函数提炼到另外一个公共文件中。默认为true。
当使用@babel/plugin-trasnform-runtime之后,原本babel会直接在文件中创建一个helper函数,现在会采用require的方式,从@babel/runtime中引入这些函数,这样就可以减少代码量
babel7中 corejs 和 corejs2 的区别
最近在给项目升级babel7,有一些改变但是变化不大,在升级中发现 babel7 变化挺大的,包括插件和包。
其中一项功能特别赞,就是 @babel/preset-env 中的 useBuiltIns 选项,如果你设置了 usage ,babel 编绎的时候就不用整个 polyfills , 只加载你使用 polyfills,这样就可以减少包的大小。
在使用 babel 中还想减少代码,就需要引入 babel 的运行时:
yarn add @babel/plugin-transform-runtime -D |
需要注意的是:
两个包引入的范围不一样:一个在开发时引入,一个在运行时引入。
- plugin-transform-runtime 已经默认包括了 @babel/polyfill,因此不用在独立引入。
在 plugin-transform-runtime 中有一个 corejs 很奇怪,可以设置成 false 或者 2。这是为什么这样?
大家知道 corejs 是一个给低版本的浏览器提供接口的库,如 Promise, map, set 等。
在 babel 中你设置成 false 或者不设置,就是引入的是 corejs 中的库,而且在全局中引入,也就是说侵入了全局的变量。可以观察以下的代码:
// 这是你写的代码 |
如果你的全局有一个引入,防止引入的库影响全局,那你就需要引把 corejs 设置成2。下面就是设真置成2,编绎成的代码:
; |
可以从编绎出的代码看到,Promise 代码变成了一个独立的变量 _promise,不会影响全局的 Promise。
这样的好处是,引入的库者自己引入了一个变量,这样如果你引入的第三方库会对 Promise 进行一些自定义操作,这样就可以避免第三方库报错。
还要注意一点是: 如果你设置了 corejs2,那你就需要加入下面的库:
corejs选项| 安装命令|里面包含的文件
–|:–:|:–:|–|
false | yarn add @babel/runtime | helpers、regenerator
2 | yarn add @babel/runtime-corejs2 | core-js、helpers、regenerator
3 | yarn add @babel/runtime-corejs3 | 还支持实例属性(例如[].includes)
参考文档:
babel-preset-env
babel-plugin-transform-runtime
runtime转换器插件主要做了三件事:
当你使用generators/async方法、函数时自动调用babel-runtime/regenerator |
库项目
@babel/preset-env 拥有根据 useBuiltIns 参数的多种polyfill实现,优点是覆盖面比较全(entry), 缺点是会污染全局, 推荐在业务项目中使用
库类项目推荐使用 @babel/plugin-transform-runtime
,因为库项目通常会面临另一个问题。如果我们直接导入 core-js 作 polyfill 的话,像 Promise
,Set
,Map
这样的全局对象就会被覆盖。对于一般的应用而言,问题不大;但如果是库,你无法预期其它开发者会在什么情况下使用你的库,很可能他的目标平台都支持这些新语法元素,不希望转译污染。
此时,使用 @babel/plugin-transform-runtime
可以让 babel 在转译时使用沙箱垫片和代码复用, 避免帮助函数重复 inject 过多的问题, 该方式的优点是不会污染全局