React 企业级实践指南(一)

React路由与懒加载

原文:Practical Enterprise React

协议:CC BY-NC-SA 4.0

一、在 React 中领先

本章将简要概述一些关键的 React 概念,包括将 React 作为前端开发工具的优势和潜在好处。

第一 React

React 是一个开源 JavaScript 库,用于创建交互式用户界面(ui)和前端应用。它在世界各地不断增长的用户数量和强大的社区可能证明了这样一个事实,即 React 正在实现其存在的理由,开发用户界面,特别是更快的和交互式的用户界面。

您“声明”您希望您的 UI 看起来如何以及它应该如何表现,React 将按照您的指示并按照您的描述在您的浏览器中呈现它。

脸书于 2011 年创建了 React,并于 2013 年开源。从技术上讲,React 是一个库,但是由于它的行为和功能,许多用户称它为框架。也许,描述或比较 React 行为的一种方式是将其视为流行的架构模式模型-视图-控制器(MVC)中的视图。

基于组件的体系结构

React 应用的核心是由组件组成的。可重用组件更准确地说。它是用户界面的一部分,每个元素都有其特定的逻辑。

React 的模块化特性允许独立开发应用的功能,并在项目内外重用。例如,在图 1-1 中,我们可以将网页分解成各种组件,如导航条、英雄部分、页脚等。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-1

典型网站截图。来源: www.reactjs.org

一个导航栏组件,如图 1-2 所示,包含页面标题和导航元素。它通常位于屏幕的顶部。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-2

导航栏组件的一个示例

如图 1-3 所示,hero section 组件包含图像,通常是大图像,旨在从页面的视野中脱颖而出并吸引注意力。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-3

英雄部分组件的示例

因此,如果你仔细观察,React 组件是独立的、自包含的可重用代码块,我们可以将它们放在一起创建复杂的用户界面。

是的,React 应用由许多可重用的组件组成。在任何编程语言中,组件都可以被认为是简单的函数。每个 React 应用的根都是一个被恰当地称为根组件的元素,它将整个应用(包括它的所有子组件)结合在一起。

文档对象模型(DOM)

学习 React 时另一个值得理解的概念是它的虚拟文档对象模型,简称为虚拟 DOM。

简单地说,虚拟 DOM 仅仅是真实 DOM 的一种表示。

数据或状态的改变首先在虚拟 DOM 上完成。在计算出变化之后,真正的 DOM 就被更新了。结果呢?整体性能更快,用户体验更好。React 仅在状态元素发生变化时重新渲染组件及其子组件。

实质上,虚拟 DOM 的工作方式如下:

  • 如果数据发生变化,虚拟 DOM 中的整个 UI 都会重新呈现。

  • 重新渲染会创建一个具有相应更改的新虚拟 DOM。

  • 接下来,对新旧虚拟 DOM 之间的差异进行比较或计算。

  • 然后,真正的 DOM 只更新已经改变的元素或状态,而不是整个 DOM 树。

所以,是的,虚拟 DOM 的巧妙创建和使用是 React 快的原因之一。

下面是一些展示 React 的虚拟 DOM 如何工作的可视化表示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-4

仅当子组件的状态依赖于父组件时,子组件才会重新渲染

在图 1-4 中,如果子组件依赖于父组件的更新状态,改变父状态将重新呈现子组件。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-5

如果子组件不依赖于父组件的更新状态,它们不会在真实的 DOM 中重新呈现

在图 1-5 中,如果子组件不依赖于父组件的更新状态,改变父状态不会重新呈现子组件。

就性能而言,访问或创建虚拟 DOM 比在真实 DOM 中构建或重新呈现要便宜。

图 1-6 提供了真实 DOM 和虚拟 DOM 之间的概要比较。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-6

真实 DOM 和虚拟 DOM 的对照表

客户端渲染和服务器端渲染

您可以选择通过客户端呈现(CSR)或服务器端呈现(SSR)来实现 React 应用。开发人员可以在客户端和服务器端构建独立和自包含的应用组件。

客户端呈现(CSR)是一种相对较新的呈现网站的方式,它使用 JavaScript 在 UI 中显示内容。在客户端运行代码有一定的性能优势,包括在每次修改代码时使界面更具交互性。当数据改变时,React 将有效地更新和重新呈现我们的组件。

CSR 上的初始页面加载应该比较慢,但是页面重新加载可能会变得非常快,因为整个 UI 不是在服务器上调用的。

React 服务器端呈现(SSR)意味着组件在服务器上呈现,输出是 HTML 内容。做 SSR 的一个论据是,它有更好的应用性能,特别是对于内容密集型应用;另一个原因是,与做 CSR 相比,HTML 输出对 SEO 更友好。

单向流/单向数据绑定

React 更多地用于单向流或单向数据绑定。向下的数据流是 React 中允许更快和更有效(更不用说容易测试的代码)开发时间的事情之一。

单向数据绑定允许您更好地控制您的应用开发,因为组件应该是不可变的,并且其中的数据不能被更改。要直接编辑任何元素,必须使用回调函数。

为什么要 React?

与其他框架、库或编程语言相比,React 不仅学习速度快;它也是后端不可知的,允许用户使用它,无论他们的栈是什么。特别是 JavaScript 开发人员,可以很快精通 React 开发。

一个很大的加分点是在其官方网站上有一个写得很好的文档,易于遵循和理解。您可以将 React 视为 CMS(内容管理系统)世界的 WordPress,因为您的大多数问题都可以通过安装开源库来解决。

React 也被认为是 SEO 友好的,这意味着 React 组件对于 Google 索引来说更加简单。这对企业来说是件大事。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-7

学习和使用 React 的理由

图 1-7 说明了 React 为何成为开发者和许多知名商业品牌的最爱。

学习和使用 React 的一些原因,以及为什么它将在许多年内保持相关性:

  • React 中虚拟 DOM 的威力。它只更新和呈现同样在 DOM 中更新和生成的元素。

  • 快速渲染和可重用组件。创建可在项目内外重用的封装的独立组件。

  • React 可以使用 Next.js 在服务器上呈现。

  • 要开发移动应用,可以使用 React Native,这是一个使用 React 的移动应用开发框架。此外,您可以在 React 本地移动应用中重用 React web 应用的业务逻辑部分。

  • React 相对于其他 JavaScript 框架,甚至是 Angular 或 Ember.js 等前端框架来说,还是比较容易学的。

  • 大量的工具可供选择,包括 React DevTools、Redux DevTools 和 MobX DevTools。

  • React 是开源的,拥有一个由世界各地活跃的社区或团体组成的强大生态系统。

最后一个,巨大的 React 社区和支持,是一个重要的因素,不管你信不信。这个活跃的社区也转化为开发人员创建许多工具和第三方库来帮助您的开发体验。

基于个人经验,我们已经求助于许多 React 团体,如 Slack、Gitter、脸书团体、Discord、Spectrum 和 Twitter。或者,每当我们遇到困难或需要澄清时,我们会联系某个特定的人。

几乎总是有人给我们答复,根据我们的经验,通常不到一个小时,可能是因为活跃的成员来自全球不同的时区。

React 中的职业机会

对 React 的兴趣高于 Vue.js、Angular 等其他流行的前端框架和库(图 1-8 )。如果你快速搜索一个前端开发人员的工作,你会看到许多公司在寻找精通 React 的开发人员或有 React 经验的人。我们相信这种趋势将会持续数年。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-8

对 React、Angular 和 Vue 的兴趣不断增加。来源: https://trends.google.com/trends/explore?date=2019-09-01

如果你想扩大你作为前端开发人员和全栈开发人员的招聘需求,掌握 React 是一条必由之路。

React 开发人员目前有很高的就业市场需求,在可预见的未来。是的,你可能有自己喜欢的语言、库或框架,这是可以理解的。尽管如此,坦率地说,学习一门新语言或一种新编程工具的标准之一应该是你有多容易被雇佣,无论是作为一名自由职业者还是作为一家公司的一部分。

摘要

本章概述了关键的 React 概念,包括学习 React 的优势和好处。我们了解到所有 React 应用的核心是其可重用组件,以及虚拟 DOM 如何实现整体快速性能和更好的用户体验。

React 变得如此流行的另一个原因是,它让开发人员有可能在客户端和服务器端构建和呈现独立和自包含的组件。最后,我们表明,随着兴趣和需求的持续增长,React 技能是一个很好的职业发展方向。

在下一章,我们将开始下载和安装构建 React 应用所需的软件包。

二、Node 包管理器入门

在我们启动和运行 React 之前,我们需要一个安装程序来下载和管理我们的 JavaScript 软件包。最流行的包管理器之一是 Node 包管理器(NPM)。另一个是纱,一个较新的包装经理,据说从 NPM 汲取了很多灵感。对于我们的项目,我们选择使用 NPM,而不是纱线。如果你喜欢,你可以用纱线。

我们的项目将使用 NPM,所以本章将介绍一些最相关的 NPM 命令和快捷方式,包括语义版本和 NPM 脚本。我们将简单讨论一下 Yarn,并浏览一些常用的 Yarn 命令。

现在不要担心遵循这里的命令,因为一旦我们开始构建我们的项目,我们将在后续章节中一步一步地做它。

Node 包管理器概述

在开始之前,让我们回顾一下什么是 NPM,以及我们如何在我们的应用中使用它。NPM 类似于其他的包管理器,比如 Ruby on Rails 中的 RubyGems 或者 Python 中的 PIP。

NPM 于 2010 年发布,是 JavaScript 的包库管理器,通常预装 Node.js,这是一个用于构建服务器端应用的环境。如果你还没有安装 Node.js,去他们的网站 www.nodejs.org (图 2-1 )。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-1

Node 的网站

确保安装长期支持(LTS)版本,因为它比当前版本更稳定。如果您已经安装了 Node,您可以检查版本:

$ node –version

说到 Node 版本管理,让我向您介绍一下——如果您还不熟悉的话——Node 版本管理器,简称为nvm

Node 版本管理器

Node 版本管理器(NVM)是一个用于管理 Node.js 运行时的不同版本的工具。

您可以轻松地降级或升级 Node.js 版本,如果您面对的是只与特定范围的 Node.js 版本兼容的遗留应用或 JavaScript 库,这将非常方便。

用一个命令安装nvm:

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | bash

#or

wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.37.0/install.sh | bash

要在 Mac 和 Linux 上完整安装 NVM,请点击这里 https://github.com/nvm-sh/nvm

不幸的是,nvm不支持 Windows,但是根据 WSL 版本的不同,它可以在 Windows Subsystem for Linux (WSL)中实现。

要在 Windows 中下载并安装 NVM,请点击此处 https://github.com/coreybutler/nvm-windows

安装 NVM 后,您可以安装特定版本的 Node:

$ nvm install 14
$ nvm use 14

前面的命令将安装并使用 Node v14 的最新次要版本。现在,如果有一个 JavaScript 项目或库只在 Node v12 中运行,您可以很容易地切换到那个版本:

$ nvm install 12
$ nvm use 12

前面的命令将安装并使用 Node v12 的最新次要版本。

对于任何 JavaScript 开发人员来说,另一个不可或缺的资源是 npmjs 网站,在那里我们可以搜索需要为我们的项目安装的库或框架。

安装软件包

你可以使用 NPM 安装 NPM 注册表中任何可用的库或框架。你可以前往 www.npmjs.com 查看更多。如果您已经有了旧版本,您可以按如下方式进行更新。

对于 Mac 和 Linux 用户:

$ npm install -g npm@latest

Node 封装模块

软件包以本地或全局模式安装。全局模式包可从命令行界面(CLI)获得,而本地模式包安装在父工作文件的node_modules文件夹中。

设置新的或现有的 NPM 软件包:

$ npm init

$ npm init –-y

填写空白处或只按Enter接受默认值或添加标志–-y来快速生成 package.json 文件。

如果您想要克隆其他人的代码,请在项目根目录下运行以下命令:

$ npm install or npm i

这将自动获取运行应用所需的所有已声明的包。声明的包位于 package.json 文件中。

Package.json 和包锁. json

Package.json 是使用 NPM 和 Node 应用时的重要清单文件。它包含您的所有应用信息,尤其是您的应用正确运行所需的依赖项或模块。这个清单文件也是许多开发人员在运行项目的本地版本时首先要查看的。

Package-lock.json 是你的 package.json 文件的副本,版本依赖树。

清单 2-1 中所示的 package.json 文件是我们在终端中执行 npm install 命令时自动创建的。这只是一个 package.json 文件的示例对象结构,包括依赖项和 devDependencies。我们将在第四章开始为我们的应用安装软件包和库。

{ "name": "npmproject",
   "version": "1.0.0",
   "private"
   "description": "NPM commands",
   "main": "index.js",
   "scripts": {
        "start": "react-scripts start",
        "build": "react-scripts build",
        "test": "react-scripts test",
        "eject": "react-scripts eject",
   "backend": "json-server --watch db.json --port 5000 -- delay=500",
   "start:fullstack": "concurrently \"npm run backend\" \"npm run start\""
 },
    "author": "Devlin Duldulao",
    "license": "MIT",
    "dependencies": {
    "react": "¹⁶.8.6",
               "react-dom": "¹⁶.8.6",
},
"devDependencies": {
    "husky": "⁴.3.0",
    "json-server": "⁰.16.2",
    "lint-staged": "¹⁰.4.0",
    "mobx-react-devtools": "⁶.1.1",
    "prettier": "².1.2"
  },
 }

Listing 2-1A Sample of a package.json File

而 package.json 用于项目属性、作者、版本、描述、脚本、许可证等依赖项。,package-lock.json 同样会自动创建,以将依赖项锁定到特定的版本号。

如清单 2-1 所示,脚本对象是我在已有项目中首先读到的,因为脚本告诉我们运行项目或构建项目需要运行什么命令。脚本对象还通过它的键和值对帮助我们运行更短的命令。你可以在脚本中做的另一件很酷的事情是定制它。

假设您想要同时使用脚本’npm run backend’和’npm run start’。然后你可以在你的脚本中添加一个快捷方式,比如“npm start:fullstack”,然后运行它:

"start:fullstack": "concurrently \"npm run backend\" \"npm run start\""

如果您想亲自尝试使用start:fullstack命令,您可以并行安装 npm。Concurrently 是一个非常方便的工具,允许我们同时运行多个命令:

$ npm i concurrently

在我们的例子中,依赖项最初只包含 react 和 react-dom 等最少的元素,它们是我们的应用在运行时需要的库。但是也请注意devDependencies对象。devDependencies对象包含 JavaScript 库,您希望只在本地开发和测试期间添加这些库,因为您在生产中不需要它们。一些很好的例子包括 Gulp、Prettier 和 ESLint。

语义版本化或 Semver

有几种类型的版本控制,但是语义版本控制或 semver 是最流行的一种。

在所谓的语义版本化中,有三个版本号需要考虑。在其官网上, www.semver.org/ ,我们可以看到如下的总结:

给出了一个版本号专业。MINOR.PATCH,增量:

当您进行不兼容的 API 更改时,增量为主要版本,

当您以向后兼容的方式添加功能时,增量为次要版本,

当您进行向后兼容的错误修复时,增量为修补版本。

预发布和构建元数据的附加标签可作为主版本的扩展。小调。补丁格式。??

语义版本控制的一个例子是“³.2.1".

第一个数字(3)是主要版本,(2)是次要版本,(1)是修补版本。

脱字符^告诉 NPM,我们将接受该方案的主要版本;次要版本和修补程序版本可能会有所不同。另一种写法是,例如,2.x,2 是主要版本。

在某些情况下,您会看到波浪号(即“3.2.1”)。这种特殊的版本化可以理解为“我们将主版本设为 3,次版本设为 2,但是补丁版本可以是任何数字。”你也可以把它写成 3.2.x。

如果您需要所有三个版本的特定数字,只需删除插入符号或波浪号字符。

故事

Yarn 是一个新的 JavaScript 包管理器。 Yarn 由脸书于 2016 年发布,旨在解决困扰 NPM 的一些性能和安全问题(当时!).如前所述,我们不会使用纱线,但你可以用它来代替 NPM。在下文中,我们强调了两者在语法上的一些相似之处和不同之处。

表格 2-1 和 2-2 强调了这两个包管理器之间的异同。

表 2-2

NPM 和纱线之间的不同命令

|

命令

|

新公共管理理论

|

故事

