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

使用Webpack编译React应用时出现模块解析失败问题求助

Webpack编译TypeScript React应用时的ModuleNotFoundError问题

我用Webpack编译TypeScript构建React应用,执行npm run server时触发ModuleNotFoundError,无法解析loginfile.js、logoutfile.js等文件。试过在HTML里用../loginfile.js、./loginfile.js、loginfile.ts等多种引入路径,都没用。

为排查问题,我在src/test目录下新建了test.ts和test.html,test.html中引入test.js的代码如下:

<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=Edge" />

    <script type="text/javascript" defer src="test.js"></script>
</head>

<body>

</body>

</html>

同样报错:

ModuleNotFoundError: Module not found: Error: Can't resolve './test.js' in '/Users/*****/****/summarizr2/summarizr/src/test'

按预期编译后的test.js应该在dist目录中可找到。以下是我的webpack配置、项目结构信息:

webpack.config.js配置

module.exports = async (env, options) => {
  const dev = options.mode === "development";
  const config = {
    devtool: "source-map",
    entry: {
      polyfill: ["core-js/stable", "regenerator-runtime/runtime"],
      vendor: ["react", "react-dom", "core-js", "@fluentui/react"],
      taskpane: ["react-hot-loader/patch", "./src/taskpane/index.tsx", "./src/taskpane/taskpane.html"],
      commands: "./src/commands/commands.ts",
      loginfile: "./src/login/loginfile.ts",
      logoutfile: "./src/logout/logoutfile.ts",
    },
    output: {
      clean: true,
    },
    resolve: {
      extensions: [".ts", ".tsx", ".html", ".js"],
    },
    module: {
      rules: [
        {
          test: /\.css$/i,
          include: path.resolve(__dirname, 'src'),          
          use: ['style-loader', 'css-loader', 'postcss-loader'],
        },
        {
          test: /\.ts$/,
          exclude: /node_modules/,
          use: {
            loader: "babel-loader",
            options: {
              presets: ["@babel/preset-typescript"],
            },
          },
        },
        {
          test: /\.tsx?$/,
          exclude: /node_modules/,
          use: ["react-hot-loader/webpack", "ts-loader"],
        },
        {
          test: /\.html$/,
          exclude: /node_modules/,
          use: "html-loader",
        },
        {
          test: /\.(png|jpg|jpeg|gif|ico)$/,
          type: "asset/resource",
          generator: {
            filename: "assets/[name][ext][query]",
          },
        },
      ],
    },
    plugins: [
      new DotenvWebpackPlugin({ path: `.env.${options.mode}` }),
      new CopyWebpackPlugin({
        patterns: [
          {
            from: "assets/*",
            to: "assets/[name][ext][query]",
          },
          {
            from: "manifest*.json",
            to: "[name]" + "[ext]",
            transform(content) {
              if (dev) {
                return content;
              } else {
                return content.toString().replace(new RegExp(urlDev, "g"), urlProd);
              }
            },
          },
        ],
      }),
      new HtmlWebpackPlugin({
        filename: "taskpane.html",
        template: "./src/taskpane/taskpane.html",
        chunks: ["taskpane", "vendor", "polyfills"],
      }),
      new HtmlWebpackPlugin({
        filename: "loginfile.html",
        template: "./src/login/loginfile.html",
        chunks: ["loginfile"],
      }),
      new HtmlWebpackPlugin({
        filename: "logoutfile.html",
        template: "./src/logout/logoutfile.html",
        chunks: ["logoutfile"],
      }),
      new HtmlWebpackPlugin({
        filename: "endlogout.html",
        template: "./src/taskpane/endlogout.html",
        chunks: ["login", "vendor", "polyfills"],
      }),
      new HtmlWebpackPlugin({
        filename: "commands.html",
        template: "./src/commands/commands.html",
        chunks: ["commands"],
      }),
      new webpack.ProvidePlugin({
        Promise: ["es6-promise", "Promise"],
      }),
    ],
    devServer: {
      hot: true,
      headers: {
        "Access-Control-Allow-Origin": "*",
      },
      server: {
        type: "https",
        options: env.WEBPACK_BUILD || options.https !== undefined ? options.https : await getHttpsOptions(),
      },
      port: process.env.npm_package_config_dev_server_port || 3000,
    },
  };

  return config;
};

项目结构

├ src
 │  ├ commands
 │  │  ├ commands.html
 │  │  ├ commands.ts
 │  ├ constants
 │  │  ├ authentication.ts
 │  ├ login
 │  │  ├ loginfile.html
 │  │  ├ loginfile.ts
 │  ├ logout
 │  │  ├ logoutfile.html
 │  │  ├ logoutfile.ts
 │  ├ services
 │  │  ├ commands
 │  │  ├ common
 │  ├ taskpane
 │  │  ├ components
 │  │  ├ endlogout.html
 │  │  ├ index.css
 │  │  ├ index.tsx
 │  │  ├ login.html
 │  │  ├ logoutcomplete
 │  │  ├ taskpane.html
 │  │  ├ urls.ts
 ├ tailwind.config.js
 ├ tsconfig.json
 └ webpack.config.js

问题原因及解决方法

1. 手动引入JS与Webpack构建逻辑冲突

你在HTML模板中用<script>手动引入JS文件的方式,和Webpack的运行逻辑完全矛盾:

  • 开发模式下,Webpack Dev Server是内存编译,不会把文件输出到磁盘的dist目录,你手动引用的路径根本找不到实际文件;
  • 即使是生产模式,Webpack编译后的chunk文件名可能带有hash(若配置了),手动写的固定文件名也会失效;
  • test.ts没有加入Webpack的entry配置,Webpack根本不会去编译它,自然不存在对应的test.js。

2. 利用HtmlWebpackPlugin自动注入Chunk

你已经在使用HtmlWebpackPlugin,完全不需要手动写<script>标签,插件会自动把对应chunk注入到HTML中:

  • 对于test.html,需要在webpack配置中新增entry和对应的插件实例:
// 在entry中添加
test: "./src/test/test.ts",
// 在plugins数组中添加
new HtmlWebpackPlugin({
  filename: "test.html",
  template: "./src/test/test.html",
  chunks: ["test"],
}),

然后删除test.html中手动写的<script src="test.js"></script>。

  • 对于已有的loginfile.html、logoutfile.html,检查模板中是否存在手动引入loginfile.js/logoutfile.js的<script>标签,必须删除——因为你已经通过chunks: ["loginfile"]配置让插件自动注入了,手动引入会导致Webpack解析不存在的路径。

3. 其他需要修正的配置点

  • 完善output配置:明确指定输出路径和文件名,避免路径歧义:
output: {
  clean: true,
  filename: "[name].js",
  path: path.resolve(__dirname, "dist"),
},
  • 修正endlogout.html的插件配置:chunks: ["login", ...]中的login不存在于entry中,应该改为loginfile;
  • 确保resolve.extensions中.ts在.js前面,让Webpack优先解析TypeScript文件。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 04:55:54