不要使用 create-react-app:如何设置自己的 reactjs 样板。

2025-05-25

不要使用 create-react-app:如何设置自己的 reactjs 样板。

什么是 CRA?

Create React App 是由 Facebook 的开发者构建和维护的工具链,用于引导 React 应用程序。只需运行一个命令,Create React App 就会设置启动 React 项目所需的工具。

CRA的优势

  • 使用单个命令开始
npx create-react-app my-app
Enter fullscreen mode Exit fullscreen mode
  • 学习内容更少。您只需专注于 React,无需担心 webpack、babel 和其他类似的构建依赖项。
  • 仅需一个构建依赖项react-scripts。这将维护所有构建依赖项,因此只需一个命令即可轻松维护和升级。
npm install react-scripts@latest
Enter fullscreen mode Exit fullscreen mode

CRA的缺点

  • 添加自定义构建配置很困难。添加自定义配置的一种方法是弹出应用,但这会覆盖“只有一个构建依赖项”的优势。另一种方法是使用像customize-crareact-app-rewired这样的软件包,但它们的功能有限。
  • 抽象一切。理解运行 React 应用所需的一切至关重要。但由于 React只有一个构建依赖项的优势,初学者可能会认为这react-scripts是运行 React 应用所需的唯一依赖项,而可能不知道编译器 (babel) 和打包器 (webpack) 是 React 后台使用的关键依赖项。直到我读到这篇很棒的文章react-scripts,我才意识到这一点
  • 在我看来,CRA 太臃肿了。比如,如果你正在使用 SASS,CRA 会自带 SASS 支持,plain CSS或者Less它是一个你永远不会使用的额外依赖项。这是一个已移除的 CRA 应用的package.json文件。

CRA 的替代方案是自行设置样板。CRA 唯一的优势在于只需一个命令即可上手,并且可以通过自行设置依赖项和配置来消除其所有缺点。我们无法利用 CRA 的另外两个优势,因为它引入了两个缺点(抽象所有内容和难以添加自定义构建配置)。

这个repo 包含本博文中使用的所有代码。

首先,使用 npm 和 git 初始化你的项目

npm init
git init
Enter fullscreen mode Exit fullscreen mode

让我们快速创建一个 .gitignore 文件来忽略以下文件夹

node_modules
build
Enter fullscreen mode Exit fullscreen mode

现在,让我们看看运行 React 应用程序需要哪些基本依赖项。

react 和 react-dom

这是您需要的仅有的两个运行时依赖项。

npm install react react-dom --save
Enter fullscreen mode Exit fullscreen mode

转译器(Babel)

Transpiler 将 ECMAScript 2015+ 代码转换为在当前浏览器和老版本浏览器中向后兼容的 JavaScript 版本。我们还使用它通过添加预设来转译 JSX。

npm install @babel/core @babel/preset-env @babel/preset-react --save-dev 
Enter fullscreen mode Exit fullscreen mode

React 应用的简单 babel 配置如下所示。你可以将此配置添加到 .babelrc 文件,也可以将其作为 package.json 中的属性添加。

{
    "presets": [
        "@babel/preset-env",
        "@babel/preset-react"
    ]
}
Enter fullscreen mode Exit fullscreen mode

您可以根据需要添加各种预设插件。

捆绑器(Webpack)

Bundler 将您的代码及其所有依赖项捆绑在一个捆绑文件中(如果您使用代码拆分,则可以捆绑更多)。

npm install webpack webpack-cli webpack-dev-server babel-loader css-loader style-loader html-webpack-plugin --save-dev 
Enter fullscreen mode Exit fullscreen mode

React 应用程序的简单 webpack.config.js 如下所示。

const path = require('path');
const HtmlWebPackPlugin = require('html-webpack-plugin');

