zoukankan      html  css  js  c++  java
  • @babel/preset-env使用polyfill遇到的坑

    场景还原

    最近将一个项目由babel@6升级到babel@7,升级后最重要的两个包:

    • @babel/preset-env: 提供代码的转换和API的polyfill的能力
    • @babel/plugin-transform-runtime: 复用babel注入的helper代码以及提供无污染全局环境的polyfill功能

    基于此,对项目中js语法的transform和API的polyfill进行了调整:

    • 关闭@babel/plugin-transform-runtime的polyfill功能
    • 开启@babel/preset-env的polyfill和transform功能

    其中,@babel/preset-env的polyfill使用usage形式(不了解的可以查看官方文档),意思是以项目设置的target环境为前提,根据项目中使用到的api功能进行polyfill;具体babel配置片段如下:

    {
      "plugins": [
        [
          "@babel/plugin-transform-runtime",
          {
            "corejs": false,
            "regenerator": false
          }
        ]
      ],
      "sourceType": "unambiguous",
      "presets": [
        [
          "@babel/preset-env",
          {
            "modules": false,
            "shippedProposals": true,
            "useBuiltIns": "usage",
            "corejs": {
              "version": "3.10",
              "proposals": true
            },
            "targets": {
                ...
            }
          }
        ]
      ]
    }
    

    然后项目中使用到了Promise.allSettled静态方法:

    Promise.allSettled([p1, p2, p3]).then(res => console.log(res));
    

    通过webpack打包后运行,js会报错:

    TypeError: Promise.allSettled is not a function

    不对呀,按照官网就是这么配置的,一度对babel的配置产生怀疑,折腾半天最后都排除掉;没招了,那就试试断点调试,别说还真发现问题,直接上图:

    相信大家能够看出问题所在,Promise.allSettled的polyfill之后重新引入Promise的polyfill,后面的Promise的polyfill覆盖了Promise.allSettled的polyfill,导致调用该方法时报错。

    那会不会是babel的bug导致的呢,于是开起查找问题之旅了。。。

    问题追踪

    首先,简要说明下@babel/preset-env实现polyfill的思路:babel会生成代码的ast,并对其traverse过程中,根据代码使用的新API来确定需要填充的polyfill。

    遇到这种问题,首先想到会不会是@babel/preset-env的bug,google半天也没有找到类似问题,于是就开启debug调试模式。在调试追踪到babel-plugin-polyfill-corejs3/lib/index.js中的usageGlobal方法,其在解析代码中使用到了PromiseallSettled的api,如下图:

    babel会根据代码用到的api,最终解析出为这些api注入的polyfill,如下图:

    从图可以看出最终需要为PromiseallSettled注入的依赖polyfill;但是注入的polyfill存在问题,即es.promisees.promise.all-settled顺序反了,后者依赖前者;由此可见是babel的bug已确定无疑了。

    接着进如resolve方法,发现其在确定代码的相关polyfill依赖后,对与依赖的先后顺序存在bug;因为代码调用Promise.allSettled会依赖:

    • 全局global的Promise api
    • Promise的静态方法allSettled api

    所以babel在获取二者对应的polyfill在合并时产生了问题,这可以在babel-plugin-polyfill-corejs/lib/built-in-definitions.js文件中:

    // 所有静态方法的polyfill
    const StaticProperties = {
        ...
        Promise: {
            all: define(null, PromiseDependenciesWithIterators),
            allSettled: define(null, ["es.promise.all-settled", ...PromiseDependenciesWithIterators]),
            any: define(null, ["esnext.promise.any", ...PromiseDependenciesWithIterators]),
            race: define(null, PromiseDependenciesWithIterators),
            try: define(null, ["esnext.promise.try", ...PromiseDependenciesWithIterators])
          },
      ...
    }
    

    可以看出Promise的相关静态方法的polyfill都放置到第一位,而define为对该数值进行任何排序:

    const define = (pure, global, name = global[0], exclude) => {
      return {
        name,
        pure,
        global,
        exclude
      };
    };
    

    查到这里可以猜测这个babel-plugin-polyfill-corejs3@0.1.7有bug,查看最新版本0.2.0的代码发现对这个方法进行了修复:

    var _data = _interopRequireDefault(require("../core-js-compat/data.js"));
    
    const polyfillsOrder = {};
    Object.keys(_data.default).forEach((name, index) => {
      polyfillsOrder[name] = index;
    });
    
    const define = (pure, global, name = global[0], exclude) => {
      return {
        name,
        pure,
        global: global.sort((a, b) => polyfillsOrder[a] - polyfillsOrder[b]),
        exclude
      };
    };
    

    可以看出该方法对注入的polyfill做了排序,进过排序得到正确的依赖顺序,于是果断升级@babel/preset-env@7.13.15,因为之前@babel/preset-env@7.13.10依赖的是babel-plugin-polyfill-corejs3@0.1.7,至此一直困扰我的这个大坑给堵上了。

    出于好奇心,对babel-plugin-polyfill-corejs3代码进行blame,果然发现这个问题在24天前进行了修复:

    blame.png

    进一步查看发现,之前已经有人提出过类似的bug:The order of promise and promise.finally after compilation seems to be wrong,于是做了修复。

    总结

    困扰我一天的问题算是解决了,分享给大家希望大家避坑。

    不过话说回来,开始遇到这个问题时,换成@babel/preset-enventry模式的polyfill模式不会发生任何问题,但是心中过不去这个坎为啥usage模式不能用,明明后者有一定的体积优势,最终得到答案;这一过程虽然耗费一定的时间,但是有收获,值!

  • 相关阅读:
    14.4.9 Configuring Spin Lock Polling 配置Spin lock 轮询:
    14.4.8 Configuring the InnoDB Master Thread IO Rate 配置InnoDB Master Thread I/O Rate
    14.4.7 Configuring the Number of Background InnoDB IO Threads 配置 后台InnoDB IO Threads的数量
    14.4.7 Configuring the Number of Background InnoDB IO Threads 配置 后台InnoDB IO Threads的数量
    14.4.6 Configuring Thread Concurrency for InnoDB 配置Thread 并发
    14.4.6 Configuring Thread Concurrency for InnoDB 配置Thread 并发
    14.4.5 Configuring InnoDB Change Buffering 配置InnoDB Change Buffering
    14.4.5 Configuring InnoDB Change Buffering 配置InnoDB Change Buffering
    14.4.4 Configuring the Memory Allocator for InnoDB InnoDB 配置内存分配器
    14.4.4 Configuring the Memory Allocator for InnoDB InnoDB 配置内存分配器
  • 原文地址:https://www.cnblogs.com/wonyun/p/14667430.html
Copyright © 2011-2022 走看看