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/preset-env或者@babel/plugin-transform-runtime基础上改造呢?原因就是这两个都是独立的包,改造起来成本都大,且都会有侵入性,所以干脆重新开一个仓库维护,并重写了polyfill的内部实现,更多详情可以参考RFC: Rethink polyfilling story
import _flatMapInstanceProperty from "core-js-pure/stable/instance/flat-map.js"; import _trimLeftInstanceProperty from "core-js-pure/stable/instance/trim-left.js"; _flatMapInstanceProperty(foo).call(foo, x => [x, x + 1]); _trimLeftInstanceProperty(bar).call(bar); arr.includes(2);
constmodulesByVersions=require('./modules-by-versions');constmodules=require('./modules');module.exports=function(raw){// 判断传入的额corejs版本号是否符合npm版本号规范constcorejs=semver(raw);if(corejs.major!==3){throwRangeError('This version of `core-js-compat` works only with `core-js@3`.');}constresult=[];// modulesByVersions提供了core-js每个版本提供的polyfill apifor(constversionofObject.keys(modulesByVersions)){// 将小于传入core-js版本号的api传入result数组if(compare(version,'<=',corejs)){result.push(...modulesByVersions[version]);}}// modules包含core-js最新支持的所有api,这里的目的是从modules中过滤掉,不包含在result中的apireturnintersection(result,modules);};
语雀地址
目录
背景
在公司内部对项目进行构建大小优化的时候,发现构建产物,包括core-js与core-js-pure两份core-js相关的代码,所以想去尝试能不能只保留一份core-js
另外在使用一些新的api时,比如[].at(index),发现项目内并没有对array.at方法进行polyfill,这有点奇怪,毕竟项目的内的babel配置如下所示,按道理应该会有对应的polyfill,但是实际上并没有
为了解决上面的问题,及更近一步了解polyfill,于是有了如下实践
core-js介绍
core-js目前主流的polyfill库,babel内部默认的polyfill库
core-js目前有两个主流版本在使用2.x与3.x
3.x与2.x的主要区别
常见polyfill入口
其实不论是在项目中还是npm中,一般都不会直接使用core-js来进行polyfill,而是会使用babel来进行polyfill,原因是我们写的代码,如果需要运行在低版本的浏览器上,不仅需要对api进行polyfll,而且还需要对相关的es6+语法转换成es5语法,使用babel就可以把这两件事一起做了
babel polyfill
babel polyfill在陆续的演变中,提供了两种polyfill的方式,分别是@babel/preset-env与@babel/plugin-transform-runtime,二者都提供了polyfill的能力,但是提供的方式略有不同
原理:babel将code => ast => 遍历ast => 碰到对应的api则引入core-js对应的api or 直接引入整个core-js,如下所示
@babel/preset-env
@babel/preset-env
与polyfill相关的参数如下所示target
法语与api兼容的最终终端目标useBuiltIns
是否开启polyfill功能corejs
core-js相关配置version
允许设置成3.1、3.21等值proposals
是否允许使用提案语法shippedproposals
是否允许使用稳定的提案语法需要注意参数就是
useBuiltIns: 'entry' | 'usage' | false;
entry
代表直接引入整个core-js包usage
代表代码内使用了哪些api,就引入对应api的polyfillfalse
代表不进行polyfill考虑到项目大小,一般推荐使用
useBuiltIns: 'usage'
@babel/plugin-transform-runtime
为什么
@babel/preset-env
已经提供了polyfill,@babel/plugin-transform-runtime
还需要提供polyfill,这不是增加使用难度吗?原因是:
@babel/preset-env
仅提供非纯方式引入的polyfill,在项目使用场景没有问题,但是对于npm包场面,则可能会有问题,因为npm包一般是第三方提供的,为了尽可能的减少引入的npm对项目产生影响,使用无污染的方式导入polyfill更合理,所以最终演变成了@babel/plugin-transform-runtime
提供无污染的polyfill方式(关于为什么不在@babel/preset-env
直接做无污染的方式,猜测可能是不同的成员开发的)@babel/plugin-transform-runtime
相关参数如下所示corejs
version
只允许设置2、3,这里与@babel/preset-env
有差异propsals
babel新的polyfill方式
babel在polyfill方面,一直在致力以更优及更小的方式帮助项目or npm包导入polyfill,所以babel团队针对目前的polyfill方式,又提出了一种新的解决方案
之前babel polyfill存在两个问题
@babel/preset-env
或者@babel/plugin-transform-runtime
所以babel团队成员提供了一个新的polyfill插件,通过该插件支持
@babel/preset-env
与@babel/plugin-transform-runtime
包含的polyfill方式那么为什么不在
@babel/preset-env
或者@babel/plugin-transform-runtime
基础上改造呢?原因就是这两个都是独立的包,改造起来成本都大,且都会有侵入性,所以干脆重新开一个仓库维护,并重写了polyfill的内部实现,更多详情可以参考RFC: Rethink polyfilling story@babel/preset-env
在7.12.17版本接入新的babel-polyfills包@babel/plugin-transform-runtime
在7.13.0接入新的babel-polyfills包babel-plugin-polifill-corejs3原理
然后我们来看下,新的polyfill方式是如何来实现的,以babel-plugin-polyfill-corejs3@0.2.4为例
三种注入core-js的方式
entry-global
注入全局polyfill
import "core-js";
import "core-js/modules/es7.array.flat-map.js";
import "core-js/modules/es6.array.sort.js";
import "core-js/modules/es7.string.trim-right.js";
import "core-js/modules/web.timers.js";
...
对应
@babel/preset-env
全局polyfill场景usage-global
按需注入polyfill
foo.flatMap(x => [x, x+1]);
bar.trimLeft(); arr.includes(2);
import "core-js/modules/es.array.flat-map.js"; import "core-js/modules/es.array.unscopables.flat-map.js";
import "core-js/modules/es.string.trim-start.js";
foo.flatMap(x => [x, x + 1]); bar.trimLeft();
arr.includes(2);
对应
@babel/preset-env
按需polyfill场景usage-pure
以非全局污染的方式按需导入polyfill
foo.flatMap(x => [x, x+1]);
bar.trimLeft(); arr.includes(2);
import _flatMapInstanceProperty from "core-js-pure/stable/instance/flat-map.js";
import _trimLeftInstanceProperty from "core-js-pure/stable/instance/trim-left.js";
_flatMapInstanceProperty(foo).call(foo, x => [x, x + 1]);
_trimLeftInstanceProperty(bar).call(bar); arr.includes(2);
对应
@babel/plugin-transform-runtime
polyfill 按需无污染场景polyfill原理
uages
,则添加import core-js/modules/es.array.find-index.js
pure
,则添加import _findIndex from '@babel/runtime-corejs3/core-js/instance/find-index'
关键点:怎么知道代码内使用的某个api是否符合polyfill规则
根据传入的corejs版本号及core-js-compat 包内提供的get-modules-list-for-target-version.js
modules-by-versions.json 包含每个core-js版本支持的polyfill api,这样做的原因是ecma规范是不断变化的,那么api也会不断的变化状态,比如从state0-state4,在比如新增or删除一个api,所以core-js也是不断变化的,所以每个core-js版本支持的api也是不同的
modules.json 包含core-js最新版本支持的所有api
注意
proposals
与shippedProposals
的区别是,proposals
代表所有提案,shippedProposals
代表进入第四个阶段的提案所以从这里看,如果是使用
@babel/preset-env
polyfill 应该这样设置项目支持所有api的polyfill
项目不支持提案api的polyfill
所以从这里看,如果是使用
@babel/plugin-transform-runtime
polyfill 应该这样设置npm包支持所有api的polyfill
npm包不支持提案api的polyfill
注意这里
@babel/preset-env
与@babel/plugin-transform-runtime
传入的corejs参数有两个差异@babel/plugin-transform-runtime
的corejs参数,不支持传入3.x这样带小版本号的数字proposals
二者插件之间的表象不一致@babel/plugin-transform-runtime
传入corejs小版本号会抛错@babel/preset-env
与@babel/plugin-transform-runtime
的proposals: true
表象不一致从babel输出结果可以看到,二者传入的都是
corejs: { version: 3, proposals: true}
为什么得到的结果却是不同的,@babel/preset-env
没有polyfill到arrary.at
方法,而@babel/plugin-transform-runtime
确polyfill到了array.at
方法,原因是什么呢?先看
![image.png](https://camo.githubusercontent.com/8333061ae6ccc524c8377695576b906d639e1ea5ada0e31b546454aaf393bb27/68747470733a2f2f63646e2e6e6c61726b2e636f6d2f79757175652f302f323032322f706e672f32323230393835312f313636323130383830383634372d32656131376364332d396365352d346537322d383535332d6233643461643064336263622e706e6723636c69656e7449643d7563636166366466352d313531312d342663726f703d302663726f703d302663726f703d312663726f703d312666726f6d3d7061737465266865696768743d3438302669643d756438633230373830266d617267696e3d2535426f626a6563742532304f626a656374253544266e616d653d696d6167652e706e67266f726967696e4865696768743d393630266f726967696e57696474683d31373530266f726967696e616c547970653d62696e61727926726174696f3d3126726f746174696f6e3d302673686f775469746c653d66616c73652673697a653d323636353932267374617475733d646f6e65267374796c653d6e6f6e65267461736b49643d7564303063333531332d303439302d346339632d623330612d3731613738613330633132267469746c653d2677696474683d383735)
babel-plugin-polyfill-corejs3
插件内的usageGlobal
实现准确的过滤出
esnext.array.at
方法,而esnext.array.at
是在core-js 3.8版本内提供的,所以core-js3.0版本内是存在该api,所以最终的polyfill不包含array.at方法在看
babel-plugin-polyfill-corejs3
内的usagePure实现最终进行匹配的是
esnext.string.at
api, 而不是esnext.arrary.at
api,而esnext.string.at
恰好包含在corejs 3.0支持的api内,所以@babel/plugin-transform-runtime
场景下arrary.at
polyfill成功了结论:
proposals: true
在@babel/preset-env
与@babel/plugin-transform-runtime
下表现可能是不一致的,需要看具体的apicore-js与core-js-pure共存问题
core-js是有污染的方式导入垫片,而core-js-pure是无污染的方式导入垫片;所以如果在项目使用中,出现了无污染方式进行polyfill的包,那么最终构建产物就会包含这两份,如下图所示
![image.png](https://camo.githubusercontent.com/692a9cab5061ca945618f0a9e0bd40e01b696eb10472e4c6610edadf458a1402/68747470733a2f2f63646e2e6e6c61726b2e636f6d2f79757175652f302f323032322f706e672f32323230393835312f313636323136393736373932372d31313338306666642d336461362d343036372d613630332d3532343564313664613534612e706e6723636c69656e7449643d7563626164386235622d396430302d342663726f703d302663726f703d302663726f703d312663726f703d312666726f6d3d7061737465266865696768743d3237382669643d756466396334346132266d617267696e3d2535426f626a6563742532304f626a656374253544266e616d653d696d6167652e706e67266f726967696e4865696768743d353535266f726967696e57696474683d31323437266f726967696e616c547970653d62696e61727926726174696f3d3126726f746174696f6e3d302673686f775469746c653d66616c73652673697a653d313831363130267374617475733d646f6e65267374796c653d6e6f6e65267461736b49643d7535383231643063382d643466392d346434642d623138332d3066373363333162326434267469746c653d2677696474683d3632332e35)
对于这个问题有两个思路
对于内部公司内部npm包在进行构建的时候,是否一定要无污染的polyfill?目前认为是不需要的
原因就是,公司内部的项目本身会做兼容性要求,所以会进行polyfill,是可控的,所以如果引入的npm包又是纯的polyfill,那么项目在构建的产物里面最终会包含core-js 以及 core-js-pure两个包,而这两个包又有一定的大小,所以是自己项目的npm包,不推荐进行polyfill or 使用非纯的方式进行polyfill
推荐配置
鉴于上面
@babel/preset-env
与@babel/plugin-transform-runtime
关于proposals: true
表现不一致,为了尽可能的降低理解成本,推荐直接使用babel-plugin-polyfill-corejs3
插件,而关闭@babel/preset-env
与@babel/plugin-transform-runtime
polyfill的能力如果还未升级到babel最新版本,建议升级到babel最新版本,以便支持新的polyfill方式
公司内部npm包
web项目
总结
回到最开始的两个问题:
在公司内部对项目进行构建大小优化的时候,发现构建产物,包括core-js与core-js-pure两份core-js相关的代码,所以想去尝试能不能只保留一份core-js
解决方案:对于公司内部的npm包,可以直接使用有污染的方式进行polyfill or 不进行polyfill由项目内统一处理,对于第三方的npm包,则可以尝试使用webpack alias来处理
另外在使用一些新的api时,比如[].at(index),发现项目内并没有对array.at方法进行polyfill,这有点奇怪,毕竟项目的内的babel配置如下所示,按道理应该会有对应的polyfill,但是实际上并没有
解决方案:保证项目的core-js版本是最新的,同时确保传入的corejs参数版本号是最新的,且proposals设置为true
如果碰到相关的api最终没有被polyfill,推荐按以下步骤进行排查
yarn list core-js core-js-compat
@babel/preset-env
or@babel/plugin-transform-runtime
corejs
参数版本号及proposals
参数core-js-compact/modules-by-versions.json
内查询使用的api在哪个corejs版本内The text was updated successfully, but these errors were encountered: