Create React App

本章节介绍如何将 Create React App(简称 CRA)或 CRACO 项目迁移到 Rsbuild。

CRA eject

如果你的项目已经执行了 CRA 的 eject 命令,那么本文档的大部分内容将不再适用。

CRA 项目在 eject 后,更近似于直接使用 webpack 的项目,因此你可以参考 webpack 迁移指南

安装依赖

首先你需要把 CRA 的 npm 依赖替换为 Rsbuild 的依赖。

  • 移除 CRA 的依赖:
npm
yarn
pnpm
bun
npm remove react-scripts

对于使用 CRACO 的项目,你还可以移除 @craco/craco 依赖。

  • 安装 Rsbuild 的依赖:
npm
yarn
pnpm
bun
npm add @rsbuild/core @rsbuild/plugin-react -D

更新 npm scripts

下一步,你需要把 package.json 中的 npm scripts 更新为 Rsbuild 的 CLI 命令。

package.json
{
  "scripts": {
-   "start": "react-scripts start",
-   "build": "react-scripts build",
-   "eject": "react-scripts eject"
+   "start": "rsbuild dev",
+   "build": "rsbuild build",
+   "preview": "rsbuild preview"
  }
}
TIP

Rsbuild 未集成测试框架,因此没有提供用于替换 react-scripts test 的命令,你可以直接使用 Jest 或 Vitest 等测试框架。你可以参考 Rsbuild react-jest 示例项目进行配置。

创建配置文件

在 package.json 的同级目录下创建 Rsbuild 的配置文件 rsbuild.config.ts,并添加以下内容:

rsbuild.config.ts
import { defineConfig } from '@rsbuild/core';
import { pluginReact } from '@rsbuild/plugin-react';

export default defineConfig({
  plugins: [pluginReact()],
});

HTML 模板

CRA 默认使用 public/index.html 文件作为 HTML 模板。在 Rsbuild 中,你可以通过 html.template 来指定 HTML 模板:

rsbuild.config.ts
export default defineConfig({
  html: {
    template: './public/index.html',
  },
});

在 HTML 模板中,如果使用了 CRA 的 %PUBLIC_URL% 变量,请替换为 Rsbuild 的 assetPrefix 变量,并使用斜杠进行连接:

-  <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
+  <link rel="icon" href="<%= assetPrefix %>/favicon.ico" />

这样就完成了从 CRA 到 Rsbuild 的基本迁移,此时你可以执行 npm run start 命令来尝试启动开发服务器。

构建产物目录

CRA 默认会将构建产物输出到 build 目录,而 Rsbuild 的默认构建产物目录为 dist

你可以配置 Rsbuild 的 output.distPath.root 选项,将目录修改为 build,与 CRA 保持一致:

rsbuild.config.ts
export default {
  output: {
    distPath: {
      root: 'build',
    },
  },
};

更多细节请参考构建产物目录章节。

使用 CSS 预处理器

Rsbuild 通过插件来支持 Sass 和 Less 等 CSS 预处理器,使用方式请参考:

使用 SVGR

如果你使用了 CRA 的 "SVG 转 React 组件" 功能(即 SVGR),那么还需要安装 Rsbuild 的 SVGR 插件。

比如使用了以下用法:

import { ReactComponent as Logo } from './logo.svg';

const App = () => (
  <div>
    <Logo />
  </div>
);

只需要安装和注册 @rsbuild/plugin-svgr 即可:

rsbuild.config.ts
import { pluginSvgr } from '@rsbuild/plugin-svgr';

export default {
  plugins: [pluginSvgr({ mixedImport: true })],
};

请参考 SVGR 插件 文档了解如何在 Rsbuild 中使用 SVGR。

配置迁移

以下是 CRA 配置对应的 Rsbuild 配置:

CRA Rsbuild
HOST server.host
PORT server.port
HTTPS server.https
WDS_SOCKET_HOST dev.client.host
WDS_SOCKET_PATH dev.client.path
WDS_SOCKET_PORT dev.client.port
PUBLIC_URL dev.assetPrefix / output.assetPrefix
BUILD_PATH output.distPath
GENERATE_SOURCEMAP output.sourceMap
IMAGE_INLINE_SIZE_LIMIT output.dataUriLimit
FAST_REFRESH dev.hmr
TSC_COMPILE_ON_ERROR @rsbuild/plugin-type-check

说明:

  • 上述表格尚未覆盖到 CRA 的所有配置,欢迎补充。

编译 node_modules

CRA 默认会使用 Babel 编译 node_modules 中的依赖,但 Rsbuild 则不会,这是为了避免二次编译带来的性能损耗和潜在的编译错误。

如果你需要处理 node_modules 中依赖引起的语法兼容性问题,可以使用 source.include 配置项来编译 node_modules。

环境变量

CRA 默认会将 REACT_APP_ 开头的环境变量注入到 client 代码中,而 Rsbuild 默认会注入 PUBLIC_ 开头的环境变量(参考 public 变量)。

