You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Babel is a toolchain that is mainly used to convert ECMAScript 2015+ code into a backwards compatible version of JavaScript in current and older browsers or environments. Here are the main things Babel can do for you:
Transform syntax
Polyfill features that are missing in your target environment (through @babel/polyfill)
@babel/preset-env is a smart preset that allows you to use the latest JavaScript without needing to micromanage which syntax transforms (and optionally, browser polyfills) are needed by your target environment(s).
Modular standard library for JavaScript. Includes polyfills for ECMAScript up to 2021: promises, symbols, collections, iterators, typed arrays, many other features, ECMAScript proposals, some cross-platform WHATWG / W3C features and proposals like URL. You can load only required features or use it without global namespace pollution.
前言
作为前端工程化工具,无论是Babel还是Webpack,在前端工程化中都扮演非常重要的角色。但是这类工具除了在项目初始创建时会频繁接触,到了后期的功能开发和维护中却又鲜有涉及,加之这类工具可配置属性多,随着工具更新配置项又会经常变化,因此对我个人而言,一直掌握的并不是很好。本次在升级组件库中遇到了一系列问题,借此机会记录问题的解决。
对于Babel涉及以下相关的问题:
Babel是什么?
按照Babel官方的说法:
如官方所说,Babel是JavaScript编译器,作为工具链主要用来将ECMAScript2015+的代码兼容为能运行在当前和旧版本的浏览器或其他环境中的代码。其实在上面的官方介绍中就表示了Babel所提供的两大功能:
Babel作为开箱即用工具链,在不做任何配置的情况下,Babel并不会做任何处理,而Babel所需要处理的任何工作都需要借助插件(plugin)完成。例如当我们在.babelrc中配置:
时,Babel便可以用来编译箭头函数:
当我们在项目中想使用Babel支持众多特性和语法,一条条插件配置过于繁重,因此Babel提供了Preset(预设),其本质就是一系列Babel插件的集合,例如:
@babel/preset-env是什么?
关于@babel/preset-env官方介绍:
@babel/preset-env允许我们在不需要微观管理的情况下,根据目标浏览器环境,进行语法转换(polyfill)从而使用最新的JavaScript。
在 babel@6 版本中,一般使用的是stage,例如:babel/preset-stage-0,对stage其实只会语法转化,对应的polyfill对应的API则交给 babel-plugin-transform-runtime 或者 babel-polyfill 来实现。
在 babel@7版本,废弃了stage,转而引入了@babel/preset-env,@babel/preset-env不仅提供了语法转化,同时也提供了polyfill的能力。
target
target参数表明我们项目需要适配到的环境,比如可以声明适配到的浏览器版本(例如IE11),这样 babel 会根据浏览器的支持情况自动引入所需要的 polyfill。
@babel/preset-env实际上依赖了类似于:browserslist、compat-table、electron-to-chromium这类第三方库数据,@babel/preset-env利用这些数据并按照我们配置target,获得我们目标浏览器所支持的JavaScript语法和浏览器特性,从而对应这些JavaScript语法和浏览器特性所需要的Babel插件和Polyfills。
当@babel/preset-env的target参数为空时,默认指向的是项目层级的browserslistrc配置。
一般来说,项目层级的浏览器支持配置可以通过 .browserslistrc 文件来设定目标浏览器,例如:
表示目标是包括浏览器市场份额超过0.25%且忽略没有安全更新的浏览器(如 IE10 和 BlackBerry)的用户所需的Polyfills 和代码转换。如果未设置,则默认使用browserslist配置源。browserslist的默认配置为:
需要注意的是,@babel/preset-env目前不支持stage-x阶段的插件,需要单独引入相应的插件。
corejs
按照core-js官方的介绍
core-js是JavaScript的模块化标准库,包含到ECMAScript 2021的polyfill,例如:promises、symbols、collections、iterators 等特性及提案。
目前core-js分为v2和v3两个大的版本,v3版本并不向后兼容v2版本,目前推荐使用core-js@3的主要原因在于:
@babel/preset-env的corejs属性仅当配置 useBuiltIns: usage 或 useBuiltIns: entry 时才对应生效,corejs对应的属性值为 core-js 所支持的版本,从而 决定 @babel/preset-env 如何注入polyfill。
useBuiltIns
useBuiltIns作为@babel/preset-env的配置项,支持一下三个值:
useBuiltIns: false时,@babel/preset-env不会引入polyfill,需要你在项目中主动引入:
通过这种方式,会把所有的polyfill全部引入,造成包体积庞大。
useBuiltIns: entry时,我们需要在项目入口中主动引入polyfill库,babel 根据 targets 替换成浏览器不兼容的所有 polyfill。
会被编译成:
useBuiltIns: usage时,无序引入任何的polyfill库,babel 根据 targets ,以及项目代码中用到的 API 实现按需添加,例如:
会被编译成
此时@babel/preset-env则会按需引入polyfill。
@babel/plugin-transform-runtime
@babel/plugin-transform-runtime的官方文档介绍如下:
@babel/plugin-transform-runtime的功能可以总结为三点:
自动处理generators/async函数
对于不支持的generators/async的浏览器,我们必须使用polyfill兼容处理。例如我们通过@babel/preset-env配置useBuiltIns为usage:
但是这种方式会造成全局环境污染,利用@babel/plugin-transform-runtime配置regenerator属性则可以避免该情况。
避免polyfill污染全局变量
@babel/plugin-transform-runtime插件的另外一个目的是构建代码的沙盒环境。我们之前提到的,引入 core-js 或者 @babel/polyfill都会污染全局环境(对于@babel/preset-env而言,无论使用的哪一种useBuiltIns),如果是应用开发不会造成额外的影响,但如果你的代码目的是发布给其他人使用的库,这就会造成其他的问题。
例如Promise的polyfill会被编译成:
而引入 @babel/plugin-transform-runtime 插件后,则会被编译成:
相比于添加全局的实例或者修改原型,而是通过的统一模块(@babel/runtime-corejs3)引入并替换,避免了对全局变量及其原型的污染,更符合类库或者工具库的定义。
如果@babel/plugin-transform-runtime 和 @babel/preset-env 共同使用,且@babel/plugin-transform-runtime 开启corejs,@babel/preset-env开启 useBuiltIns,实际效果会是怎么阳?事实上,polyfill 将会采用不污染全局的,且 @babel/preset-env 的 targets 设置将会失效。但是 @babel/plugin-transform-runtime 也并不是没有缺点,因为其导致了targets的失效,因此无法带来打包体积的优势。
自动移除Babel的helpers
Babel使用非常小的助手函数(helper)实现常见的函数,例如:_extend。默认情况下,helper会被添加到所需的每个文件中。这种逻辑会造成打包体积的膨胀。
例如:
会被编译为:
Babel为Class创造了_classCallCheck作为辅助函数(helpers),但是项目中存在多个文件,Babel就会为每个文件创建单独的辅助函数,这无疑会大大增加打包体积。这就是@babel/plugin-transform-runtime出现的主要原因,所有的helper将会引用@babel/runtime模块从而避免编译输出的内容的重复。
同样上面的内容,引入 @babel/plugin-transform-runtime,上面的类会被转译为:
因此@babel/plugin-transform-runtime插件能够复用Babel的注入helper代码从而节省资源体积。
@babel/plugin-transform-runtime关于polyfill的副作用
@babel/plugin-transform-runtime 插件实现的 polyfill 不会污染全局环境,但是采用 @babel/plugin-transform-runtime后, @babel/preset-env 中的 targets 将会失效,这会导致最终包的体积变大。
应用项目和Library中该如何分别配置polyfill
应用项目
useBuiltIns推荐设置设置为entry,将最低环境不支持的所有 polyfill 都引入到入口文件(即使你在你的业务代码中并未使用),这是一种兼顾最终打包体积和稳妥的方式,因为我们很难保证引用的三方包有处理好polyfill这些问题。除非充分保证你的三方依赖 polyfill处理得当,那么也可以把 useBuiltIns 设置为 usage。
建议配置如下:
并在项目开头处引入:
Library
Library与应用项目不同在会被发布给第三方使用,因而无法确定使用环境,因而要求整个环境必须是不会污染全局环境的沙盒。因此必须借助babel/plugin-transform-runtime插件,议开启 corejs,polyfill 由 @babel/plugin-transform-runtime 引入。@babel/preset-env 关闭 useBuiltIns。
建议配置如下:
The text was updated successfully, but these errors were encountered: