反应不只是“工作”的盒子。它使用的关键字和语法,node(在本教程中使用的v. 9.3.0)还没有完全跟上。它需要相当多的设置,搞清楚可能很麻烦,而Facebook提供了一个选项,让启动React应用变得容易。为什么要这么麻烦?
问题是,create- response -app抽象了很多让React应用工作时远离你的东西——至少不用弹出它,也不用手动调整所有选项。您可能想要自己实现的原因有很多,或者至少对它的底层功能有一些了解。
正如我所提到的,启动React应用程序有几个障碍。首先是node不能处理所有语法(比如import/export和jsx)。第二,在开发过程中,你要么需要构建文件,要么以某种方式为你的应用程序提供服务——这在后一种情况下尤其重要。
幸运的是,我们可以用Babel和Webpack处理这些问题。
设置
首先,为你的React应用创建一个新目录。然后,用npm init
初始化你的项目,并在你选择的编辑器中打开它。这也是一个使用git init
的好时机。在您的新项目文件夹中,创建以下结构:
.
+--public
+--src
稍微提前考虑一下,我们最终想要构建应用程序,并且可能想要从提交中排除构建版本和节点模块,所以让我们继续添加一个.gitignore
文件(至少)去排除node_modules
和dist
目录
我们的public
目录将处理所有静态资产,最重要的是包含我们的index.html
文件,该文件将用于渲染你的应用。
下面的代码来自react文档,只做了一些非常细微的修改。请随意将以下HTML标识文本复制到public
目录中的新文件index.html
中。
<!-- sourced from https://raw.githubusercontent.com/reactjs/reactjs.org/master/static/html/single-file-example.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>React Starter</title>
</head>
<body>
<div id="root"></div>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<script src="../dist/bundle.js"></script>
</body>
</html>
需要注意的最重要的一行是12,这是我们的React应用将钩子到的根目录和第16行,它引用了我们(即将)构建的react应用程序。你可以任意命名你的构建脚本,但在本教程中我将使用bundle.js。
现在我们已经设置好了HTML页面,我们可以开始认真了。我们还需要设置一些东西。首先,我们需要确保我们编写的代码可以被编译,因此我们需要Babel。
Babel
继续运行
npm install—save-dev @babel/core@7.1.0 @babel/cli@7.1.0 @babel/preset-env@7.1.0 @babel/preset-react@7.0.0
babel-core
是babel的主要包——我们需要它来对我们的代码进行任何转换。babel-cli
允许您从命令行编译文件。preset-react
和preset-env
都是转换特定类型代码的预定义值——在本例中,env预定义值允许我们将ES6+转换为更传统的javascript, react预定义值也可以实现同样的功能,但使用的是JSX。
在项目根目录中,创建一个名为.babelrc
的文件。这里,我们告诉babel我们将使用env
和react
预设。
{
"presets": ["@babel/env", "@babel/preset-react"]
}
Webpack
现在我们需要获取和配置Webpack。我们还需要更多的包,您需要将它们保存为dev依赖项:
npm install—save-dev webpack@4.19.1 webpack-cli@3.1.1 webpack-dev-server@3.1.8 style-loader@0.23.0 css-loader@1.0.0 babel-loader@8.0.2
Webpack使用loader来处理不同类型的文件进行打包。它还可以很容易地与开发服务器一起工作,我们将使用开发服务器来为我们的React项目提供服务,在对React组件的更改(已保存)上重新加载浏览器页面。为了利用这些,我们需要配置Webpack来使用加载器并准备dev服务器。
在项目的根目录下创建一个名为webpack.config.js
的新文件。这个文件导出一个带有webpack配置的对象。
const path = require("path");
const webpack = require("webpack");
module.exports = {
entry: "./src/index.js",
mode: "development",
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /(node_modules|bower_components)/,
loader: "babel-loader",
options: { presets: ["@babel/env"] },
},
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
],
},
resolve: { extensions: ["*", ".js", ".jsx"] },
output: {
path: path.resolve(__dirname, "dist/"),
publicPath: "/dist/",
filename: "bundle.js",
},
devServer: {
contentBase: path.join(__dirname, "public/"),
port: 3000,
publicPath: "http://localhost:3000/dist/",
hotOnly: true,
},
plugins: [new webpack.HotModuleReplacementPlugin()],
};
让我们快速浏览一下:entry
(第4行)告诉Webpack我们的应用程序从哪里开始,以及从哪里开始打包我们的文件。
下面的代码行让webpack知道我们是在开发模式下工作——这让我们不必在运行开发服务器时添加一个模式标志。
module
对象帮助定义如何转换导出的javascript模块,以及根据给定的rules
数组包含哪些模块。
我们的第一个规则是关于转换ES6和JSX语法的。 test
和exclude
属性是匹配文件的条件。在本例中,它将匹配node_modules
和bower_components
之外的任何内容, 因为我们也将转换.js
和.jsx
文件,所以我们需要将Webpack定向到使用Babel。最后,我们在options
中指定要使用env
预设。
下一个规则是处理CSS。因为我们没有对CSS进行预处理或后处理,所以我们只需要确保在use
属性中添加style-loader
和css-loader
。css-loader
需要style-loader
才能工作。当只使用一个加载器时,loader
是use
属性的简写。
resolve
属性允许我们指定Webpack将解析哪些扩展——这允许我们导入模块而不需要添加它们的扩展。
output
属性告诉Webpack把绑定的代码放在哪里。publicPath
属性指定包应该放在哪个目录中,还告诉webpack-dev-server从哪里提供文件。
publicPath
属性是帮助我们开发服务器的一个特殊属性。它指定了目录的公共URL—至少在webpack-dev-server知道或关心的范围内。如果这个设置不正确,你会得到404,因为服务器不能从正确的位置提供你的文件!
我们在devServer
属性中设置webpack-dev-server
。这对我们的需求没有太大的要求——只需要我们提供静态文件的位置(比如index.html)和我们想要运行服务器的端口。注意,devServer
也有一个publicPath
属性。这个publicPath
告诉服务器我们绑定的代码的实际位置。
最后一点可能有点令人困惑——请密切注意这里的output.publicPath devServer.publicPath是不同的。阅读这两个条目。两次。
最后,因为我们希望使用Hot Module Replacement,所以不必经常刷新来查看更改。
对于这个文件,我们所做的就是在plugins
属性中实例化一个新的插件实例,并确保在devServer
中将hotOnly
设置为true
。不过,在Hot Module Replacement工作之前,我们还需要在React中设置更多东西。
我们已经完成了繁重的准备工作。现在让我们开始React吧!
React
首先,我们需要获得另外两个包:react@16.5.2
和react-dom@16.5.2
。继续并将它们保存为常规依赖项。
我们需要告诉React应用程序在哪里与DOM挂钩(在index.html
中)。在src目录中创建一个名为index.js
的文件。这是一个非常小的文件,在你的React应用中做了很多事情。
ReactDOM.render
是一个函数,它告诉我们渲染什么以及在哪里渲染——在本例中,我们渲染一个名为App的组件(我们很快就会创建它),它在ID为root
的DOM元素上渲染(index.html
的第10行)。
现在,在src中创建另一个名为App.js的文件。如果您使用过create- response -app与React一起工作,那么您应该非常熟悉这一部分。这个文件只是一个React组件。
import React, { Component} from "react";
import "./App.css";
class App extends Component{
render(){
return(
<div className="App">
<h1> Hello, World! </h1>
</div>
);
}
}
export default App;
当我们还在这里的时候,我提到webpack也处理CSS(并且我们需要它作为我们的组件)。让我们向src目录添加一个非常简单的手写笔。
.App {
margin: 1rem;
font-family: Arial, Helvetica, sans-serif;
}
你的最终项目结构应该如下所示,除非你在过程中改变了一些名字:
.
+-- public
| +-- index.html
+-- src
| +-- App.css
| +-- App.js
| +-- index.js
+-- .babelrc
+-- .gitignore
+-- package-lock.json
+-- package.json
+-- webpack.config.js
我们现在有了一个有效的react应用!我们可以通过在终端中执行webpack-dev-server --mode development
来启动我们的dev服务器。我建议把它放在你的start
脚本,在package.json中。可以为你节省9次按键。
Finishing HMR
如果您现在运行服务器,您将注意到您的任何更改实际上都不会导致客户机发生任何事情。到底发生了什么事?
嗯,HMR需要知道实际上要替换什么,目前我们还没有给它任何东西。为此,我们将使用react团队提供的一个包:react-hot-loader@4.3.11
。
你可以安装这个作为常规依赖-根据文档
注意:您可以安全地将 react-hot-loader作为常规依赖项而不是dev依赖项安装,因为它会自动确保不会在生产中执行,并且占用的空间最少。
现在,通过修改如下代码,在App.js中导入反应热加载程序,并将导出的对象标记为热重新加载。
import React, { Component} from "react";
import {hot} from "react-hot-loader";
import "./App.css";
class App extends Component{
render(){
return(
<div className="App">
<h1> Hello, World! </h1>
</div>
);
}
}
export default hot(module)(App);
当您现在运行您的应用程序时,对代码的更改应该在保存后立即更新客户端。
Last Details
在启动项目时,您可能会注意到一些有趣的(或令人吃惊的)事情:构建的文件从不显示在dist
目录中
看,webpack-dev-server
实际上是从内存中提供捆绑的文件——一旦服务器停止,它们就消失了。要真正构建文件,我们将正确的使用 webpack
——在package.json
中添加一个名为build
的脚本,使用下面的命令:webpack --mode development。您可以用production
代替development
,但是如果完全省略--mode
,它将退回到后者并向您发出警告。
这几乎涵盖了渲染一个基本React应用程序所需的所有内容,而无需触及create-react-app。还有更多的东西需要添加到实现中,使其更加完整——例如,图像不是被Webpack设置来处理的,但是有一个加载器。我将把实现留给您。毕竟,如果您不需要或不想提供文件,那么,这只是膨胀,对吗?