背景

在 ie 环境下,有一些高级的 api 可能不存在,比如 promise,如下:

  1. /** ./src/index.js **/
  2. const P = new Promise((resolve, reject)=>{});

打包过后,我们得到以下的结果:
image.png
我们发现 new Promise 并没有不转化,如果在 ie 环境下就会报错了。

解决方案(三种)

这边有几种方案可选:

  • @babel/preset-env 方案(推荐)
  • @babel/polyfill 方案
  • @babel/plugin-transform-runtime 方案

    @babel/preset-env 方案

    我们需要通过之前使用的 @babel/preset-env 来在我们的环境中构建一个 Promise。除此之外我们还需要用到 core-js ,Promise 的代码就被存放在 core-js 中。

    安装

    安装 core-js 包并且它是运行时依赖。

    1. yarn add core-js@3

    webpack 配置

    给 @babel/preset-env 穿参数时,presets 的值改成数组的数组,数组元素的第一个为包的名字,第二个参数为 包的参数,如下:
    参数配置如下:

  • useBuiltIns 这个参数代表了 @preset-env 如何处理polyfills,它有三个参数:false,“entry” ,“usage”。其默认值为false。当取值为false的时候,polyfill会被全部引入到代码中。所以,一般不会取false;剩下的两个配置要和corejs搭配使用,当取值为当取值为“entry”或”usage”的时候,须声明corejs的版本并安装corejs库,如果不安装的话,执行babel命令时会弹出提示。

  • 当取值为”entry”时,需要在项目的入口文件处手动引入”core-js”,执行babel命令时根据配置项的targets目标环境找出需要的polyfill进行部分引入。当取值为”usage”的时,除了会考虑目标环境缺失的API以外,同时还会关注我们项目代码里使用到的ES6特性。只有我们使用到的ES6特性API在目标环境缺失的时候,才会进行引入。所以在项目中,一般是采用”usage”的配置。因此在这里可以对”entry”和“usage”的区别做一个总结,主要有以下两点:

    • 采用entry配置时,须在入口文件处手动引入 core-js,usage不需要

      1. import 'core-js'
    • 采用entry配置时,会将环境中缺失的api全部引入,而usage只会引入项目中用到api

所以这边我们采用 usage 模式,同时安装 core-js,指定 core-js 的版本,如下:

  1. /** ./webpack.config.js **/
  2. let path = require('path');
  3. //...
  4. module.exports = {
  5. //...
  6. module: {
  7. rules: [
  8. {
  9. test: /\.js$/,
  10. exclude: /node_modules/,
  11. include: path.resolve(__dirname, './src'),
  12. use:[
  13. {
  14. loader: 'babel-loader',
  15. options: {
  16. //帮你转化高版本的api语法
  17. //不支持在 提案中的方法 stage-x ,例如装饰器。
  18. //不使用这种方法的话,可以在代码中引用 @babel/polyfill
  19. presets: [
  20. [
  21. '@babel/preset-env',
  22. {
  23. useBuiltIns: 'usage',// 只转化使用的 api
  24. corejs: { version: 3 }
  25. }
  26. ]
  27. ],
  28. plugins: ['@babel/plugin-transform-runtime']
  29. }
  30. }
  31. ]
  32. }
  33. ]
  34. }
  35. //...
  36. }
  37. //...

最终打包结果

再看我们的打包文件,如下:
image.png
我们会发现多了很多代码,这就是在新构建的 Promise,这样在 ie 环境下也能够使用 Promise 了。

@babel/polyfill 方案

已经废弃了,不推荐使用,官方文档上也没有了。

@babel/plugin-transform-runtime 方案

@babel/plugin-transform-runtime 之前我们也用到过了,用来提取公共代码,这边解决高级 api 的问题,也可以用到这个包,配置方法如下:

安装依赖包

刚才的 @babel/preset-env 依赖 core-js 包。而 @babel/plugin-transform-runtime 不需要依赖这个包,但是相似的依赖另外一个包 @babel/runtime-corejs3 ,也是 corejs 的包。

  1. yarn add @babel/runtime-corejs3

webpack 配置

  1. //...
  2. module.exports = {
  3. //...
  4. module: {
  5. rules: [
  6. {
  7. test: /\.js$/,
  8. exclude: /node_modules/,
  9. include: path.resolve(__dirname, './src'),
  10. use:[
  11. {
  12. loader: 'babel-loader',
  13. options: {
  14. presets: [['@babel/preset-env',{
  15. //useBuiltIns: 'usage',
  16. //corejs: { version: 3 }
  17. }]],
  18. plugins: [['@babel/plugin-transform-runtime', {
  19. "absoluteRuntime": false,
  20. "corejs": 3,
  21. "helpers": true,
  22. "regenerator": true,
  23. "version": "7.0.0-beta.0"
  24. }]]
  25. }
  26. }
  27. ]
  28. },
  29. ]
  30. }
  31. //...
  32. }
  33. //...

最终打包结果

image.png
编译通过,promise 都被打包进来了。

补充

  • 除了 Promise 外,还支持字符串、数组的 includes 等其他方法,如下:

image.png
image.png
因为使用了 usage,所以必须在代码中用到 includes 才会被打包进来,如下:
image.png