You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何指定Webpack打包node_modules的ES5版本?解决IE11兼容问题

问题描述

升级AJV.js到6.4版本后,我的vendor bundle中包含了uri-js的ESNEXT版本而非ES5版本,直接破坏了IE11的兼容性。

分析过程

AJV通过require('uri-js')引入该依赖,而uri-js在/node_modules/uri-js/dist/下明确提供了两个版本:

  • es5
  • esnext

但Webpack 4.8默认打包了esnext版本,我找不到指定优先使用ES5版本的配置方式。

相关配置文件

webpack.config.js

const path = require("path");
const webpack = require("webpack");
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const CopyWebpackPlugin = require('copy-webpack-plugin');

module.exports = {
  entry: {
    app: './src/index.tsx'
  },
  output: {
    filename: "js/[name].bundle.js",
    path: __dirname + "/dist"
  },
  devtool: "source-map",
  resolve: {
    extensions: [".ts", ".tsx", ".js", ".jsx", ".json"]
  },
  module: {
    rules: [
      { test: /\.tsx?$/, loader: "ts-loader" },
      {
        test: /\.less$/,
        use: ExtractTextPlugin.extract({
          use: [{
            loader: "css-loader",
            options: {
              localIdentName: '[local]--[hash:5]',
              sourceMap: true
            }
          }, {
            loader: "less-loader",
            options: { sourceMap: true }
          }],
          fallback: "style-loader",
          publicPath: "../"
        }),
        exclude: "/node_modules/"
      },
      { test: /\.html$/, use: 'raw-loader' },
      { test: /\.jpe?g$|\.gif$|\.png$/i, loader: "file-loader?name=assets/img/[name].[ext]" },
      { test: /\.woff2?$|\.ttf$|\.eot$|\.svg$/, use: "file-loader?name=assets/[name].[ext]" }
    ]
  },
  plugins: [
    new ExtractTextPlugin({ filename: "quino/style/style.css", allChunks: true }),
    new HtmlWebpackPlugin({ template: "src/index.html", filename: "index.html" }),
    new CopyWebpackPlugin([])
  ],
  optimization: {
    splitChunks: {
      cacheGroups: {
        commons: {
          test: /[\\/]node_modules[\\/]/,
          name: "vendors",
          chunks: "all"
        }
      }
    }
  }
};

package.json

{
  "name": "MyApp",
  "version": "1.0.0",
  "sideEffects": false,
  "description": "-",
  "private": false,
  "scripts": {
    "build": "webpack --config webpack.config.js --mode production",
    "dev": "webpack-dev-server --config webpack.config.js --host 0.0.0.0 --progress --colors --history-api-fallback --mode development"
  },
  "author": "Me",
  "license": "some",
  "devDependencies": {
    .... stuff ....
  },
  "dependencies": {
    .... stuff ....
    "ajv": "^6.4.0",
    .... more stuff ....
  }
}

tsconfig.json

{
  "compilerOptions": {
    "outDir": "build/dist",
    "module": "esnext",
    "target": "es5",
    "lib": ["es6", "dom"],
    "sourceMap": true,
    "allowJs": true,
    "jsx": "react",
    "moduleResolution": "node",
    "rootDir": "src",
    "forceConsistentCasingInFileNames": true,
    "noImplicitReturns": true,
    "noImplicitThis": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "suppressImplicitAnyIndexErrors": true,
    "noUnusedLocals": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  },
  "exclude": [
    "dist/**",
    "./*.js",
    "node_modules",
    "build",
    "scripts",
    "acceptance-tests",
    "webpack",
    "jest",
    "src/setupTests.ts"
  ]
}

我的核心疑问:

  1. 能否在webpack.config或package.json中指定优先使用node_modules里dist目录的ES5版本?
  2. node_modules下dist目录的版本选择逻辑是怎样的?

解决方案

问题1:如何指定优先使用ES5版本

当然可以!这里有几种简单有效的方式:

方式1:配置Webpack别名(最直接)

在webpack.config.js的resolve对象里添加别名,直接把uri-js指向它的ES5入口文件:

resolve: {
  extensions: [".ts", ".tsx", ".js", ".jsx", ".json"],
  alias: {
    // 强制Webpack使用uri-js的ES5版本
    'uri-js': require.resolve('uri-js/dist/es5/uri.all.js')
  }
}

这个方法相当于给Webpack硬指定了解析路径,完全不会有歧义,是我最推荐的方案。

方式2:利用项目package.json的browser字段

如果你的项目是纯浏览器环境,可以在自己的package.json里添加browser字段,指定该模块在浏览器下的替代入口:

{
  "browser": {
    "uri-js": "uri-js/dist/es5/uri.all.js"
  }
}

不过这个方法依赖Webpack对browser字段的默认支持,如果你修改过resolve.mainFields配置,需要注意优先级。

方式3:调整模块入口字段的优先级

Webpack解析模块时,会按resolve.mainFields的顺序读取模块package.json里的字段。默认浏览器环境的顺序是['browser', 'module', 'main'],而uri-js的module字段指向esnext版本,main字段指向ES5版本。

你可以修改这个顺序,把main放到module前面:

resolve: {
  extensions: [".ts", ".tsx", ".js", ".jsx", ".json"],
  mainFields: ['browser', 'main', 'module']
}

这样Webpack会优先读取main字段对应的ES5文件,但要注意这个配置会影响所有模块的解析顺序,如果你其他模块需要优先用ES模块版本做tree-shaking,需要权衡。


问题2:node_modules下版本选择的逻辑

Webpack选择模块入口的核心逻辑围绕这几点:

  1. 模块package.json字段的优先级:默认浏览器环境下是browser > module > main,Node.js环境是main > module
    • browser:专门为浏览器环境提供的入口,通常是ES5兼容版本
    • module:指向ES6+的ES模块版本,方便tree-shaking优化
    • main:指向CommonJS格式的版本,一般是ES5兼容的
  2. 你的Webpack配置:resolve.alias会直接覆盖默认解析路径;resolve.mainFields会改变字段读取顺序
  3. 模块自身的结构:像uri-js这种在dist目录下分版本的,是作者主动提供了不同环境的构建产物,Webpack会根据上述规则匹配对应的入口

回到你的情况,uri-js的module字段指向了esnext版本,而Webpack默认优先读取module字段,所以才打包了不兼容IE11的版本。

另外补充:虽然你用TypeScript转译自己的代码到ES5,但Webpack默认不会转译node_modules里的代码,除非你修改module.rules的exclude配置。不过既然uri-js已经提供了现成的ES5版本,直接指定使用它是更高效的方案,没必要额外做转译。

内容的提问来源于stack exchange,提问作者Marc

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 04:01:25