module.exports = {
  output: {
    path: path.resolve(__dirname, 'build'),
    filename: 'bundle.js',
  },
  resolve: {
    modules: [path.join(__dirname, 'src'), 'node_modules'],
    alias: {
      react: path.join(__dirname, 'node_modules', 'react'),
    },
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
        },
      },
      {
        test: /\.css$/,
        use: [
          {
            loader: 'style-loader',
          },
          {
            loader: 'css-loader',
          },
        ],
      },
    ],
  },
  plugins: [
    new HtmlWebPackPlugin({
      template: './src/index.html',
    }),
  ],
};
Enter fullscreen mode Exit fullscreen mode

您可以根据需要添加各种加载器。请查看我关于webpack 优化的博客文章,其中我讨论了各种可以添加的 webpack 配置,以使您的 React 应用可以投入生产。

这就是我们需要的所有依赖项。现在让我们添加一个 HTML 模板文件和一个 React 组件。

让我们创建 src 文件夹并添加 index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>React Boilerplate</title>
</head>
<body>
  <div id="root"></div>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

让我们在 src 文件夹中创建一个 HelloWorld.js React 组件

import React from 'react';

const HelloWorld = () => {
  return (
      <h3>Hello World</h3>
  );
};

export default HelloWorld;
Enter fullscreen mode Exit fullscreen mode

我们将 index.js 文件添加到 src 文件夹

import React from 'react';
import { render } from 'react-dom';

import HelloWorld from './HelloWorld';

render(<HelloWorld />, document.getElementById('root'));
Enter fullscreen mode Exit fullscreen mode

最后,我们在 package.json 中添加启动和构建脚本

"scripts": {
    "start": "webpack-dev-server --mode=development --open --hot",
    "build": "webpack --mode=production"
  }
Enter fullscreen mode Exit fullscreen mode

就是这样。现在我们的 React 应用可以运行了。试试命令npm startnpm run build

现在,让我们实现CRA 的“单命令入门”优势。简单来说,我们将使用一个可执行的 JS 文件,当我们在命令行中输入特定命令(您的样板名称)时,该文件就会运行。例如,reactjs-boilerplate new-project为此,我们将使用package.json 中的bin属性。

首先创建可执行的 JS 文件。安装fs-extra

npm i fs-extra
Enter fullscreen mode Exit fullscreen mode

bin/start.js在项目根目录下创建包含以下内容的文件。

#!/usr/bin/env node
const fs = require("fs-extra");
const path = require("path");
const https = require("https");
const { exec } = require("child_process");

const packageJson = require("../package.json");

const scripts = `"start": "webpack-dev-server --mode=development --open --hot",
"build": "webpack --mode=production"`;

const babel = `"babel": ${JSON.stringify(packageJson.babel)}`;

const getDeps = (deps) =>
  Object.entries(deps)
    .map((dep) => `${dep[0]}@${dep[1]}`)
    .toString()
    .replace(/,/g, " ")
    .replace(/^/g, "")
    // exclude the dependency only used in this file, nor relevant to the boilerplate
    .replace(/fs-extra[^\s]+/g, "");

console.log("Initializing project..");

