create-react-app
(CRA),并为React应用程序创建自己的模板。在这里,我们将讨论CRA的优缺点,并提出可以替代的解决方案create-react-app
。
什么是CRA?
Create React App是由Facebook开发人员构建和维护的一套工具。CRA用于快速创建模板化的React应用程序。使用CRA,只需一个命令即可创建React项目库。
CRA的优势
- CRA允许您使用一个命令为React项目创建基础:
npx create-react-app my-app
- 使用CRA可使开发人员免于对辅助工具进行深入研究的需要。开发人员可以专注于React,而不必担心配置Webpack,Babel和其他实用程序机制。
- 在应用CRA时,开发人员只需要一个与构建项目相关的依赖项。这是
react-scripts
。此依赖关系包括所有其他程序集依赖关系,结果证明,例如,一个命令足以安装和更新依赖关系:
npm install react-scripts@latest
CRA
- CRA .
eject
, CRA- . customize-cra react-app-rewired, . - CRA . React- , React- . CRA « », ,
react-scripts
— , React-. , , ,react-scripts
— , , «» (Babel) (Webpack), React- . , , , . - 在我看来,CRA充满了很多机会,在某些项目中,这些机会很可能是无人认领的。例如,使用CRA构建的应用程序存根支持SASS。也就是说,如果您的项目使用常规CSS或更少的CSS,则完全不需要SASS支持。如果您对此感兴趣,请在此处输入
package.json
CRA应用程序文件eject
。此文件“展开”先前显示的依赖项react-scripts
。
CRA的替代方法是开发自己的模板,以快速创建基本的React项目。
替代CRA
在开发CRA替代方案时,我们将使它具备仅使用一个命令即可快速创建基本React项目的能力。这重复了有用的功能之一
create-react-app
。当然,我们不会将CRA的缺点转移到我们的系统中,而不会独立安装依赖项并设置项目。我们的项目将不包括其他两个有用的CRA功能(使开发人员不必研究辅助机制和“一个依赖”方案),因为它们也具有缺点(隐藏了开发者辅助子系统的内部机制以及自定义其自己的项目构建配置的复杂性) ...
这是存储库,其中包含我们将在本文中讨论的所有代码。
让我们从使用工具
npm
初始化项目并初始化其git存储库开始:
npm init
git init
让我们创建一个包含
.gitignore
以下内容的文件:
node_modules
build
这将使我们不将名称存在于文件中的文件夹包括在存储库中。
现在让我们考虑一下构建和运行React应用程序所需的基本依赖项。
React和React-dom库
这些是我们需要的唯一运行时依赖项:
npm install react react-dom --save
Transpiler(通天塔)
Babel编译器将符合ECMAScript 2015+的代码转换为可在新版和旧版浏览器中使用的代码。由于使用了预设,Babel还用于处理JSX代码:
npm install @babel/core @babel/preset-env @babel/preset-react --save-dev
这就是一个简单的Babel配置,它可以使您的React应用程序启动并运行。可以将此配置添加到文件
.babelrc
或package.json
:
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
]
}
Babel支持许多预设和插件。可以在需要时将它们添加到项目中。
班德勒(Webpack)
Webpack捆绑器负责构建项目,并根据项目代码及其依赖关系的代码形成单个应用程序文件(捆绑)。使用代码拆分等项目优化技术时,应用程序捆绑包可以包含多个文件。
npm install webpack webpack-cli webpack-dev-server babel-loader css-loader style-loader html-webpack-plugin --save-dev
用于构建React应用程序包的简单Webpack配置如下所示:
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',
}),
],
};
根据特定应用程序的需要,可以在此处添加各种引导程序。如果您对此主题感兴趣,请阅读我的文章,其中讨论了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>
让我们在同一文件夹中创建一个React组件
HelloWorld
:
import React from 'react';
const HelloWorld = () => {
return (
<h3>Hello World</h3>
);
};
export default HelloWorld;
将文件添加到同一文件夹
index.js
:
import React from 'react';
import { render } from 'react-dom';
import HelloWorld from './HelloWorld';
render(<HelloWorld />, document.getElementById('root'));
最后,添加到
package.json
用于启动(start
)和构建(build
)项目的脚本描述:
"scripts": {
"start": "webpack-dev-server --mode=development --open --hot",
"build": "webpack --mode=production"
}
就这样。现在,我们可以使用一个可行的存根React应用程序。您可以通过运行命令
npm start
和进行验证npm run build
。
现在,让我们的系统具备使用单个命令准备项目模板的能力。也就是说,我们将重建CRA的优势之一。我们将使用一个可执行的JS文件,当您在命令行中输入适当的命令时,该文件将被调用。例如,这样的命令可能如下所示:
reactjs-boilerplate new-project
为了实现这个想法,我们将使用file的bin部分
package.json
。
首先,安装fs-extra软件包:
npm i fs-extra
现在,让我们创建一个可执行的JS文件
start.js
,该文件将位于bin
我们的项目文件夹中。让我们在文件中添加以下代码:
#!/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, "")
// , ,
.replace(/fs-extra[^\s]+/g, "");
console.log("Initializing project..");
// 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`;
// ,
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, , .gitignore, ; . GitHub- raw- .gitignore
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");
//
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..");
//
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));
}
);
}
);
现在,让该JS可执行文件与来自的命令链接
package.json
:
"bin": {
"your-boilerplate-name": "./bin/start.js"
}
让我们为包创建一个本地链接:
npm link
现在,执行完该命令后,如果我们在终端中执行格式如下的命令
your-boilerplate-name my-app
,我们的可执行文件将被调用start.js
。这将创建一个新的文件夹的名称my-app
,复制文件package.json
,webpack.config.js
并且.gitignore
,还有文件夹src
,并安装新的项目的依赖my-app
。
精彩。现在,它们都可以在您的计算机上运行,并允许您使用单个命令使用其自己的构建配置来创建基本的React项目。
您可以继续并将模板发布到npm注册表。为此,您首先需要将项目提交到GitHub存储库。然后按照以下说明进行操作。
恭喜你!我们仅在几分钟内创建了一个替代方案
create-react-app
。我们的解决方案不会因不必要的功能而过载(可以在需要时将依赖项添加到基于它的项目中)。使用它,您可以轻松调整项目构建配置以适合您的需求。
当然,我们的解决方案是简约的。基于其创建的项目不能视为已准备好用于生产。为了使它们准备好进行实际工作,我们需要为模板配备一些负责优化项目构建的Webpack设置。
我已经准备好reactjs-boilerplate模板这样您就可以创建准备在生产中工作的项目。它使用适当的构建配置,lint和挂钩在创建提交之前负责检查项目。试试这个模板。如果您有任何改进的想法,或者您决定为它的发展做出贡献,请加入有关它的工作。
结果
这是我们在本文中讨论的内容:
- 我们已经讨论了利弊
create-react-app
。 - 我们在项目中实现了一种有用的CRA功能,可使用一个命令创建空白的React应用程序。我们摆脱了CRA的缺点。
- 我们为项目配备了构建和运行React应用程序所需的最小Webpack和Babel配置。
- 我们创建了一个React组件
HelloWorld.js
,提供了构建项目并使用开发服务器运行它的能力。 - 我们创建了一个可执行的JS文件,并使用
bin
file部分将其链接到相应的命令package.json
。 - 我们使用团队
npm link
为我们的项目创建本地链接,并能够通过一个团队从中创建新的基准项目。
我希望本文能帮助您开发基于React的项目。
创建新的React项目时是否使用create-react-app?