最近折腾 @babel/preset-env 的一些小心得

近来厂里的项目越来越多,代码共享必不可少。我现在采取的方案是:

  1. 把公共组件拿出来,开一个新仓库
  2. 使用 webpack 进行打包编译,libraryTarget: 'umd'
  3. 将打包编译的代码一起提交到仓库
  4. 使用 npm i <owner>/<repo> -S 安装依赖,因为我厂的仓库均为私有,所以不能发布到 NPM

这套方案简单好用,实操效果良好。接下来我希望优化打包结果,于是研究了打包配置项,下面是我的一点心得。

[2021-04-07] 更新:

我们目前采用 GitHub Registry 托管私有 packages 的方案,比上面直接安装仓库的方案更好,想了解的同学可以看 使用 GitHub Registry 托管私有 NPM 源

@babel/preset-env

首先,Babel 推荐使用 @babel/preset-env 套件来处理转译需求。顾名思义,preset 即“预制套件”,包含了各种可能用到的转译工具。之前的以年份为准的 preset 已经废弃了,现在统一用这个总包。

同时,babel 已经放弃开发 stage-* 包,以后的转译组件都只会放进 preset-env 包里。

browserslist

@babel/preset-env 支持一些参数,用来处理哪些 feature 要转译,哪些不要。其中比较重要的是 targets,用来指定目标环境。targets 使用 browserslist 来筛选浏环境,这样我们就不需要指定所有浏览器版本,而可以使用类似 last 2 versions 这样的描述。具体怎写,可以看文档,这里不再赘述。

如果你想知道自己配置的是否合适,可以在仓库目录下执行 npx browserslist,列出所有目标浏览器,比如:

zhailujiadeiMac:fe meathill$ npx browserslist
and_chr 73
and_ff 66
and_qq 1.2
and_uc 11.8
android 67
android 4.4.3-4.4.4
baidu 7.12
chrome 73
chrome 72
edge 18
edge 17
firefox 66
firefox 65
ios_saf 12.2
ios_saf 12.0-12.1
kaios 2.5
op_mini all
op_mob 46
op_mob 12.1
opera 58
opera 57
safari 12.1
safari 12
samsung 9.2
samsung 8.2

Babel 官方建议我们把 targets 写到 .browserslistrc 或者 package.json 里,这样其它工具也能更轻易的获取到目标浏览器。另外,npx browserslist 无法从 .babelrc 等 babel 配置文件里读取配置,所以执行的时候看到的会是默认结果。

useBuiltIns

接下来,我们可以配置 useBuiltIns,这个属性决定是否引入 polyfill。它有三个可选值,默认是 false,即不引入,或者说,Babel 编译结果不引入,把引入的位置、引入哪些 polyfill 交给用户处理。因为我们的页面中通常有大量的 JS,在每个文件里分别引用 polyfill 太浪费资源,所以可以在核心入口 JS 引用一次即可。

但是这样我们必须手动 import '@babel/polyfill' 引入所有 polyfill,其实并不理想,因为大部分浏览器不需要这些。

所以推荐用 useBuiltIns: 'usage' 即“按需引用”。虽然文档中标记为“experimental”,但我用起来也没遇到什么问题。如果目标浏览器不支持需要的 feature,那么就引入 polyfill,不然的话就不引用。由于目前的打包工具越发智能,随着 tree shaking 的完善,这样可以最低限度引入 polyfill。

core-js

core-js 目前最新版本是 3.0.1,关于 v3 和 v2 的对比,大家可以看这篇博文:https://github.com/zloirock/core-js/blob/master/docs/2019-03-19-core-js-3-babel-and-a-look-into-the-future.md。这里简单总结一下,core-js 2 封版于 1.5 年之前,所以里面只有对 1.5 年之前 feature 的 polyfill,最近 1.5 年新增的 feature 都不支持,也就存在因为新功能没有 polyfill 于是在旧浏览器里失败的风险。

所以我们应当升级到最新版,npm i core-js@3 -D 然后修改 babel 配置:

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": "> 5%",
        "useBuiltIns": "usage",
        "corejs": 3
      }
    ]
  ]
}

注意,目前 Vue Cli 3 集成了 core-js 2,不支持升级到 v3,无法手动升级。需要等待 Vue Cli 4。

@babel/polyfill

@babel/polyfill 是对 core-js 的封装,引用了 core-js 的内容和生成器(regenerator-runtime)。 v7.4 之后,这个仓库就被废弃了,希望用户自己选择使用哪个兼容库。

换言之,以前:

import "@babel/polyfill";

需要被替换成

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

不过我不建议这么做。对于绝大部分情况,使用 @babel/preset-env + useBuiltIns: 'usage' 仍然是最好的选择。


总结

这些知识并不复杂,基本上文档里都有。不过一次性看大量英文文档可能对很多同学来说都是负担。我比较提倡这样学习:

  1. 遇到需求就去看一遍,不求全部理解,能解决目前的问题即可
  2. 重复这个过程,争取每次都比上一次理解更多
  3. 建立不同工具之间的逻辑体系,要求能够内恰
  4. 继续重复这个过程,知道确认自己理解了
  5. 通过看文档巩固

如果您觉得文章内容对您有用,不妨支持我创作更多有价值的分享:


已发布

分类

,

来自

评论

《 “最近折腾 @babel/preset-env 的一些小心得” 》 有 7 条评论

  1. […] browserslist,可以看到当前要支持的浏览器列表(其实仔细一看,这个列表和 最近折腾 @babel/preset-env 的一些小心得 […]

  2. web_zhou 的头像
    web_zhou

    presets这种方式直接配置没问题吗?好像这种方式babel处理不了node_modules里面的模块(有些node_modules里面的插件是高级语法),

    1. meathill 的头像

      没问题。首先大多数时候 babel 都会忽略 node_modules(通过 babel-loader 的 exclude options)。其次大部分“处理不了”的原因在于你的目标浏览器没有包含在 browserslist 里面,只是你不知道。

  3. 阿照 的头像
    阿照

    求助个问题,我使用@babel/preset-env来解决兼容问题,但是使用window.location.search也会被加了一大堆polyfill,怎么解决这个问题呢

    1. meathill 的头像

      你这个问题的确有点意思,我也实验了一下,果然如果用到 location.search 就会不可避免地引入一堆 polyfill,其原因是 babel 无法判断你到底是想修改 url 里的值,还是想调用 string.search() 方法。参考:https://github.com/babel/babel/issues/7841

      1. 阿照 的头像
        阿照

        好麻烦,这样看下来,按需加载也会导致大量无用的polyfill被加进来,有试用过https://github.com/babel/babel-polyfills这个新方案吗,不知道用这个还需不需要@babel-preset-env

        1. meathill 的头像

          你这个方案我还没看过,感谢普及。

          不过我觉得难点并不在这里:并不是实现不了 polyfill,而是无法判断何时该加载 polyfill。应该是开发团队认为 location.href 并不值得投入那么多时间,所以需求就这么被搁置了。

欢迎吐槽,共同进步

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据