乐趣区

babelpolyfill的相关知识

使用 @babel/polyfill 可以让你在任何 es2015+ 的环境中编写代码,而不需要担心兼容性问题。它会在全局变量上添加一些类似于原生的方法。但是 webpack 一直以来配置都特别复杂,直到 webpack4 才开始做 0 配置。项目中如果需要 webpack 的配置可能都是 ctrl+c ctrl+v, 没有及时去更新,会对 polyfill 有一些误解,比如说,项目中会同时出现 babel-plugin-transform-runtimebabel-polyfill,在已经使用了 babel-polyfill 的基础上还是会担心使用新语法带来的问题。这篇文章将会解答一些与 babel-polyfill 相关的问题。

polyfill 与 core-js

  • 简单来说 polyfill 包括了 core-jsregenerator-runtime(转义 async 和 await 的包)。
  • 而且 babel7.4.0 开始 @babel/polyfill 改成直接引入 core-jsregenerator-runtime

    As of Babel 7.4.0, this package has been deprecated in favor of directly including core-js/stable (to polyfill ECMAScript features) and regenerator-runtime/runtime (needed to use transpiled generator functions):

    import "core-js/stable";
    import "regenerator-runtime/runtime";

polyfill 与 preset-env

preset-env中我们需要关注以下两个属性

targets

targets 可以设置 polyfill 引入哪些方法的转义,因为 preset-env 有一个内置的 json 文件, 可以根据用户设置过滤所需要 polyfill 的方法

// corejs2-built-ins.json
    {
    "es6.array.copy-within": {
        "chrome": "45",
        "edge": "12",
        "firefox": "32",
        "safari": "9",
        "node": "4",
        "ios": "9",
        "samsung": "5",
        "opera": "32",
        "electron": "0.35"
      },
      "es6.array.every": {
        "chrome": "5",
        "opera": "10.10",
        "edge": "12",
        "firefox": "2",
        "safari": "3.1",
        "node": "0.10",
        "ie": "9",
        "android": "4",
        "ios": "6",
        "phantom": "2",
        "samsung": "2.1",
        "electron": "1.1"
      },
    ……
    }

// 根据项目需要兼容的最低版本
{
  "targets": {
    "chrome": "58",
    "ie": "11"
  }
}

useBuiltIns

useBuiltIns 有两个特别有用的属性,usage 和 entry, 使用两个属性都是直接引用 core-js 对应的包。

  • entry 的用法:项目中import "@babel/polyfill";persets 的 options 中使用useBuiltIns: "entry", 配合 targets 选项,只 import 最低版本所需要的方法垫片
  • usage 的用法:只需要在 persets 的 options 中使用useBuiltIns: “usage” 即可将所有项目中用到所需要在最低浏览器版本不兼容的方法垫片,例如 includes 方法就会自动import “core-js/modules/es6.string.includes”;`

    //demo
    // index.js
    console.log("test123".includes("test"));
    // webpack.config.js
    use: {
        loader: "babel-loader",
        options: {
            presets: [
                [
                    "@babel/preset-env",
                    {
                        targets: {
                            chrome: "58",
                            ie: "11"
                        },
                        useBuiltIns: "usage"
                    }
                ]
            ]
        }
    }
  • 因为 includes 在 ie11 中不兼容,打包结果:

    • 不使用@babel/preset-env, 打包后文件 _900 多 bytes 不到 1kb_,不能兼容低版本浏览器
    • 使用preset-env+useBuiltIns: "usage",babel 识别到使用了 includes 方法,加入 polyfill 的方法有[‘es6.string.includes’, ‘es7.array.includes’],打包后文件为 6.56kb
    • 使用preset-env+useBuiltIns: "entry",并且手动 import @babel/polyfill(使用 useBuiltIns webpack 都会建议不需要手动 import polyfill,如果它检测到用户已经 import 了会提示 useBuiltIns 选用 entry 属性),polyfill 的几十个方法,也就是按照 targets 的配置将所有需要 polyfill 方法都打包进来,打包后文件为 76.7kb
    {
      'es6.array.copy-within',
      'es6.array.fill',
      'es6.array.find',
      'es6.array.find-index',
      'es7.array.flat-map',
      'es6.array.from',
      'es7.array.includes',
      ……
    }
    • 只使用 babel-loader+@babel/preset-env,打包文件大小为 86.3kb
    • 因此,如果项目本身是有兼容性要求的话,一般都会使用 babel-loader 做兼容,因为很常见的 array.from 也是要做 polyfill 的,加上 preset-env + useBuiltIns 按需加载使用一些新语法其实不需要太担心包大小的问题,也不需要手动 import @babel/polyfill
  • 虽然在 babel-preset-env 官方文档中看到 usage 还是 experiment 状态,但是可以在 vue-cli 的源码看到默认打包的 app 的 presets 配置里 useBuiltIns 是 usage,所以可以放心的用(其实 babel6 的时候usage 就已经是 experiment,babel7 也还是)

    /vue-cli/packages/@vue/babel-preset-app/index.js

  • 需要注意的是如果你使用的不是 TC39 stage4 的提案,你还是需要自己手动去设置 babel-plugin,preset-env 不会帮你引入其他 stage 的语法垫片

polyfill 与 plugin-transform-runtime

  • 如果有长时间使用过 webpack,一定不会对 babel-plugin-transform-runtime 这个插件陌生, 大家都有做兼容的环境的作用,甚至有些项目还会看到同时使用 babel-polyfillbabel-plugin-transform-runtime,这种做法貌似在 babel-preset-env 出来之前是有一定道理的。
  • 实际上 transform-runtime 的转换是非侵入性的,也就是它不会污染你的原有的方法,比如挂在 Array 原型上的 includes 方法就只能使用babel-polyfill
  • transform-runtime的使用场景应该是库,遇到需要转换的方法它会另起一个名字,否则会直接影响使用库的业务代码,平常的项目使用 babel-polyfill 即可。

polyfill.io

  • 虽然 presets-env+ 实际压缩已经能够优化掉大部分体积的 polyfill,但是对于一些最新浏览器版本来说,任何的 polyfill 都是浪费资源的。
  • 这时候 polyfill.io 能够解决这个问题。polyfill.io 以服务端渲染的方式从请求中获取到 useragent 的信息,然后返回对应的 polyfill。但据说是因为浏览器版本太多,国内还有套壳的 360/qq 等浏览器,ua 要是判断失误没有其他回退的方案,因此没有办法广泛应用起来。
退出移动版