为了兼容 CRA 的行为,你可以手动调用 Rsbuild 提供的 loadEnv 方法来读取 REACT_APP_ 开头的环境变量,并通过 source.define 配置项注入到 client 代码中。

rsbuild.config.ts
import { defineConfig, loadEnv } from '@rsbuild/core';

const { publicVars } = loadEnv({ prefixes: ['REACT_APP_'] });

export default defineConfig({
  source: {
    define: publicVars,
  },
});

值得注意的是,CRA 允许在代码中访问完整的 process.env 对象,也允许对 process.env 进行解构。而 Rsbuild 出于包体积和安全的考虑,并没有注入完整的 process.env 对象。

src/index.js
// CRA 中可以这样访问
const { PUBLIC_URL } = process.env;
console.log(PUBLIC_URL);
console.log(process.env);

在 Rsbuild 中,你可以通过 source.define 配置项设置 process.env,读取 loadEnv 方法返回的 rawPublicVars,来允许以上用法:

rsbuild.config.ts
import { defineConfig, loadEnv } from '@rsbuild/core';

const { publicVars, rawPublicVars } = loadEnv({ prefixes: ['REACT_APP_'] });

export default defineConfig({
  source: {
    define: {
      ...publicVars,
      'process.env': JSON.stringify(rawPublicVars),
    },
  },
});

引用未知资源

在 CRA 中,如果你引用了一个构建工具无法识别的资源,CRA 默认会将该文件输出到 build/static/media 目录下,比如 document.pdf 文件:

index.js
import document from './document.pdf';

在 Rsbuild 中,遇到无法识别的资源时,Rsbuild 会输出报错日志,类似于:

You may need an appropriate loader to handle this file type.

为了解决该错误,你可以采用以下方法:

比如,你可以添加以下 asset modules 配置,来保持与 CRA 相同的输出结果:

rsbuild.config.ts
export default {
  tools: {
    rspack: {
      module: {
        rules: [
          {
            // 匹配 .png 文件
            // 你可以调整该正则表达式,以匹配不同类型的文件
            test: /\.png$/,
            type: 'asset/resource',
            generator: {
              filename: 'static/media/[name].[hash][ext]',
            },
          },
        ],
      },
    },
  },
};

移除 react-app-polyfill

CRA 提供了 react-app-polyfill 来手动注入 polyfill 代码。

在 Rsbuild 项目中,你可以移除 react-app-polyfill 相关的依赖和代码,因为 Rsbuild 会自动读取 browserslist 配置,并按需注入 polyfill 代码。

src/index.js
- import 'react-app-polyfill/ie11';
- import 'react-app-polyfill/stable';

你可以参考 浏览器兼容性 来了解 Rsbuild 是如何处理 polyfill 的。

添加 ESLint 插件

CRA 默认启用了 eslint-webpack-plugin,并内置了一套 ESLint 规则集。

在 Rsbuild 项目中,如果你需要保持类似的行为,可以添加 @rsbuild/plugin-eslint,并使用 eslint-config-react-app 作为 ESLint 规则集。

.eslintrc.cjs
module.exports = {
  extends: 'react-app',
};

读取 jsconfig.json

在非 TypeScript 项目中,CRA 支持读取 jsconfig.json 中的 paths 字段作为路径别名。

如果你需要在 Rsbuild 中使用该特性,可以使用 source.tsconfigPath 选项,使 Rsbuild 能够识别 jsconfig.json 中的 paths 字段。

rsbuild.config.mjs
export default {
  source: {
    tsconfigPath: './jsconfig.json',
  },
};

CRACO 迁移

如果你的项目使用了 CRACO 来覆盖 CRA 的配置,可以参考下表来迁移:

CRACO Rsbuild
webpack.configure tools.rspack
webpack.alias resolve.alias
webpack.plugins.add appendPlugins of tools.rspack
webpack.plugins.remove removePlugin of tools.rspack
style.modules output.cssModules
style.css tools.cssLoader
style.sass Sass 插件
style.postcss tools.postcss
babel Babel 插件
typescript @rsbuild/plugin-type-check
devServer server configs

示例

下面是一个从 webpack.configure 迁移到 tools.rspack 的例子:

  • 迁移前:
craco.config.js
const { whenDev } = require('@craco/craco');

module.exports = {
  webpack: {
    configure: {
      resolve: {
        mainFields: ['browser', 'module', 'main'],
      },
    },
    plugins: [...whenDev(() => [new MyWebpackPlugin()], [])],
  },
};
  • 迁移后:
rsbuild.config.ts
export default {
  tools: {
    rspack: {
      resolve: {
        mainFields: ['browser', 'module', 'main'],
      },
      plugins:
        process.env.NODE_ENV === 'development' ? [new MyWebpackPlugin()] : [],
    },
  },
};

内容补充

当前文档只涵盖了迁移过程的部分事项,如果你发现有合适的内容可以补充,欢迎通过 pull request 来完善文档 🤝。

Rsbuild 的文档位于 rsbuild/website 目录。