// create folder and initialize npm
exec(
  `mkdir ${process.argv[2]} && cd ${process.argv[2]} && npm init -f`,
  (initErr, initStdout, initStderr) => {
    if (initErr) {
      console.error(`Everything was fine, then it wasn't:
    ${initErr}`);
      return;
    }
    const packageJSON = `${process.argv[2]}/package.json`;
    // replace the default scripts
    fs.readFile(packageJSON, (err, file) => {
      if (err) throw err;
      const data = file
        .toString()
        .replace(
          '"test": "echo \\"Error: no test specified\\" && exit 1"',
          scripts
        )
        .replace('"keywords": []', babel);
      fs.writeFile(packageJSON, data, (err2) => err2 || true);
    });

    const filesToCopy = ["webpack.config.js"];

    for (let i = 0; i < filesToCopy.length; i += 1) {
      fs.createReadStream(path.join(__dirname, `../${filesToCopy[i]}`)).pipe(
        fs.createWriteStream(`${process.argv[2]}/${filesToCopy[i]}`)
      );
    }

    // npm will remove the .gitignore file when the package is installed, therefore it cannot be copied, locally and needs to be downloaded. Use your raw .gitignore once you pushed your code to GitHub.
    https.get(
      "https://raw.githubusercontent.com/Nikhil-Kumaran/reactjs-boilerplate/master/.gitignore",
      (res) => {
        res.setEncoding("utf8");
        let body = "";
        res.on("data", (data) => {
          body += data;
        });
        res.on("end", () => {
          fs.writeFile(
            `${process.argv[2]}/.gitignore`,
            body,
            { encoding: "utf-8" },
            (err) => {
              if (err) throw err;
            }
          );
        });
      }
    );

    console.log("npm init -- done\n");

    // installing dependencies
    console.log("Installing deps -- it might take a few minutes..");
    const devDeps = getDeps(packageJson.devDependencies);
    const deps = getDeps(packageJson.dependencies);
    exec(
      `cd ${process.argv[2]} && git init && node -v && npm -v && npm i -D ${devDeps} && npm i -S ${deps}`,
      (npmErr, npmStdout, npmStderr) => {
        if (npmErr) {
          console.error(`Some error while installing dependencies
      ${npmErr}`);
          return;
        }
        console.log(npmStdout);
        console.log("Dependencies installed");

        console.log("Copying additional files..");
        // copy additional source files
        fs.copy(path.join(__dirname, "../src"), `${process.argv[2]}/src`)
          .then(() =>
            console.log(
              `All done!\n\nYour project is now ready\n\nUse the below command to run the app.\n\ncd ${process.argv[2]}\nnpm start`
            )
          )
          .catch((err) => console.error(err));
      }
    );
  }
);

Enter fullscreen mode Exit fullscreen mode

现在让我们用命令映射可执行的 JS 文件。将其粘贴到你的 package.json 中

"bin": {
    "your-boilerplate-name": "./bin/start.js"
  }

Enter fullscreen mode Exit fullscreen mode

现在让我们通过运行在本地链接包(样板)

npm link
Enter fullscreen mode Exit fullscreen mode

现在,当在终端(命令提示符)中输入此命令时,我们的可执行文件将被调用,它会创建一个名为的新文件夹,复制、、,your-boilerplate-name my-appstart.jsmy-app项目package.json安装依赖项。webpack.config.jsgitignoresrc/my-app

太棒了,现在它在你的本地运行了。你只需一个命令就可以引导 React 项目(使用你自己的构建配置)。

您还可以更进一步,将样板发布到npm 注册表。首先,提交并推送代码到 GitHub,然后按照以下说明操作。

太棒了!我们在几分钟内就创建了 create-react-app 的替代方案,它不臃肿(您可以根据需要添加依赖项),并且更容易添加/修改构建配置。

当然,我们的设置非常简陋,肯定还不能用于生产环境。你需要添加一些 webpack 配置来优化构建。

我创建了一个reactjs-boilerplate,其中包含可用于生产的版本、linters 和预提交钩子。不妨一试。欢迎提出建议和贡献。

回顾

  • 我们看到了 CRA 的优点和缺点。
  • 我们决定利用 CRA 的单一命令优势,将其应用于我们的项目并消除其所有缺点。
  • 我们添加了运行 React 应用程序所需的最少 webpack 和 babel 配置
  • 我们创建了一个 HelloWorld.js 反应组件,使用开发服务器运行它,并构建它。
  • 我们创建了一个可执行的 JS 文件,并通过 package.json 中的 bin 属性将其与命令名称映射。
  • 我们曾经npm link链接我们的样板,并使我们样板用单个命令来引导新的反应项目。

好了,各位,感谢阅读这篇博文。希望它对您有所帮助。欢迎留言提出您的问题和建议。

参考

文章来源:https://dev.to/nikhilkumaran/don-t-use-create-react-app-how-you-can-set-up-your-own-reactjs-boilerplate-43l0
PREV
2024 年成为 Java 后端开发人员的路线图
NEXT
使用 HTML CSS 和 GSAP 制作动画个人资料卡