|
| — | — | — |
| To install dependencies | npm install | yarn install |
| To install packages | npm install[package-name] | yarn add [package-name] |
| To uninstall packages | npm uninstall [package-name] | yarn remove [package-name] |
| To install packages globally | npm install –global [package-name] | yarn global add [package-name] |
| To uninstall packages globally | npm uninstall –global [package-name] | yarn global remove [package-name] |
| To update packages * for updating minor and patch releases only | npm update [package-name] | yarn upgrade [package-name] |
| To install only regular dependencies | npm install --production | yarn --production |
| To show only the top-level dependencies | npm list -g --depth 0 | yarn list --depth=0 |
| To install and save packages in devDependencies | npm install --save-dev [package-name | yarn add [package-name] -D |

表 2-1

NPM 和纱线之间的常用命令

|

命令

|

新公共管理理论

|

故事

|
| — | — | — |
| To initialize a project | npm init | yarn init |
| To set up the defaults | npm init –y | yarn init -y |
| To check if any package is outdated | npm outdated | yarn outdated |
| To clear local cache | npm cache clean | yarn cache clean |
| To run a script | npm run build | yarn run |
| To see a list of installed dependencies | npm list | yarn list |

使用 NPM 的技巧:

  1. 要快速生成 package.json 文件,可以使用npm init –-y

  2. private: true添加到 package.json 中,以防止意外发布任何私有回购。

  3. 在 devDependencies 中添加用于开发目的的包(例如,传输代码或运行测试)。

  4. 不要删除 package.json,但是可以在提交之前删除 package-lock.json。

  5. 如果您遇到 yarn.lock 并希望使用 npm,只需删除 yarn.lock 并进行 NPM 安装,以在您的应用中自动创建 package-lock.json。

  6. 从 Git 存储库克隆项目后,您需要运行 npm install。

  7. 不建议将 node_modules 推送到您的源代码控制 repo(如 Git)中。

摘要

在这一章中,我们讨论了如何开始使用 NPM,包括您需要理解的各种关键命令。我们还研究了如何使用nvm轻松地从一个 Node 版本切换到另一个 Node 版本。在下一章,我们将讨论各种 React 组件,以及如何在我们的应用中使用它们。

三、React 函数组件和 TypeScript 入门

在前一章中,我们学习了如何使用 Node 包管理器或 NPM,它是一个命令行工具,允许我们安装和更新应用包。

本章将研究 React 函数组件,这已经成为编写最新 React 应用的现状,以及使用 TypeScript 定义函数组件所用属性的语法。简单地说,React 函数组件本质上是返回 JSX 或 JavaScript XML 的 JavaScript 函数。

我们将使用 TypeScript 设置一个基本的 create-react-app (CRA)应用,并使用 CRA TypeScript 模板检查一些关键特性。TypeScript 是 JavaScript 的强类型超集,它带来了直观的特性,以强大的方式扩展了 JS,例如以多种方式定义对象的结构。

我们将讨论在 TypeScript 中使用类型化的明显好处,尤其是在构建大型企业级应用时。此外,我们将编写 React 函数组件,或 React。简称 FC,并触碰两个常用的 React 挂钩:useStateuseEffect

创建一个创建-React-应用应用

是开始构建单页面应用(SPA)的最流行也是最简单的方法。它也是脸书官方的样板文件生成器,用于创建一个简单的或入门的 React 应用。

CRA 使用预配置的 webpack 版本进行开发,使我们能够立即深入我们的代码并构建我们的 React 应用,而无需手动设置和配置我们的本地开发环境和生产版本。

最低要求是 Node> = 8.10,NPM >= 5.6。

首先,在您的终端中,检查您是否已经拥有 NPX:

$ npx -v

或者您可以通过运行以下命令单独安装它

$ npm install -g npx

NPX(Node 包执行)是 NPM 自带的。NPX 对于一次性软件包很有用,它从 NPM 注册表执行软件包,而不在本地安装它们。

Note

如果您之前已经通过 npm install -g create-react-app 全局安装了 create-react-app,则需要使用

npm uninstall -g create-react-app

#或者

yarn global remove create-react-app

这是为了确保 NPX 始终使用最新版本。不再支持 CRA 的全局安装。

现在,让我们用以下命令创建我们的第一个create-react-app应用:

$npx create-react-app <name-of-your-app> --template
typescript

cd <name-of-your-app>

npm start

命名约定是使用所有小写字母,并用破折号分隔单词。- template TypeScript 标志告诉 CRA 使用 TS 作为默认语法并添加所需的配置。要启动开发服务器,运行或。

您可以在 package.json 中的“脚本”下找到完整的命令脚本。

运行该命令后,在您的浏览器中检查您的应用—http://localhost:3000——并看到如图 3-1 所示的 CRA 初始加载页面。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-1

带打字稿的 CRA 初始加载页

当您打开 IDE 或编辑器时,您应该能够看到如图 3-2 所示的目录。我们可以在下面看到 CRA 自动生成的项目或默认文件夹结构。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-2

最初使用 create-react-app 创建的文件(CRA)

让我们回顾一下用 create-react-app 生成的一些文件。首先,让我们检查将要构建的项目的两个关键文件:

public/index.html是我们应用的主页面 HTML 模板,包括 React 代码和 React 渲染上下文。在这里,您可以找到让 JavaScript 运行应用的“根”。它也是 React 应用的挂载点:

 <!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width,         initial-scale=1" />
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <title>React App</title>
  </head>
  <body>
    <section id="root"></section>
  </body>
</html>

持有来自 React DOM 的主渲染调用。它导入我们的 App.tsx 组件,该组件告诉 React 在哪里呈现它:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

以下是 CRA 附带的一些文件和文件夹的简短定义和用法:

它包含了 JavaScript 库和一些我们已经安装的库的依赖项。我们通常不把这个文件夹包含在我们的 Git 库中,因为它很大,大约 100mb 到 500mb,这取决于我们在应用中安装了多少个包。

这是 CRA 附带的默认文件。当基于当前的 React 应用构建渐进式 Web 应用(PWA)时,需要配置文件。

对 PWA 的一个简单解释是,它允许您脱机或在没有互联网连接的情况下运行应用。它的作用是缓存你的应用的数据或内容,这样即使应用离线,你仍然可以看到你网站的内容。仅供参考,如果没有互联网连接,你将再也看不到霸王龙了。我相信我们都看过(你甚至可以播放它!)那只恐龙是吧?

public/robots.txt:又称机器人排除协议或标准。你可以在这里声明你想在谷歌搜索结果中隐藏的页面或 HTML。示例页面包括管理页面,因为您的用户不需要知道您的应用的管理页面。同样,你可以在这里指定你希望或者不希望哪个搜索引擎来索引你的站点。

src:包含 app UI 代码,包括组件和 CSS 样式。这是 React app 的核心。我们还可以看到App.tsx,我们应用的入口点,以及index.tsx,它引导我们的应用。

gitignore:位于根文件的文本文件;这是您放置希望 Git 忽略的文件的地方,比如您的node_modules和。env files。Git 已经跟踪的文件不受影响。使用命令git rm –cached停止索引一个已经被跟踪的文件。

如果你已经在你的机器上安装了 Yarn,默认情况下,CRA 会搜索 Yarn package manager,但是如果它不可用,它会返回到 NPM。如果您使用 Yarn,那么您将会看到yarn-lock.json文件。

package.json:我们已经在前一章中讨论过package.json,但是简单回顾一下,它管理我们项目应用的依赖、脚本、版本和其他元数据。

tsconfig.json:根目录下的这个文件表示该目录是 TypeScript 项目的根目录。

tsconfig.json内部是compilerOptions对象,具有以下一些配置:

  • "target" : "es5":现代浏览器支持所有 ES6 特性,但是您希望您的代码向后兼容 ECMAScript 2009 或更老的环境。

  • "lib":类型检查过程中包含的标准类型定义。

  • "strict" : true:这是 TypeScript 中严格的编译器选项;这意味着您选择了默认严格的类型安全模式,而不必单独启用每个编译器选项。

  • "module" : "esnext":指定代码生成。

  • "noEmit" : true:不要发布 JavaScript 源代码、源代码图或声明,而是使用 Babel。

  • 是否支持 JSX。tsx 文件。

  • "include":打字稿要检查的文件和文件夹。

使用 React 的 TypeScript 的声明文件

声明文件也称为。d .ts文件以@types.为前缀

使用声明文件——也称为定义文件——我们可以避免错误处理库,并在我们的编辑器中获得非常有用和强大的“智能感知”或自动完成功能。

让我们快速浏览一下我们的package.json文件,看看我们的 CRA 打字稿模板中包含的. d.ts 文件:

    @testing-library/jest-dom":
    "@testing-library/react":
    "@testing-library/user-event":
    "@types/jest":
    "@types/node":
    "@types/react":
    "@types/react-dom":

为了确定我们最近安装的库是否需要声明文件(@types),我们转到名为 DefinitelyTyped 的 GitHub 存储库,声明文件就存放在这里。另一种方法是直接进入 www.npmjs.com ,在搜索框中给库名加上前缀@types/。幸运的是,许多人已经为最常见和最流行的 JavaScript 库编写了声明文件。

如果你使用一个需要类型声明文件的外部库,而你没有安装它,你会得到错误或者你的 IDE 会抱怨。

打字稿中的打字

打字稿中的打字本身就是文件。是的,一开始可能看起来有点额外的工作,但从长远来看,这是值得的。许多最初看不到它的价值的 React 开发人员说,他们现在无法想象不使用它。

考虑下面的例子。我们明确定义了每个变量的类型——名称是字符串,年龄是数字,等等:

const name: string = "Jane Doe",
      age: number = "18",
      isStudent: boolean = true;

您会注意到,当您自己编写类型脚本时,代码的描述是代码本身的一部分。

如果你熟悉 JavaScript,你会知道它是无类型的。JavaScript 可能是唯一一种既爱又恨的编程语言,因为它的灵活性——有时可能有点过分——在于它的“规则”是按照我们想要的或我们想要的方式传递数据和对象。

现在,想象一下,在一个团队中有一群人,用成千上万的代码构建一个应用。代码会很快变得混乱和复杂。您可能会遇到在不存在的对象中调用的方法,或者从未声明的变量。

但是在 TypeScript 中,我们需要定义我们的对象和数据格式。这样,编译器就可以智能地搜索或检测我们的代码,并捕捉潜在的错误,比如用错误的参数调用函数,或者引用当前范围内不可用的变量。

使用 TypeScript 的显著好处包括拥有增强的工具选项,如自动完成、重构和在整个应用中查找用法。你可以在这里阅读更多关于打字稿的内容: www.typescriptlang.org/docs/handbook/basic-types.html

了解 React 功能组件与 Props

React 功能组件(React。FC)是一个 JavaScript/ES6 函数,它接受参数作为组件的属性,并返回有效的 JavaScript XML (JSX)。如果需要,它还会将 props 作为参数。

下面是一个带有属性的简单功能组件:

function Message(props) {
  return <div>{props.message}</div>
}

现在让我们创建一个 React 函数组件。在src文件夹中,创建一个名为components,的目录,在components,文件夹中创建一个名为Customer.tsx的文件。

在我们继续之前,让我们首先使用 TypeScript 的类型别名来定义我们的 props。另一种方法是使用interface,,但是我们更喜欢使用类型别名来确定我们的属性:

import React from "react";

type Person = {
  firstName: string;
  lastName: string;
  age: number;
  isLegal: boolean;
};

请注意,我们定义了一个名为Person的类型,它指定了组件将采用的属性。firstNamelastName是必需的strings,,而age是必需的numberisLegal是必需的boolean

通过创建一个type,,我们现在可以在项目中的任何地方使用它,就好像它是一个字符串、数字或者任何原语或引用类型。

在我们新创建的组件中,使用 Person 类型作为属性的模型或形状。请参见以下内容:

const Customer = (props: Person) => {
  const { firstName, lastName, age, isLegal } = props;

  return (
    <div>
      <h1> Hello React</h1>
    </div>
  );
};

export default Customer;

我们可以看到,Customer是一个无状态的函数组件,它接受一个props对象,并析构< Person >所有需要的属性。

现在我们已经有了带有属性的函数组件,让我们进入 App.tsx 来渲染它。

首先,我们需要像这样导入组件:

import Customer from "./components/Customer";

在我们的回访中:

function App() {
  return (
    <div className="App">
      <Customer
        firstName="Jane"
         lastName="Doe"
         age={21}
         isLegal={true} />
    </div>
  );
}

export default App;

当我们在代码编辑器中使用代码智能感知功能时,创建类型定义也很有用。

代码编辑器中添加的智能感知

为我们的属性创建类型并声明组件的参数的另一个好处是我们在编辑器中得到的所谓的智能感知或代码建议。VS 代码中支持智能感知。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-3

编辑器中的代码建议/智能感知

你猜怎么着?如果编写 Customer 组件时没有传递所有必需的值,就会出现 TypeScript 错误。甚至在你运行它之前,你已经可以在中看到红色的曲线来指示一个错误或者一个严重的警告。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-4

编译器中的类型脚本错误

但是等等。如果不需要申报或者不用全部属性呢?你可以在属性类型中使用?来选择一些属性:

type Person = {
  firstName: string;
  lastName: string;
  age: number;
  address?: string;     // the address is optional
  isLegal: boolean;
};

关键是您可以选择不在组件中定义所有的类型,以实现自动完成的优秀工具和类型脚本的类型安全特性。

是的,在开始时,可能需要一点努力来设置和习惯在变量和函数中使用类型化。尽管如此,从长远来看,它会给我们一个更好的开发者体验和一个可维护的应用。

在我们继续之前,让我们再多谈谈 React 钩子和两个流行的钩子,即useStateuseEffect

React 钩

React 钩子本质上是 JavaScript 函数,是“React 16.8 中的新增功能”。它们让你不用写类就能使用状态和其他 React 特性。”

还要记住,钩子在类内部是不起作用的。钩子是一个很大的话题,但是我们会在开发应用的过程中学习基本的概念。但是现在,这里有三个钩子的重要规则需要记住:

  1. 只在 React 函数的顶层调用钩子。

  2. 仅从 React 函数组件调用挂钩,而不是从常规 JavaScript 函数调用挂钩。

  3. 仅从自定义挂钩调用挂钩。

React 钩子:使用状态

useState是一个钩子,它将允许函数组件中的状态变量。useState也是我们最有可能经常使用的一个。

假设我们想更新 JSX 的一个州。我们如何在我们的函数组件中做到这一点呢?嗯,我们用useState来表示!

在组件内部创建一个本地状态,并传递一个默认值。注意useState是 React 的一个命名导出:

import React, { useState } from "react";
import "./App.css";

function App() {
  const [title, setTitle] = useState("Practical React Enterprise");

方括号中的状态变量是一种叫做数组析构的 JavaScript 语法。第一个变量[title],也就是 getter,被定位为useState返回的第一个值,[setTitle]是 setter 或 set 函数,让我们更新它。

如何在我们的 JSX (HTML 元素)中使用状态变量或本地状态?在 React 中,由于我们的useState是在 JavaScript 中,我们需要用花括号{}将 getter 括起来,以便在 JSX 或 HTML div 中读取它的值:

...
      <div className="App">
      <h1
        style={{
          color: "blue",
          marginBottom: "5rem",
        }}
      >
        {title}
      </h1>

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-5

使用本地状态呈现 UI 的屏幕截图

现在我们有了本地值,但是如何更新值[title]?是 setter 函数[setTitle]的工作。顺便说一下,如果你问的是 div 中的className,我们在 React 中使用className而不是class来防止名称冲突,因为后者是现代 JavaScript 和 TypeScript 中的保留关键字。

在我们继续之前,我们需要简单讨论一下什么是 JSX 或 JavaScript XML。在 React JSX 中,我们可以将 HTML 和 JavaScript 结合在一起编写。我们可以运行 JS 代码(逻辑、函数、变量等。)中,就像我们声明{title}{setTitle()}时一样。

接下来,我们需要一个事件来触发 JSX 中的 setter 函数setTitle。那么我们如何找到一个事件呢?如果您在基本代码 <h1> (中使用前缀为“on”的,见图 3-6 ,将出现一个窗口,显示所有可用事件:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-6

IDE 中的代码建议

return (
  <div className="App">
      <h1 onClick={() =>
   setTitle("Become an Effective React Developer from Day 1")
        }}</h1>
    </div>
  );
}

export default App;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-7

使用本地状态更新用户界面

进入你的浏览器,点击标题“实际 React 企业”来更新本地状态标题。

例如,我们也可以将事件放在<button><div>.上。让我们创建一个handleAlert函数,并将它放在<按钮>上,以弹出一条警告消息:

const handleAlert = () => {
  alert("I'm a button!");
};

return (
  <div className="App">
    <button
      style={{
        color: "#ffff",
        height: "5rem",
        width: "10rem",
        backgroundColor: "tomato",
        borderRadius: "5px",
        fontSize: "18px",
      }}
      onClick={handleAlert}
    >
      Click me
    </button>

  </div>
);

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-8

弹出警告消息

React 挂接:useEffect

useEffect用于所有副作用,本质上取代了我们之前使用的生命周期方法(componentDidUpdate, componentDidMount, componentWillUnmount)。这个全面的钩子接受两个参数——第一个是必需的函数,第二个是特定状态的可选参数。

现在,假设我们想在用户登陆我们的应用时触发一个事件,或者在 UI 中的所有内容呈现之前触发一个事件。

是的,为此我们有useEffect。我们将做一个简单的 handleAlert 消息,当用户登陆浏览器时弹出,不需要在我们的 JSX 中调用它:

import React, {useEffect, useState} from "react";
import "./App.css";

function App() {
  const [title, setTitle] = useState("Practical React Enterprise");

  const handleAlert = () => {
    alert("I'm a button!");
  };

  useEffect(() => {
    alert("Welcome to the Practical React Enterprise!");
  });

  return (
...

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-9

具有使用效果的警告消息

useEffect的一个用例是,当您想要向 web 服务发送一个请求,以获取您的用户数据或一系列值,如客户姓名、地址等。

也可以把 setter 放在useEffect里面,比如:

  useEffect(() => {
    // handleAlert();
    setTitle("Updating the React Enterprise using useEffect");
  }, []);

注意到useEffect钩子中的方括号了吗?

  1. 数组括号将防止useEffect在无限循环中连续运行。

  2. 您还可以在数组中添加一个状态。并且状态值(即title)的任何变化将触发useEffect重新呈现:

  useEffect(() => {
    //another state here
  }, [title]);

技巧

  1. 不要使用"eject": "react-scripts eject"命令,除非你需要暴露 CRA 在应用开始时包含的管道,比如 Babel RC 和 Webpack。只有当您需要在 React 项目中进行一些定制时,才这样做。请记住,一旦弹出,您将无法回到 create-react-app 的原始样板结构。

  2. 在运行时代码实现之前声明类型。当前模块中使用的类型必须在顶部可见。

  3. 在命名约定中,组件应该用 PascalCase,而方法应该用 camelCase。

摘要

在本章中,我们使用了一个简单的 create-react-app 样板文件和 TypeScript 来展示构建 react 应用的最简单的方法。我们通过 TypeScript 学习了 CRA 的一些基本元素,以及它们如何一起正确运行我们的应用。

我们还看到了在 React 应用中使用 TypeScript 的一些好处,包括

  • 代码建议和选项使我们更容易阅读他人的代码。这可以提高团队沟通的效率和代码库的一致性。

  • IntelliSense 和错误捕捉在运行代码的早期和之前

不知何故,我们设法用 TypeScript 解构了 React 函数组件。

但是我们在这里触及的只是冰山一角,特别是在理解使用 React 的 TypeScript 的全部功能以及 TS 可以给 JavaScript 代码带来什么方面。

由于这本书是关于构建一个实用的企业级应用,我们将在将它应用到我们的代码中时更深入地研究它。在下一章中,我将向您展示如何以一种有用而有效的方式建立我们项目应用的基础,这样您将从第一天起就成为一名高效的 React 开发人员。

四、设置企业级应用

无论您是编程领域的新手还是专家,您一定想知道其他开发人员是如何设置和配置他们的项目应用的,对吗?

因此,这一章探讨了一种有效和智能的方法来建立一个生产就绪的样板文件。

我们将研究如何为我们的项目选择正确的样板,使用工具、框架和模块,允许我们管理性能、样式、异步和构建实际应用所需的其他基本内容。

是的,我们不仅仅是从无处不在的 ToDo list 应用开始学习新的框架或语言。这是一个你可以确信会在很多企业级 React 应用中看到的项目——尤其是配置甚至文件夹排列。更重要的是,它将具有良好的架构和高度的可伸缩性。

首先,我们将看看我用来为特定应用找到正确样板的标准。然后,让我们看看使用样板文件的利弊。

如何选择一个好的样板

像我认识的许多经验丰富的“聪明”的开发人员一样,我坚信应该使用样板文件,或者至少对此持开放态度,对吗?唯一的技巧——或者实际上可能是最难的部分——是为你的项目找到并选择正确的样板文件。

**在决定了样板文件之后,您需要做的第一件事就是了解您在 package.json 中看到的每个 NPM 包

Tip

在决定样板文件是否适合您之前,请检查样板文件中包含的库和工具。

获得一个包或依赖项的简单定义的一个简单方法是进入 www.npmjs.com 并在那里搜索它。

我有几个标准来决定什么是特定项目的正确样板,但以下是三个主要标准:

  1. 它包含了我需要的大部分(如果不是全部)核心功能。这是最关键的过程。寻找一个样板文件,让实际上检查你需要的所有主要特性,这样你就不必做大量的定制工作,这会耗费你大量的时间。总是阅读样板的特征和描述。

  2. 有一定数量的投稿人或作者。这是样板文件好的另一个关键部分。许多有经验的、活跃的开发人员正在为样板文件做贡献,并为更新和其他东西维护它。

  3. 它包含频繁的更新或提交。在 GitHub 上检查最新的补丁或提交。一个维护良好的模板或样板文件会有来自贡献者的定期更新或提交。

请记住,每个项目甚至项目团队都可能意味着许多不同的样板应用。大多数时候,你决定的样板文件取决于项目本身,你的合作伙伴,甚至是客户。

但关键是,你会希望从一个标准的代码基础开始,快速启动并运行。

使用样板文件的好处

作为一名开发人员,我确信您已经经历了似乎永无止境的设置所有库、主题和一开始就需要的依赖项的折磨。光是将它们全部配置好而不出错就要花费数小时。

相信我。如果我们要自己实现全套的 React、TypeScript、Redux、Redux 工具包、测试和所有其他必要的东西,这将花费我们比我们愿意花费的时间长得多。这是假设当我们一个接一个地安装它们时,一切都顺利进行。没有错误,所有的库版本都很好地集成在一起。

以下是我为什么说使用正确的样板文件作为你应用的起点可以让你从第一天起就成为一个有能力并且高效的开发者的原因:

  1. 您可以减少设置和开发时间。作为开发人员,我们都知道重用代码或模板非常有意义。使用样板文件可以减少使用相同代码模式的设置和开发时间,并避免从头开始重写。

  2. 你可以派生出一个样板文件,并根据你的需要进行定制。当我还是初级开发人员时,快速学习一个新的库或框架的一个方法是查看两个或更多流行的应用。我会研究和比较它们,包括所有使用的依赖项。之后,我会根据自己的需求构建一些东西。

  3. 你可以研究这款应用的架构良好的结构。样板文件立即向您介绍了一种设计 React 项目的优秀方法。这就像是首席架构师将构建应用的蓝图交给你。

  4. 你可以受益于该领域专家的贡献。有一个样板就像有一个高级开发人员或前端架构师已经给了你开始项目的指导方针。这是无价的,尤其是如果你是团队或特定工具的新手。在确定对你有用的东西之前,你不必做研究和测试所有的东西。

使用样板文件的缺点

像任何其他好东西一样,总会有反对的声音。当然有,对吧?但是诀窍是认识和理解缺点,并根据清单中的优点来权衡它们。也就是说,让我们来看看反对在应用中使用样板文件而不是从头开始构建项目的一些原因。

让我们来看看使用样板文件的一些缺点:

  1. 你可能会发现自己陷入了一个时间黑洞。你实际上仍然需要检查和了解事情是如何运行的。当然,在选择特定的样板文件之前,您需要了解其中包含的核心特性。有时,您可能会发现自己要么添加额外的基本功能,要么去掉多余的不需要的模块。

  2. 你可能会陷入不必要的复杂之中。您可能会得到比您需要的更多的特性和比必要的更高的复杂性。

在考虑了前面所有的要点之后,现在是时候设置我们选择的样板文件了。

克隆样板文件

首先,如果你想随书一起编码,去 my GitHub 并克隆项目的起点。确保从第四章开始。

我分叉这个样板文件只是为了防止你在一年或两年后阅读这本书,所以你仍然有相同的代码库,以防你想跟随我。我建议您使用我的分叉样板文件,以确保获得相同的开发体验。

你会在图 4-1 中找到我的 GitHub 的链接,如果你想和我一起编码,你应该从那里开始。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-1

实际企业 React 从源头: github。com/webmasterdevlin/practical-enterprise-react/tree/master/chapter-4/starter-boilerplate

然而,一旦你读完了这本书,并且很好地掌握了构建企业级 React 应用的过程,我强烈建议你在准备制作应用时使用图 4-2 中的原始样板文件。

您可以在图 4-2 中找到 React 样板 CRA 模板的主副本。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4-2

React 样板 CRA 模板来源: github。react-boilerplate/react-boilerplate-CRA-template

Note

不要忘记比较 package.json 中库的版本,以避免任何错误。

老实说,您甚至可以搜索另一个更适合您的项目规范的样板文件。

我的目标是为您提供构建生产就绪型应用的一步一步的过程,并通过武装您使用正确的工具,或至少如何选择它们,使您成为更自信的企业级应用开发人员。

React 样板遇到 CRA

我们为这个项目选择的模板是 create-react-app (CRA)和 react 样板的完美结合,后者是最受欢迎和喜爱的入门模板,包括所有行业标准工具:Redux、Redux 工具包、TypeScript 等等。

以下是我为我们的项目选择这个特殊样板的原因**😗*

  • create-react-app :内置 react 样板,可以弹出进行定制配置。

  • Redux :管理 React 应用全局状态的独立库。Redux 存储是应用的核心。Redux 是类似流量的单向数据流的更好的实现。

  • TypeScript :通过在点击刷新之前捕捉错误*,改善开发人员的体验。TS 还通过加强类型安全来防止代价高昂的错误。最后,由于参数和模型的类型化,TypeScript 是自文档化的。*

  • Jest :在 React 生态系统中相当受欢迎的一个测试跑者。

  • Reselect :用于切分 Redux 状态,并向 React 组件提供必要的子树。

  • react-router :用于该样板文件中的路由。

以下是需要安装在样板文件上的附加库。npm install我们将使用以下软件包:

  • Material-UI是 Google 在 2014 年开发的,它提供了一个可选的 CssBaseline 组件,用于更快的 web 开发。它使用基于网格的布局、动画、过渡、填充等等。

  • clsx是一个用于有条件地构造类名字符串的小工具。

  • Formik是一个非常流行的 React 和 React Native 开源表单库。

  • Yup是一个用于值解析和验证的 JavaScript 模式构建器。

  • axios是一个允许我们向外部资源发出 HTTP 请求的库。

对于依赖关系:

$ npm i [package name]

 @material-ui/core
 @material-ui/icons
 @material-ui/lab
 @material-ui/pickers
 @material-ui/styles
 clsx
 formik
 @types/yup
 yup
 axios

对于 devDependencies:

$ npm i -D [package name]

cypress
concurrently
json-server
json-server-auth

在我们继续之前,有必要了解汇聚在一起形成单一工作应用的不同工具和技术。

强壮的

在我看来,Husky 是添加到我们应用中的一个很好的工具。它本质上防止您提交或推送错误的提交或代码;它与 TypeScript 的打字功能一起增加了保护:

"husky(remove-everything-in-these-parentheses.See-the-issue-#29)": {
    "hooks": {
      "pre-commit": "npm run checkTs && lint-staged"
    }
  },

属国

在 IDE 中打开项目并打开 package.json 文件,检查是否安装了所有的依赖库:

"dependencies": {
  "@material-ui/core": "4.11.1",
  "@material-ui/icons": "4.9.1",
  "@material-ui/lab": "4.0.0-alpha.56",
  "@material-ui/pickers": "3.2.10",
  "@material-ui/styles": "4.11.1",
  "@reduxjs/toolkit": "1.4.0",
  "@testing-library/jest-dom": "5.11.6",
  "@testing-library/react": "10.0.1",
  "@types/fontfaceobserver": "0.0.6",
  "@types/jest": "25.1.4",
  "@types/node": "13.9.3",
  "@types/react": "16.9.25",
  "@types/react-dom": "16.9.3",
  "@types/react-helmet": "5.0.15",
  "@types/react-redux": "7.1.11",
  "@types/react-router-dom": "5.1.6",
  "@types/react-test-renderer": "16.9.2",
  "@types/styled-components": "5.1.4",
  "@types/testing-library__jest-dom": "5.9.5",
  "@types/webpack-env": "1.15.3",
  "@types/yup": "0.29.9",
  "axios": "0.21.0",
  "clsx": "1.1.1",
  "cross-env": "7.0.2",
  "eslint-config-prettier": "6.15.0",
  "eslint-plugin-prettier": "3.1.4",
  "fontfaceobserver": "2.1.0",
  "formik": "2.2.5",
  "husky": "4.3.0",
  "i18next": "19.8.4",
  "i18next-browser-languagedetector": "4.0.2",
  "jest-styled-components": "7.0.3",
  "lint-staged": "10.5.2",
  "node-plop": "0.26.2",
  "plop": "2.7.4",
  "prettier": "2.2.0",
  "react": "16.13.0",
  "react-app-polyfill": "1.0.6",
  "react-dom": "16.13.0",
  "react-helmet-async": "1.0.7",
  "react-i18next": "11.7.3",
  "react-redux": "7.2.2",
  "react-router-dom": "5.2.0",
  "react-scripts": "4.0.1",
  "react-test-renderer": "16.13.0",
  "redux-injectors": "1.3.0",
  "redux-saga": "1.1.3",
  "reselect": "4.0.0",
  "sanitize.css": "11.0.0",
  "serve": "11.3.2",
  "shelljs": "0.8.4",
  "styled-components": "5.2.1",
  "stylelint": "13.8.0",
  "stylelint-config-recommended": "3.0.0",
  "stylelint-config-styled-components": "0.1.1",
  "stylelint-processor-styled-components": "1.10.0",
  "ts-node": "8.8.2",
  "typescript": "3.9.7",
  "yup": "0.31.0"
},

仔细看看一些依赖关系

"cross-env":运行在各种平台上设置和使用环境变量的脚本

"eslint-config-prettier":关闭所有可能与更漂亮相冲突的规则

"eslint-plugin-prettier":运行更漂亮,ESLint 规则和日志不同

"fontfaceobserver":监控网页字体何时加载

"husky":防止错误的提交或推送

"i18next":适用于任何 JavaScript 环境或浏览器的国际化框架

"i18next-browser-language detector":检测浏览器中的用户语言

"jest-styled-components":改善测试体验

"lint-staged":通过强制执行设定的代码风格,帮助确保没有错误进入存储库

"node-plop":帮助自动生成代码,无需使用命令行

"plop":便于在整个团队中统一创建文件

增强风格和代码的一致性

"react-app-polyfill":多种浏览器填充,包括常用语言功能

"react-dom":使用 DOM 的 React 包

"react-helmet-async":React 头盔的叉子

"react-i18next":React 的国际化

"react-redux":Redux 官方 React 绑定

"react-router-dom":React 路由的 DOM 绑定

CRA 使用的配置和脚本

"react-test-renderer" : React 包进行快照测试

"redux-injectors":仅在必要时装载 Redux 减速器和 redux-saga,而不是一次全部装载

"redux-saga":用于 Redux 处理副作用的 Saga 中间件

"sanitize.css":确保 HTML 元素和其他默认样式的风格一致

"serve":给出一个清晰的界面来列出目录的内容

"shelljs":基于 Node.js API 的 Unix shell 命令的可移植(Windows/Linux/OS X)实现

帮助防止错误并确保风格的一致性

"ts-node":node . js 的类型脚本执行环境和 REPL,支持源映射

"Cypress":用于端到端测试

在脚本里面插入: "cypress:open": "cypress open"

      $ npm run cypress:open

类型依赖关系

这些是类型脚本定义:

    "@material-ui/core": "4.11.1",
    "@material-ui/icons": "4.9.1",
    "@material-ui/lab": "4.0.0-alpha.56",
    "@material-ui/pickers": "3.2.10",
    "@material-ui/styles": "4.11.1",
    "@reduxjs/toolkit": "1.3.2",
    "@testing-library/jest-dom": "5.1.1",
    "@testing-library/react": "10.0.1",
    "@types/fontfaceobserver": "0.0.6",
    "@types/jest": "25.1.4",
    "@types/node": "13.9.3",
    "@types/react": "16.9.25",
    "@types/react-dom": "16.9.3",
    "@types/react-helmet": "5.0.15",
    "@types/react-redux": "7.1.7",
    "@types/react-router-dom": "5.1.3",
    "@types/react-test-renderer": "16.9.2",
    "@types/styled-components": "5.0.1",
    "@types/testing-library__jest-dom": "5.0.2",
    "@types/webpack-env": "1.15.1",
    "@types/yup": "0.29.9",

查看这里的“脚本”。这非常令人惊讶,因为它们包含了许多脚本,所以您不必亲自动手:

"scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject",
    "test:generators": "ts-node --project=./internals/ts-node.tsconfig.json ./internals/testing/test-generators.ts",
    "cypress:open": "cypress open",
    "start:prod": "npm run build && serve -s build",
    "checkTs": "tsc --noEmit",
    "eslint": "eslint --ext js,ts,tsx",
    "lint": "npm run eslint -- src",
    "lint:fix": "npm run eslint -- --fix src",
    "lint:css": "stylelint src/**/*.css",
    "generate": "cross-env TS_NODE_PROJECT='./internals/ts-node.tsconfig.json' plop --plopfile internals/generators/plopfile.ts",
    "prettify": "prettier --write"
  },

"engines": {
    "npm": ">=6.4.1",
    "node": ">=10.13.0"
  },
  "lint-staged": {
    "*.{ts,tsx,js,jsx}": [
      "npm run eslint -- --fix"
    ],
    "*.{md,json}": [
      "prettier --write"
    ]
  },

  "husky": {
    "hooks": {
      "pre-commit": "npm run checkTs && lint-staged"
    }
  },

"jest": {
    "collectCoverageFrom": [
      "src/**/*.{js,jsx,ts,tsx}",
      "!src/**/*/*.d.ts",
      "!src/**/*/Loadable.{js,jsx,ts,tsx}",
      "!src/**/*/types.ts",
      "!src/index.tsx",
      "!s rc/serviceWorker.ts"
    ],

  "devDependencies": {
    "concurrently": "5.3.0",
    "cypress": "6.0.0",
    "json-server": "0.16.3",
    "json-server-auth": "2.0.2"
  }
}

前面的包不是我们的应用中需要的唯一的包。我们将安装一些第三方 JS 库。

Tip

你可以去我在 https://github.com/webmasterdevlin/practical-enterprise-react/ 的 GitHub 查看我们是否在相同的安装包版本上工作。

现在让我们来设置您的 VS 代码,但是如果您愿意的话,也可以使用您喜欢的 IDE。

设置 Visual Studio 代码(混合文本编辑器)

我们需要熟悉我们的文本编辑器 VS 代码,以帮助我们获得最佳的开发者体验。

安装 VS 代码扩展市场中可用的扩展,根据您的喜好定制您的 IDE:

  • vscode-icons :灯光主题。

  • 材质图标主题:为深色主题。

  • 代码拼写检查器:帮助你发现常见的拼写错误。错误的主要原因之一是打字错误。

  • 导入成本:这让你对你的库的文件大小有一个概念。

  • Version lens :给你一个简单的方法来更新你的软件包,并检查是否有可用的更新。在对主要版本进行更新时要小心,以免破坏您的代码。次要版本或补丁可能是安全的:

    • 单击向上箭头进行更新。进行更改后,再次运行 npm 安装。
  • 路径自动完成:为 VS 代码提供路径自动完成。

  • ES7 React/Redux/graph QL/React-Native snippets:为您的语法提供信心,因为它在您编码时提供了 snippers。

  • Live Share :允许您与其他人实时协作编辑和调试,无论使用或开发的是什么编程语言或工具。

  • ESLint :一个优秀的 linter,它在你运行代码之前检测并警告你语法错误。

  • Chrome 调试器(Debugger for Chrome):VS 代码扩展,用于调试 Google Chrome 浏览器或任何支持 Chrome DevTools 协议的目标中的 JS 代码。

摘要

让我们总结一下到目前为止我们在本章中学到的东西。

我们为 VS 代码安装了各种扩展,以帮助我们在构建应用时获得更好的开发体验。

我们讨论了如何选择正确的样板,在开始构建您的应用时使用样板的优缺点,以及为什么我们选择在我们的应用中使用 React 样板 CRA 模板。

最后但同样重要的是,我们列出了所有已安装的软件包库及其相应的版本号,以确保您在随书一起编码时会使用相同的版本号。

在下一章,我们将继续 React Router,React 的标准路由库,并创建 SPAs,或具有动态路由功能的单页应用。**

五、React 路由的导航

本章使用 React Router 处理导航,React Router 是一个用于在 web 应用中处理和管理路线的特定库,我们将按如下方式处理:

  1. 在根目录下创建一个 JavaScript 文件 router.js。对于 TS 文件,我们将使用 router.tsx。

  2. 使用 react-router-dom 中的路由来定义路径和与路径相关联的组件,例如:

  3. 从 react-router-dom 包装我们在交换机内部创建的所有路由,例如:

<Route path={"/hello"} component={HelloComponent}/>

  1. 使用 react-router-dom 中的链接成功导航到任何页面,例如:
<Switch><Router .../> and more...</Switch>

<Link to="/hello">Hello</Link>

在这个过程中,我们将学习如何在我们正在构建的应用中使用我们首选的设计库,即 Material-UI。

我们还将讨论组件的延迟加载或代码分割,以加快初始加载时间并改善用户体验。在许多相对较小的 React SPAs(单页应用)中,代码分割的概念可能是不必要的,或者可能对性能没有太大影响。然而,如果我们有一个很大的应用,比如说,有一个管理仪表板和一个客户门户,那么在初始加载时尝试加载整个应用并不是一个好主意。

为什么选择 React 路由?

React 路由是 React 的动态路由库。它由以下包组成:react-router、react-router-dom 和 react-router-native。核心是react-router,,而另外两个是特定于环境的。React-router-dom是针对 web 应用,r eact-router-native是针对使用 React Native 的移动应用。

每当我们需要通过我们的 React 应用在多个视图中导航时,路由都会管理 URL。React Router 使我们的应用 UI 和 URL 彼此保持同步。这些好处包括:

  • 它是可堆肥的。

  • 向网页添加链接很容易。

  • 它可以将路由添加到 spa 上的几个不同页面。

  • 它根据来自 URL 的路由有条件地呈现组件。

为什么是 Material-UI?

使用 UI 库的一个基本原因是它节省了我们设计应用的时间。我们不需要重新发明轮子,我们可以使用现成的东西,更重要的是,它已经经过了社区的考验。

当然,选择 UI 设计取决于您的项目规范。不过,通常情况下,我会先看看其他开发人员最推荐和最流行的,比如 Material-UI。同样,我会查看 GitHub 上的活跃贡献者,帮助维护项目的贡献者。在 npmjs.com 中,Material-UI 的周下载量约为 140 万次。

您在应用中可能遇到的任何问题也很有可能已经得到解决,并记录在 Stack Overflow 或其他网站上。

其他值得一试的顶级 UI 设计包括 Ant Design 和 React-Bootstrap。我们将在后面的章节中更多地讨论 React 组件的样式。

入门指南

让我们开始为我们的应用构建 React 路由导航。首先,让我们对一些默认文件进行一些编辑,然后一步一步地重新构建它。

在你的src/app/ folder中,删除以下两个目录:containers/HomePagecomponents/NotFoundPage.见图 5-1 。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 5-1

删除文件夹:容器和组件

它应该会破坏应用中的某些东西。因此,打开文件src/app/index.tsx,用下面的代码行更新文件:

export function App() {
  return (
    <BrowserRouter>
      <Helmet
        titleTemplate="%s - React Boilerplate"
        defaultTitle="React Boilerplate"
      >
        <meta name="description" content="A React Boilerplate application" />
      </Helmet>

      <Switch>
        <Route exact path= '/' component={} />

      </Switch>
      <GlobalStyle />
    </BrowserRouter>
  );
}

Listing 5-1Updating index.tsx

接下来,在 app 目录下,创建另一个名为views的文件夹。在views下,创建另一个名为pages的文件夹。最后,在pages目录中,创建一个新文件,并将其命名为Home.tsx。文件夹结构见图 5-2 。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 5-2

新文件夹结构

如果使用 VS 代码,打开文件Home.tsx并输入代码片段rafce(React 箭头函数导出组件的缩写)。在 WebStorm 中,片段是rsc(React 无状态组件的简称,没有 prop 类型和 ES6 模块系统)。

图 5-3 展示了如何在我们的代码编辑器中使用代码片段。确保您已经从 VS 代码市场安装了扩展 ES7 React/Redux/graph QL/React-Native snippets。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 5-3

键入 rafce 片段,react arrow 函数导出组件的缩写

如清单 5-2 所示,在返回声明<h1>Home Page</h1>中添加标题。

import React from 'react';

const Main = () => {
  return (
    <div>
      <h1>Home Page</h1>
    </div>
  );
};

export default Main;

Listing 5-2Adding h1 Heading to the Home Page

在我们的app/index.tsx文件中,我们将 Home 组件添加到我们的 Route 路径中,如清单 5-3 所示。

export function App() {
  return (
    <BrowserRouter>
      <Helmet
        titleTemplate="%s - React Boilerplate"
        defaultTitle="React Boilerplate"
      >
        <meta name="description" content="A React Boilerplate application" />
      </Helmet>

      <Switch>
        <Route exact path="/" component={Home} />
      </Switch>
      <GlobalStyle />
    </BrowserRouter>
  );
}

Listing 5-3Adding the Home Component to routes.tsx

当您运行应用并转到默认的localhost:3000时,您应该会看到呈现的页面。

图 5-4 是在 UI 中呈现的主页。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 5-4

将主页呈现给用户界面

我们在这里做了什么?我们已经展示了使用 React Router 导航应用的简单方法。

接下来,让我们创建另一个文件,并将其命名为material-buttons.tsx,,路径如下:app/components/material-buttons.tsx.

这里我们将在 UI 中呈现一些按钮。我们将使用来自我们之前导入的 Material-UI 的设计。去 Material-UI 的网站,具体是按钮组件。

图 5-5 显示了到 Material-UI 网站的链接。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 5-5

使用材质 UI 按钮组件

选择包含的按钮设计。确保选择 TypeScript 选项,复制完整的源代码并粘贴到我们的material-buttons.tsx文件中。

Important Note

将文件从ContainedButtons重命名为MaterialButtons

之后,转到Home.tsx使用新创建的材质 UI 按钮组件,如清单 5-4 所示。

import React from "react";
import MaterialButtons from 'app/components/material-buttons’;

const Home = () => {
  return (
    <div>
          <h1>Main Page</h1>
          <MaterialButtons/>
    </div>
  );
};

export default Home;

Listing 5-4Copying the Material-UI Button Component

在您的本地主机中,您应该会看到以下变化,如图 5-6 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 5-6

呈现材质-UI 按钮组件

既然我们已经有了可以在设计中使用 Material-UI 库的概念证明,我们就可以开始配置路由和导航了。

但是首先,让我们快速回顾一下 React 路由的安装。

基础概述

通过安装作为样板文件一部分的react-router-dom,我们可以访问index.tsx中的以下三个组件:<BrowserRouter>, <Route>,<Switch>.

打开您的index.tsx文件,让我们逐一讨论:

<BrowserRouter>是基本配置。它包装了其他组件,并使它们与 URL 保持同步。

<Switch>是 web 应用的动态部分。顾名思义,它会根据 URL 进行更改或动态切换。Switch 组件确保将呈现匹配 URL 位置的第一个路由子组件。

<Route>需要一个“路径属性”,当它获得匹配的或精确的 URL 路径时被呈现。

现在让我们看看如何在应用中导航 URL 路径,从一个组件到达下一个组件。

React-router-dom允许我们改变路径的几种方式,包括最常见的一种叫做<Link>的标签,我们会在我们的应用中经常用到。为了更清晰的代码和关注点的分离,我们将在the app directory and name it routes.tsx.创建一个新文件

创建 routes.tsx

我们的路线路径可能会变得更长,因此我们将创建一个新文件,并将其命名为routes.tsx .,而不是在我们的app/index.tsx文件中构建所有路线

以下是文件路径:

app ➤ routes.tsx

接下来,我们将把 index.tsx 中的<Switch>和组件移动到我们新创建的和routes.tsx中,如清单 5-5 所示。

import React, { lazy, Suspense } from 'react';
import { Switch, Route } from 'react-router-dom';
import Home from './views/pages/Home';

const Routes = () => {
  return (
      <Switch>
        <Route exact path="/" component={Home} />
       </Switch>
);
}
export default Routes;

Listing 5-5Moving Switch and Route Components to routes.tsx

现在我们需要使用我们在index.tsx,新创建的<Routes />,如清单 5-6 所示。

export function App() {
  return (
    <BrowserRouter>
      <Helmet
        titleTemplate="%s - React Boilerplate"
        defaultTitle="React Boilerplate"
      >
        <meta name="description" content="A React Boilerplate application" />
      </Helmet>
      <Routes/>
      <GlobalStyle/>
    </BrowserRouter>
  );
}

Listing 5-6Using the Routes in index.tsx

让我们创建另一个视图页面,并将其命名为路径 views/pages 下的AboutPage.tsx。添加一个

这是关于页面

清单 5-7 是关于页面组件。

import React from 'react';

const AboutPage = () => {

return (
  <div>
   <h1>This is the About Page</h1>
  </div>
);
 };
export default AboutPage;

Listing 5-7Creating AboutPage and Adding h1 Heading

不要忘记在 routes.tsx 中添加新组件,并在顶部导入 About Page 组件:

<Route exact path="/about" component={AboutPage} />

检查你的localhost:3000/about看看它是否还在工作。

Note

当构建一个应用时,我通常在编写任何代码之前收集需求。我总是检查我是否需要为应用创建一个仪表板或一个管理仪表板。我们尽可能首先制定路由,并确定是否有嵌套路由。

现在,我们可以在应用中导航 URL 路径。第一项任务是构建一个仪表板。

构建仪表板

进入 Material-UI 的网站,搜索一个导航栏或应用栏。他们是一样的。抓取简单 App Bar 的 TS 源代码。见图 5-7 中简单 App 栏截图。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 5-7

Material-UI 网站上的简单应用栏截图

导航栏

navbar组件是我们放在应用顶部的东西,它允许我们在不同的页面之间切换,比如Home, About, Login,等。

在文件夹 components 下创建一个新文件,并将其命名为navigation-bar.tsx.

粘贴从 Material-UI 简单应用栏抓取的源代码(见图 5-8 )。不要忘记将默认文件名从ButtonAppBar更改为NavigationBar.

此时,让我们删除文件material-buttons.tsx,因为我们不再需要它了。我们创建它只是为了快速展示如何在组件页面中使用材质 UI 设计。

之后,再次打开app/index.tsx,渲染NavigationBar。确保将它放在<Routes/>组件的顶部或之前,以将导航栏永久定位在 web 应用的顶部区域,如清单 5-8 所示。

export function App() {
  return (
    <BrowserRouter>
      <Helmet
        titleTemplate="%s - React Boilerplate"
        defaultTitle="React Boilerplate"
      >
        <meta name="description" content="A React Boilerplate application" />
      </Helmet>
      <NavigationBar />
      <Routes/>
      <GlobalStyle/>
    </BrowserRouter>
  );
}

Listing 5-8Using the NavigationBar in index.tsx

检查您的localhost:3000以查看 UI 中的导航栏。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 5-8

在浏览器中显示导航栏

我们将在接下来的几章中详细讨论各种 React 样式方法。但同时,我只想指出,Material-UI 的样式解决方案来自许多其他样式库,如 styled-components。

Material-UI 的核心是使用在运行时和服务器端工作的 CSS-in-JS。我们将在后面的章节中更多地讨论 CSS-in-JS。

看看 Material-UI 中的导航栏样式组件。我们使用 makeStyles,它基本上允许我们为每个样式表创建多个样式规则。然后,我们将在 NavigationBar 组件内部调用返回的函数 useStyles:

const useStyles = makeStyles((theme: Theme) =>
  createStyles({
    root: {
      flexGrow: 1,
    },
    menuButton: {
      marginRight: theme.spacing(2),
    },
    title: {
      flexGrow: 1,
    },
  }),
);

在我看来,Material-UI 是最好的样式库之一,尤其是对于 React 应用。这些组件是可重用的、快速的和声明性的。在其 GitHub 上,它也有超过 63K 颗恒星,这意味着我并不孤单,在我看来。

添加导航链接

让我们在导航栏中添加一些按钮和导航链接,并将它们命名如下——<Home> <About> <Dashboard> – as shown in列表 5-9 。

不要忘记从“react-router-dom”导入命名组件:

import { Link } from 'react-router-dom';

Note

我们将在本章后面详细讨论 React 应用可以使用的不同导航路径,但是现在,让我们先构建我们的组件。

import React from 'react';
import { createStyles, makeStyles, Theme } from '@material-ui/core/styles';
import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import Button from '@material-ui/core/Button';
import { Link } from 'react-router-dom';
import { colors } from '@material-ui/core';

export default function NavigationBar() {
  const classes = useStyles();

  return (
    <div className={classes.root}>
      <AppBar position="static">
        <Toolbar>
          <Link className={`${classes.link} ${classes.title}`} to={'/'}>
            LOGO
          </Link>
          <Button color="inherit">
            <Link to={'/'}>
              Home
            </Link>
          </Button>
          <Button color="inherit">
              About
          </Button>
          <Button color="inherit">
              Dashboard
          </Button>
          <Button color="inherit">
              Login
          </Button>
        </Toolbar>
      </AppBar>
    </div>
  );
}

Listing 5-9Adding Navigation Links to the navbar

同时,用 logo 替换单词“News ”,因为那是我们稍后要放置图标 LOGO 的地方。

我们还添加了链接样式对象。参见清单 5-10 中的编辑。

const useStyles = makeStyles((theme: Theme) =>
  createStyles({
    root: {
      flexGrow: 1,
    },
    menuButton: {
      marginRight: theme.spacing(2),
    },
    link: {
     color: colors.lightBlue[50],
     textDecoration: 'none',
     },
    title: {
      flexGrow: 1,
    },
  }),
);

Listing 5-10Adding a link Style Object

正如你在清单 5-11 中看到的,我们现在可以使用保留的 React 单词<className>.在我们的按钮导航链接中使用它

首先,我们通过调用useStyles钩子并将其存储在一个变量中来使用它。出于可读性的考虑,我们将变量命名为classes:

  const classes = useStyles();

然后让我们将导航链接添加到其余的按钮上。

export default function NavigationBar() {
  const classes = useStyles();

  return (
    <div className={classes.root}>
      <AppBar position="static">
        <Toolbar>
          <Link className={`${classes.link} ${classes.title}`} to={'/'}>
            LOGO

          </Link>
          <Button color="inherit">
            <Link className={classes.link} to={'/'}>
              Home
            </Link>
          </Button>
          <Button color="inherit">
            <Link className={classes.link} to={'/about'}>
              About
            </Link>
          </Button>
          <Button color="inherit">
            <Link className={classes.link} to={'/dashboard'}>
              Dashboard
            </Link>
          </Button>
          <Button color="inherit">
            <Link className={classes.link} to={'/login'}>
              Login
            </Link>
          </Button>
        </Toolbar>
      </AppBar>
    </div>
  );
}

Listing 5-11Adding the Navigation Links and CSS Class Objects in the NavigationBar

在我们的 LOGO 组件中,注意我们需要使用反勾号``来使用按钮组件中的两个 CSS 类对象。

Link className={`${classes.link} ${classes.title}`} to={'/'}> LOGO </Link>

Listing 5-12Using Backticks for Two or More CSS Class Objects

请务必清理任何未使用或变灰的导入库。

接下来,让我们在 views/pages 下添加另一个组件,并将其命名为NotFoundPage.tsx.

再次键入代码片段“rafce”,以便为我们创建无状态箭头组件。并且还添加了标题

404 页面未找到

import React from 'react';

const NotFoundPage = () => {
  return (
      <div>
         <h1>404 Page Not Found</h1>
      </div>
      )
 }
export default NotFoudPage;

Listing 5-13Creating a 404 Not Found Page

别忘了定义NotFoundPage after importing it in routes.tsx的路径:

import NotFoundPage from './views/pages/NotFoundPage';
<Route exact path="/not-found"component={NotFoundPage} />

导航 React 路由:

在我们的routes.tsx中,让我们添加<Redirect />,,幸运的是它已经内置在 react-router-dom 中。

有许多将用户重定向到特定页面的用例。一般来说,如果我们需要改变 URL 路径而不需要用户点击该路径的链接,我们使用<Redirect />

这里有几个条件:

  • 当用户访问受限页面时。

  • 当用户正在访问找不到或不再存在的页面时。

  • 当用户在地址栏中键入错误的路径时(输入错误!).

  • 登录成功,用户现在被定向到主页或仪表板。

现在,让我们为所有没有在 404 页面上定义的路径创建一个<Redirect />:

<Route path={'/not-found'} component={NotFoundPage} exact />
        <Redirect from={'*'} to={'/not-found'} exact />

在我们结束这一部分之前,让我们在页面上做一些样式设计,尤其是填充和边距。

添加容器样式的类组件

转到app/index.tsx,,我们将使用 Material-UI 将<Routes/>包装在一个容器中。从 Material-UI 导入命名组件,直接使用包装 Routes 组件:

...
import { Container } from '@material-ui/core';

<Container>
    <Routes />
</Container>

检查浏览器,你会注意到,如图 5-9 所示,标题< h1 >不再被推到左上角。该样式适用于在<Container> <Routes/> </Container>中包装的所有页面。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 5-9

使用材质中的容器-用户界面

但是不要太担心这些页面的特定样式。我们只是向您展示使用 Material-UI 来设计我们的应用的简单方便。

请记住,本书的主要目标是指导您构建企业级应用,并让您了解开发人员如何类似地开发他们的应用。最后,你还是要根据你的具体需求来决定如何制作你的应用。

那么仪表盘呢?让我们现在就开始吧。

创建仪表板布局

app文件夹中,创建一个名为layouts,的文件夹,然后创建一个子文件夹,命名为dashboard-layout

在仪表板布局文件夹中,创建文件dashboard-sidebar-navigation.tsx

路径是 app布局仪表盘-布局仪表盘-工具条-导航. tsx

让我们用下面的代码填充新创建的文件。首先,导入以下库。

import React, { useEffect } from 'react';
import { Link } from 'react-router-dom';
import { createStyles, makeStyles } from '@material-ui/core/styles';
import Drawer from '@material-ui/core/Drawer';
import Toolbar from '@material-ui/core/Toolbar';
import { useRouteMatch } from 'react-router';

Listing 5-14Importing Libraries to dashboard-sidebar-navigation.tsx

好了,在我们继续之前,让我们简要解释一下我们在这里引入的几个不熟悉的库。我们将从react-router-dom中姗姗来迟的<Link>和应用中导航路径的其他方式开始。

导航路径:

来自react-router-dom<Link>允许用户导航应用,并在不刷新的情况下重新呈现页面。它为我们的应用提供了一个简单明了的导航路径。

我们可以使用它的一些常见方法:

  • to: string:通过连接位置的路径名、搜索和哈希属性创建。比如:<Link to="/products/?sort=name" />

  • to: object:这可以是以下任何属性:路径名、搜索、散列和状态。

  • replace: bool:如果为 true,单击链接将替换历史栈中的当前条目,而不是添加新条目。比如:<Link to="/products" replace />

  • to: function:当前位置作为参数传递,位置表示应该作为对象或字符串返回。例如:

<Link to={location => ({ ...location, pathname: "/products" })} />
<Link to={location => `${location.pathname}?sort=name`} />

React 钩子:使用者游戏

useRouteMatch是一个钩子,我们在使用<Route>.时通常需要它,useRouteMatch 允许我们访问一个<match>对象,并在组件内部使用它;所以不用渲染一个<Route>,,直接用useRouteMatch.

好了,现在我们已经讨论了我们在这里导入的一些库,现在让我们创建我们的DashboardSidebarNavigation组件。

const DashboardSidebarNavigation = () => {
  const classes = useStyles();
  const { url } = useRouteMatch();

  useEffect(() => {}, []);

  return (

      <div className={classes.root}>
        <Drawer
          className={classes.drawer}
          variant="permanent"
          classes={{
            paper: classes.drawerPaper,
          }}
          anchor="left"
        >
          <Toolbar
            style={{ width: '6rem', height: 'auto' }}
            className={classes.toolbar}
          >
            <Link to={`${url}`} className={classes.logoWithLink}>
              Logo
            </Link>
          </Toolbar>

        </Drawer>
      </div>

  );
};

export default DashboardSidebarNavigation;

Listing 5-15Creating the DashboardSidebarNavigation Component

我们使用 Material-UI 抽屉和工具栏的样式,并使用组件作为我们的导航路径。

样式组件来自 Material-UI。

const drawerWidth = 240;

const useStyles = makeStyles(theme =>
  createStyles({
    root: {
      display: 'flex',
    },
    drawer: {
      width: drawerWidth,
      flexShrink: 0,
    },
    drawerPaper: {
      width: drawerWidth,
    },
    drawerContainer: {
      overflow: 'auto',
    },
    toolbar: theme.mixins.toolbar,
    content: {
      flexGrow: 1,
      padding: theme.spacing(3),
    },
    link: { textDecoration: 'none', color: 'inherit' },
    logoWithLink: {
      display: 'flex',
      alignItems: 'center',
      textDecoration: 'none',
      color: 'inherit',
    },
  }),
);

Listing 5-16Styling the dashboard-sidebar-navigation Using Material-UI

完成dashboard-sidebar-navigation.tsx,后,在dashboard-layout文件夹下创建一个index.tsx并复制下面的代码。

import React from 'react';
import { Grid } from '@material-ui/core';

import DashboardSidebarNavigation from './dashboard-sidebar-navigation';

type Props = {
  children: React.ReactNode;
};

const Dashboard = ({ children }: Props) => {
  return (
    <Grid
      container
      direction="row"
      justify="flex-start"
      alignItems="flex-start"
    >
      <DashboardSidebarNavigation /> {children}
    </Grid>
  );
};

export default Dashboard;

Listing 5-17Creating the Dashboard Component

仪表板组件

这是怎么回事?我们有主仪表板组件及其子属性,它们是主要的内容页面。子属性为用户提供了从一个视图或页面成功导航到另一个视图或页面的路由。

请记住,仪表板中的 children 参数是 React 组件的入口点。例如,如果我们在仪表板中传递一个按钮组件,它将在 DashboardSidebarNavigation 组件旁边呈现。

我们现在有了一个包含 DashboardSidebarNavigation 的可重用仪表板布局。

至于<Grid>,,它用于布局的定位。基于 12 列网格布局的网格响应布局非常强大,因为它可以适应任何屏幕大小和方向。

现在我们已经有了布局,下一步是创建一个仪表板默认组件,这是用户在仪表板中看到的第一个东西。

在视图中,创建一个新的仪表板文件夹,并在其中创建两个新文件:dashboard-default-content.tsxsettings-and-privacy.tsx

路径看起来像这样:

 Views ➤ dashboard ➤ dashboard-default-content.tsx
 Views ➤ dashboard ➤ settings-and-privacy.tsx

键入代码片段“rafce”创建一个无状态箭头组件,并返回一个标题与组件名称相同的

。请参见下面的示例。

此外,请确保在组件名称中重命名并使用 Pascal 命名约定。

import React from 'react';

const DashboardDefaultContent = () => {
  return (
    <div>
      <h1>Dashboard Default Content</h1>
    </div>
  );
};

export default DashboardDefaultContent;

Listing 5-18Creating the DashboardDefaultContent Component

settings-and-privacy.tsx文件做同样的事情。

import React from 'react';

const SettingsAndPrivacy = () => {
  return (
    <div>
      <h1>Settings and Privacy</h1>
    </div>
  );
};

export default SettingsAndPrivacy;

Listing 5-19Creating the SettingsAndPrivacy Component

目前,我们只是设置了没有样式的仪表板和导航,因为我们只是想展示一个概念证明,我们能够成功地从一个页面导航到下一个页面。

现在,让我们在routes.tsx中定义路径。但在这种情况下,我们将使用渲染属性。

渲染属性

www.reactjs.org 网站上,术语渲染 属性指的是“使用一个值为函数的属性在 React 组件之间共享代码的技术。”

好吧,简单地说,这意味着我们的路由组件有一个渲染属性,它将一个函数作为一个值。然后,路由组件使用该函数来呈现该函数提供的任何内容——在我们的例子中,就是仪表板。

实际上,我们只是以一种有效的方式重用代码。

<Route path={'/dashboard'}
                render={({match: {path}}) => (
                    <Dashboard>
                        <Switch>
                            <Route exact path={path + '/'}
                                component={DashboardDefaultContent}/>

   <Route exact path={path + '/settings-and-privacy'}
                component={SettingsAndPrivacy}/>

                        </Switch>
                    </Dashboard>
                   )}>
 </Route>

Listing 5-20Using the Render Props in routes.tsx

在 React 路由库中,<Route />具有导航到不同视图或页面的行为。当路径匹配时,它呈现组件。

接下来,让我们更新dashboard-sidebar-navigation.tsx来添加设置和隐私按钮以及注销按钮。

为了有一点风格,首先,让我们导入组件,如清单 5-21 所示,从 Material-UI 到我们的dashboard-sidebar-navigation.tsx

import { List, ListItem, ListItemIcon, ListItemText } from '@material-ui/core';
import ExitToAppIcon from '@material-ui/icons/ExitToApp';
import SettingsIcon from '@material-ui/icons/Settings';

Listing 5-21Importing the Material-UI Components to dashboard-sidebar-navigation.tsx

仪表板侧边栏导航的附加样式组件如清单 5-22 所示。

紧接在之后且仍在内,添加以下代码:

<return (

      <div className={classes.root}>
        <Drawer
          className={classes.drawer}
          variant="permanent"
          classes={{
            paper: classes.drawerPaper,
          }}
          anchor="left"
        >
          <Toolbar
            style={{ width: '6rem', height: 'auto' }}
            className={classes.toolbar}
          >
            <Link to={`${url}`} className={classes.logoWithLink}>
              Logo
            </Link>
          </Toolbar>
          <div className={classes.drawerContainer}>
            <List>
              <Link className={classes.link} to={`${url}/settings-and-privacy`}>
                <ListItem button>
                  <ListItemIcon>
                    <SettingsIcon />
                  </ListItemIcon>
                  <ListItemText primary={'settings and privacy'} />
                </ListItem>
              </Link>
              <a className={classes.link} href={'/'}>
                <ListItem button>
                  <ListItemIcon>
                    <ExitToAppIcon />
                  </ListItemIcon>
                  <ListItemText primary={'logout'} />
                </ListItem>
              </a>
            </List>
          </div>
        </Drawer>
      </div>

  );
};

Listing 5-22Additional Components and Styling for dashboard-sidebar-navigation.tsx

如果您在我们的 UI 中单击“Dashboard”按钮,您应该能够看到以下附加组件:

设置和隐私

注销

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 5-10

仪表板默认内容用户界面

如果您单击设置和隐私,您应该可以成功导航到该页面。当你点击主页、关于或登录时也是一样——最后一个页面目前只是一个 404 页面,因为我们还没有构建它。此外,注销,现在,只是基本上刷新页面。

进入 React.lazy()

但是等等。我们还没完呢。我们仍然可以改进我们的路线导航。我们可以在惰性加载的帮助下做到这一点,惰性加载是加速我们的包的加载时间的最有效的方法之一。

那么什么是懒加载呢?

惰性加载的概念很容易理解。它仅仅是首先呈现用户界面中最重要的部分,同时根据需要悄悄地加载不重要的部分或项目。

React 中的延迟加载是 16.6.0 版本中发布的一个相对较新的特性。随着应用变得越来越复杂、强大和庞大,导致加载时间增加,并对用户体验产生负面影响。

作为最佳实践,我们应该始终考虑用户,包括那些使用移动数据和慢速互联网连接的用户。

Note

React.lazy(),现在已经完全集成到核心的 React 库中,已经取代了一个第三方库 react-loadable。

但是使用 react-loadable 进行服务器端代码拆分,因为 React.lazy()和悬念还不能用于服务器端渲染。

React lazy loading 允许我们使用代码分割缓慢或逐渐加载组件,而无需安装额外的库。这种方法使我们能够在初始加载时呈现必要的或关键的界面项,同时根据需要懒散地、安静地展开其他部分。

根据 React 官方文档,在 React 应用中进行代码拆分的最佳方式是通过动态导入()语法。急切加载是我们的 web 应用的默认行为,这意味着如果你下载所有的页面资源,例如,主页,那么一切都将被一口气下载完。

这样做的潜在问题是,如果应用很大,它会产生一个瓶颈——因此,我们需要进行代码拆分。

让我们展示一些代码拆分前后的示例代码。

import MyComponent from './MyComponent';

function OneComponent() {
     return (
          <div>
               <MyComponent />
          </div>
     );
}

Listing 5-23Showing Eager Loading vs. Lazy Loading

const MyComponent = React.lazy(() => import('./MyComponent'));

function OneComponent() {
     return (
           <div>
                <MyComponent />
           </div>
     );
}

注意,使用React.lazy()创建的组件需要包装在 React 中。悬念,所以我们现在来回顾一下。

React 悬念

当惰性组件被加载时,悬念就像占位符内容。请注意,我们可以用一个暂记组件在不同的层次级别包装几个惰性组件。

一个小小的警告:在 React 官方文档中,悬念仍然是一个实验性的 API,在 React 团队表示它已经完全可以投入生产之前,它仍然可能会发生一些变化。

然而,这个已经发布了几年的通知并没有阻止 React 开发者在大型应用或生产级应用中使用 React.lazy()和悬念。

提示:为了获得最佳的用户体验,如果你的懒惰组件加载失败,就将它封装在一个错误边界<ErrorBoundary />中。

让我们看看 DevTools,展示一下主页和 About Page 的急切加载。一切一气呵成,如图 5-11 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 5-11

DevTools 中急切加载主页和 About 页面的截图

你会看到所有的组件甚至在应用初始加载时就已经加载了。这意味着,即使您从主页导航到“关于”页面,也不会添加任何新内容。

现在,让我们来看看一个急切加载的组件和一个延迟加载的组件之间的区别。

急切加载与缓慢加载

现在,让我们比较一个包装在延迟加载中的组件和一个渴望加载的组件。

在我们的routes.tsx中,导入命名的组件:

import React, {lazy, Suspense}from 'react';

但是在我们比较这两个组件之前,我们需要用 React 悬念来包装我们的懒惰组件。所以让我们现在就开始吧。

接下来,为了便于比较,我们将延迟加载以下组件: About 和 Dashboard

const Routes = () => {
  return (
    <Suspense>
    <Switch>
      {/*eager loading */}
      <Route exact path="/" component={Home} />

      {/*lazy loading */}
      <Route exact path="/about" component={AboutPage} />

      {/*lazy loadings*/}
        <Route
         exact path={'/about'}
          component={lazy(() => import('./views/pages/AboutPage'))}
           />

        <Route
           exact path={'/dashboard'}
          render={({ match: { path } }) => (
            <Dashboard>
              <Switch>
                <Route
                  path={path + '/'}
                  component={lazy(
                    () => import('./views/dashboard/dashboard-default-content'), )}
                />

              <Route
                exact
                path={path + '/settings-and-privacy'}
                component={SettingsAndPrivacy}
              />
            </Switch>
          </Dashboard>
        )}
      ></Route>

      <Route exact path="/not-found" component={NotFoundPage} />
      <Redirect from={'*'} to={'/not-found'} exact />
    </Switch>

    </Suspense>
  );
};

Listing 5-24Lazy Loading the Components About and Dashboard for Comparison

但是等等。如果您查看浏览器,您会看到以下错误。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 5-12

未能编译 React 悬念

这是因为悬念需要属性“回退”

React 查找树,遇到第一个<Suspense>组件,并呈现其回退。

使用所需的暂记属性 fallback,并且不要忘记导入命名的组件。线性进度条来自 Material-UI。

...
import { LinearProgress } from '@material-ui/core';

export const Routes = () => {
  return (
    <Suspense fallback={<LinearProgress style={{ margin: '10rem' }} />}>

Listing 5-25Using the Suspense Prop fallback

好了,这应该让我们的悬念部分工作了。你也可以延迟加载设置和隐私,但是一定要让主页保持即时加载,这样我们就可以看到比较了。

刷新浏览器。刷新主页的同时打开 DevTools,如图 5-13 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 5-13

在初始加载时急切加载主页

但是,当您单击延迟加载的 About 页面时,您可以看到突出显示的部分只是添加的部分。突出显示的部分最初没有呈现在主页上,因为还不需要它们。参见图 5-14 。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 5-14

延迟加载关于页面

当您单击延迟加载的仪表板时,您会注意到同样的情况。参见图 5-15 。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 5-15

延迟加载仪表板

当我们从 About 页面导航到 Dashboard 页面时,突出显示的部分是唯一加载的内容。

你可能认为文件和加载时间可能无关紧要或很少,但是想象一下如果你的应用已经有了大量的代码,你有 50 页或更多。你的用户,尤其是使用移动应用或网速较慢的用户,会感觉到这一点。

摘要

我希望到目前为止,您已经对客户端路由的一般工作原理以及如何使用 React 路由库在 React 中实现路由有了一个大致的了解。我们还谈到了如何在我们的应用中使用延迟加载来加速我们的初始加载时间,最终目标是改善用户体验。

在下一章,我们将处理写本地状态、发送 HTTP 请求和使用 ApexCharts。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值