likes
comments
collection
share

Webpack4升级Webpack5 IE兼容性问题

作者站长头像
站长
· 阅读数 28

webpack4升级到webpack5,打包后在google上正常运行,IE11报错 记录两个兼容性问题的坑

Webpack config

一般情况下,第三方依赖打完包后提供给外部调用的代码都是使用的ES5语法,这也是为什么配置babel-loader时一般都忽略node_modules文件夹,除了性能上的考虑之外,实际上大部分依赖都不需要再进行转译了

也就是说,当babel正确配置时,也只是对项目的业务代码部分进行polyfill,第三方依赖包中的语法babel管不着(如果你配了exclude: /node_modules/)

举个babel-loader的🌰:

// webpack.config.js
{
    //...
    module: {
        rules: [
            {	
                // node_modules的依赖一般都做了编译处理,不需要再次编译
                exclude: /node_modules/  
                test: /\.(js|jsx)$/,
                loader: 'babel-loader',
                options: {
                    presets: [
                        ['@babel/preset-env', {targets: 'ie 11'}]
                    ],
                }
            }
        ]
    }
}

就算这么配置,你还是能在打包后的js文件中发现很多ES6的语法,比如箭头函数,在ie11上运行就直接报错

这是因为Webpack5在这一方面做了更细粒度的优化,它默认的打包结果使用的是ES6语法,如果有需要,才会根据具体的配置来生成代码,所以默认情况下,webpack5是不支持IE的,因为它默认的运行时代码(runtime)使用的是ES6语法,在IE下运行会报错

仔细阅读文档,Webpack5的配置项output.environment提供了一系列关于代码最终运行环境的配置,其中默认的配置项基本都不支持IE,必须手动指定为这些配置项

// 文档配置介绍
module.exports = {
  output: {
    environment: {
      // The environment supports arrow functions ('() => { ... }').
      arrowFunction: true,
      // The environment supports BigInt as literal (123n).
      bigIntLiteral: false,
      // The environment supports const and let for variable declarations.
      const: true,
      // The environment supports destructuring ('{ a, b } = obj').
      destructuring: true,
      // The environment supports an async import() function to import EcmaScript modules.
      dynamicImport: false,
      // The environment supports 'for of' iteration ('for (const x of array) { ... }').
      forOf: true,
      // The environment supports ECMAScript Module syntax to import ECMAScript modules (import ... from '...').
      module: false,
    },
  },
};		

在output.environment中加入配置: 这个配置表示我们打包后的代码运行的环境是否支持这些语法,显然有一部分语法IE是不支持的,改为false

module.exports = {
// ...其他属性
  output: {
    environment: {
      arrowFunction: false, // 环境不支持箭头函数
      bigIntLiteral: false,  // 不支持BigInt
      const: true,
      destructuring: false,  // 不支持解构
      dynamicImport: false,  // 不支持异步import
      forOf: false,   // 不支持for...of
      module: false,  // 不支持module
    },
  },
};	

另一个解决方式

感谢 @yukinotech 指出webpack提供的另一个配置,可以解决运行时代码编译出现的兼容性问题,为webpack指定运行环境,这样就不需要一项一项修改enviroment属性

// webpack.conf.js
module.export = {
    // ...
    target: ["web", "es5"]
}


第二个坑: JS压缩插件-TerserWebpackPlugin

JS文件被babel编译后,下一步就是压缩(生产环境),JS的压缩插件uglifyjs-webpack-plugin已经被terser-webpack-plugin取代,并且webpack5内置了该依赖,但是坑的一点是terser-webpack-plugin压缩时默认也不会使用ES5语法,比如

let a = 1;
let b = {a: a}

这段代码会被压缩成:

let a=1;let b={a}

其中使用了ES6的语法糖,运行在IE会报错。 需要明确在TerserWebpackPlugin中指定

const TerserPlugin = require("terser-webpack-plugin");

module.exports = {
  optimization: {
    minimize: true,
    minimizer: [
        new TerserPlugin({
                ecma: 5
        })],
  },
};

完整相关配置代码如下:

module.exports = {
    output: {
        entry: 'main.js',
        output: {
            publicPath: '',
            path: 'dist',
            target: ["web", "es5"],
            // 也可以手动指定
            //environment: {
            //  arrowFunction: false, // 环境不支持箭头函数
            //  bigIntLiteral: false,  // 不支持BigInt
            //  const: false,    // ie 10不支持const
            //  destructuring: false,  // 不支持解构
            ///  dynamicImport: false,  // 不支持异步import
            //  forOf: false,   // 不支持for...of
            //  module: false,  // 不支持module
            //},
            filename: () => (isDev ? '[name].js' : '[name].[contenthash:8].js'),
            chunkFilename: () => (isDev ? 'assets/[name].js' : 'assets/[name].[contenthash:8].js')
        },
        target: 'web',
        resolve: {
            extensions: ['ts', 'tsx', 'js', 'jsx'],
            alias: {
                '@': 'src',
                vue$: 'vue/dist/vue.runtime.esm.js'
            }
        },
        module: {
            rules: [
                // 其他的loader
                {	
                    test: /\.(js|jsx)$/,
                    // node_modules的依赖一般都做了编译处理,不需要再次编译
                    exclude: /node_modules/
                    use: {
                        loader: 'babel-loader',
                        // options可以抽离到babel.config.js文件,targets可以通过.browserslistrc配置
                        options: {
                                presets: [
                                    ['@babel/preset-env', {targets: 'ie 11'}]
                                ],
                        }
                    }
                    
                }
            ]
        },
        // 一般只在Production环境下开启
        optimization: {
            minimize: true,
            minimizer: [
                new TerserPlugin({
                        ecma: 5
                })
              ],
         },
    }
}