从头开始创建一个React App

反应不只是“工作”的盒子。它使用的关键字和语法,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_modulesdist目录

我们的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-reactpreset-env都是转换特定类型代码的预定义值——在本例中,env预定义值允许我们将ES6+转换为更传统的javascript, react预定义值也可以实现同样的功能,但使用的是JSX。

在项目根目录中,创建一个名为.babelrc的文件。这里,我们告诉babel我们将使用envreact预设。

{
  "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语法的。 testexclude属性是匹配文件的条件。在本例中,它将匹配node_modulesbower_components之外的任何内容, 因为我们也将转换.js.jsx文件,所以我们需要将Webpack定向到使用Babel。最后,我们在options中指定要使用env预设。

下一个规则是处理CSS。因为我们没有对CSS进行预处理或后处理,所以我们只需要确保在use属性中添加style-loadercss-loadercss-loader需要style-loader才能工作。当只使用一个加载器时,loaderuse属性的简写。

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.2react-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设置来处理的,但是有一个加载器。我将把实现留给您。毕竟,如果您不需要或不想提供文件,那么,这只是膨胀,对吗?

参考源码地址

https://github.com/paradoxinversion/react-starter

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,417评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,921评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,850评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,945评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,069评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,188评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,239评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,994评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,409评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,735评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,898评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,578评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,205评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,916评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,156评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,722评论 2 363
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,781评论 2 351

推荐阅读更多精彩内容