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

从Craco迁移至Webpack后React运行异常求助

从Craco迁移到Webpack后React控制台异常排查

问题背景

原本运行正常的React应用,从craco.config.js迁移到自定义webpack.config.js后,启动无报错,但控制台出现大量React组件渲染相关异常(如React.createElement: type is invalid -- expected a string or a class/function but got: undefined),未修改任何业务代码,仅调整了配置,本地启动测试阶段无法定位问题。

当前Webpack配置(webpack serve)

const path = require("path");
const platformDir = path.resolve("../../../../../platform-web");
const sourceDir = path.resolve("./src");
const publicDir = path.resolve("./public");
const resourcesFile = path.join(sourceDir, "styles/_resources.scss");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { HotModuleReplacementPlugin } = require("webpack");

module.exports = {
  mode: "development",
  entry: path.join(sourceDir, "index.ts"),
  output: {
    path: path.resolve(__dirname, "build"),
    filename: "[name].[contenthash:8].js"
  },
  resolve: {
    mainFiles: ["index"],
    extensions: [".tsx", ".ts", ".js", ".jsx"],
    modules: [
      path.resolve("./src"), "./node_modules", path.join(platformDir, "./node_modules")
    ],
    alias: {
      resources: resourcesFile,
      "@platform-web": path.resolve(__dirname, "../../../../../platform-web/src")
    },
  },
  plugins: [
    new HotModuleReplacementPlugin(),
    new HtmlWebpackPlugin({
      template: path.join(publicDir, "index.html")
    })
  ],
  module: {
    rules: [
      {
        test: /\.(ts|tsx|js|jsx)$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader"
        },
      },
      {
        test: /\.css$/i,
        use: ["style-loader", "css-loader"]
      },
      {
        test: /\.s[ac]ss$/i,
        use: [
          "style-loader",
          "css-loader",
          {
            loader: "sass-loader",
            options: {
              sassOptions: {
                includePaths: [path.resolve(platformDir, "./src/styles"), "./src/styles"],
              },
            },
          },
        ],
      },
      {
        test: /\.(png|jpe?g|woff2)$/i,
        use: {
          loader: "file-loader"
        }
      },
      {
        test: /\.svg$/,
        use: ["@svgr/webpack"]
      }
    ],
  },
  devServer: {
    port: 5100,
    hot: true,
    proxy: [{
      context: ["/_api"],
      target: "https://some.url/",
      changeOrigin: true
    }],
  },
};

Babel配置(babel.config.js)

module.exports = {
  presets: [
    ["@babel/preset-env", {
      modules: false,
    }],
    ["@babel/preset-react", {"runtime": "automatic"}],
    "@babel/preset-typescript"
  ],
  plugins: [
    ["@babel/plugin-syntax-import-attributes", { "legacy": true }],
    ["@babel/plugin-transform-class-properties", { "loose": true }],
    ["@babel/plugin-transform-private-property-in-object", { "loose": true }],
    ["@babel/plugin-transform-private-methods", { "loose": true }]
  ]
};

原Craco配置(craco.config.js)

const path = require("path");
const CracoAlias = require("craco-alias");

const platformDir = path.resolve("../../../../platform-web");
const platformSrc = path.join(platformDir, "src");
const sourceDir = path.resolve("./src");
const resourcesFile = path.join(sourceDir, "styles/_resources.scss");

module.exports = {
  reactScriptsVersion: "react-scripts",
  plugins: [
    {
      plugin: CracoAlias,
      options: {
        source: "tsconfig",
        baseUrl: "./src",
        tsConfigPath: "./tsconfig.extend.json",
      },
    },
  ],
  style: {
    css: {
      loaderOptions: {
        modules: {
          auto: true,
          exportLocalsConvention: "camelCase",
        },
      },
    },
  },
  webpack: {
    configure: (cfg) => {
      const scopePluginIndex = cfg.resolve.plugins.findIndex(
        ({ constructor }) =>
          constructor && constructor.name === "ModuleScopePlugin"
      );

      cfg.resolve.modules = [
        sourceDir,
        path.resolve("./node_modules"),
        path.join(platformDir, "./node_modules"),
      ];
      cfg.resolve.alias = { resources: resourcesFile };
      cfg.resolve.plugins.splice(scopePluginIndex, 1);

      const oneOfRule = cfg.module.rules.find((rule) => rule.oneOf);
      const tsRule = oneOfRule.oneOf.find((rule) =>
        rule.test.toString().includes("ts|tsx")
      );

      tsRule.include = [sourceDir, platformSrc];
      tsRule.exclude = /node_modules/;

      return cfg;
    },
  },
  babel: {
    plugins: [
      ["@babel/plugin-syntax-import-attributes", { "legacy": true }],
      ["@babel/plugin-transform-class-properties", { "loose": true }],
      ["@babel/plugin-transform-private-property-in-object", { "loose": true }],
      ["@babel/plugin-transform-private-methods", { "loose": true }]
    ],
    presets: [
      ["@babel/preset-env", {
        modules: false, //Setting this to false will preserve ES services
      }],
      ["@babel/preset-react", {"runtime": "automatic"}],
      "@babel/preset-typescript"
    ],
  },
  devServer: {
    port: 5100,
    proxy: {
      "/_api": {
      target: "https://some.url/",
        secure: true,
        changeOrigin: true
      },
    },
  }
};

排查与修复方案

1. 补全跨目录代码的转译范围

原Craco配置中明确将platform-web/src加入了转译范围,但当前Webpack的JS/TS规则未包含该目录,导致外部组件未被Babel处理,出现类型无效错误。修改Webpack的module.rules:

{
  test: /\.(ts|tsx|js|jsx)$/,
  include: [sourceDir, path.resolve(__dirname, "../../../../../platform-web/src")],
  exclude: /node_modules/,
  use: {
    loader: "babel-loader"
  },
}

2. 恢复CSS模块配置

原Craco开启了CSS模块的自动识别和驼峰命名转换,当前Webpack未配置该规则,可能导致样式导入异常间接影响组件。修改css-loader配置:

// CSS规则
{
  test: /\.css$/i,
  use: [
    "style-loader",
    {
      loader: "css-loader",
      options: {
        modules: {
          auto: true,
          exportLocalsConvention: "camelCase"
        }
      }
    }
  ]
},
// SCSS规则
{
  test: /\.s[ac]ss$/i,
  use: [
    "style-loader",
    {
      loader: "css-loader",
      options: {
        modules: {
          auto: true,
          exportLocalsConvention: "camelCase"
        }
      }
    },
    {
      loader: "sass-loader",
      options: {
        sassOptions: {
          includePaths: [path.resolve(platformDir, "./src/styles"), "./src/styles"],
        },
      },
    },
  ],
}

3. 确认React运行时依赖

@babel/preset-react启用runtime: "automatic"时,需确保安装了react/jsx-runtime依赖,执行以下命令确认:

npm install react react-dom @babel/plugin-transform-react-jsx-runtime --save

4. 同步别名与TS配置

原Craco通过craco-alias读取tsconfig的别名,当前Webpack手动配置的别名需与tsconfig.extend.json完全一致,避免导入路径错误导致组件找不到。

5. 添加开发环境SourceMap

在Webpack配置中添加devtool,方便定位错误位置:

module.exports = {
  // ...其他配置
  devtool: "eval-cheap-module-source-map"
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 00:28:14