翻译 | 《JavaScript Everywhere》第12章 使用React构建Web客户端
写在最前面
大家好呀,我是毛小悠,是一位前端开发工程师。正在翻译一本英文技术书籍。
为了提高大家的阅读体验,对语句的结构和内容略有调整。如果发现本文中有存在瑕疵的地方,或者你有任何意见或者建议,可以在评论区留言,或者加我的微信:code_maomao,欢迎相互沟通交流学习。
(σ゚∀゚)σ..:*☆哎哟不错哦
第12章 使用React构建Web客户端
超文本背后的原始想法是获取相关文档并将它们链接在一起:如果学术论文A
引用学术论文B
,让我们可以轻松地单击某些内容并在它们之间导航。1989
年,CERN
的一位名为Tim Berners-Lee
的软件工程师提出了将超文本与联网计算机相结合的想法,从而使人们可以轻松地建立这些连接,而不管文档的位置如何。每张猫的照片、新闻、推文、流媒体视频、求职网站和餐厅评论都应归功于全局链接文档的简单想法。
从本质上讲,网络仍然是将文档链接在一起的媒介。在网络浏览器中,每个页面都是HTML
,带有CSS
(用于样式设置)和JavaScript
(用于增强功能)。今天,我们使用这些技术来构建从个人博客和小型手册站点到复杂的交互式应用程序的所有内容。其根本优点是Web
提供了通用访问权限。任何人只需要一个可以连接网络的网络浏览器,会创建一个默认的环境。
我们正在构建什么
在接下来的章节中,我们将为社交笔记应用Notedly
构建Web
客户端。用户将能够创建和登录帐户,在Markdown
中编写笔记,编辑他们的笔记,查看其他用户笔记的摘要以及“收藏”其他用户笔记。为此,我们将与GraphQL
服务器API
进行交互。
在我们的Web
应用程序中:
- 用户将能够创建笔记、阅读、更新和删除他们创建的笔记。
- 用户将能够查看其他用户创建的笔记的摘要,并阅读其他人创建的单个笔记,尽管他们将无法更新或删除它们。
- 用户将能够创建帐户,登录和注销。
- 用户将能够检索其个人资料信息以及其他用户的公共个人资料信息。
- 用户将能够收藏其他用户的笔记以及检索其收藏夹列表。
这些功能将涉及很多领域,但是在本书的这一部分中,我们将把它们分成小块。一旦学会了使用所有这些功能构建React
应用程序,就可以将工具和技术应用于构建各种富Web
应用程序。
我们将如何构建它
你可能已经猜到了,要构建此应用程序,我们将使用React
作为客户端JavaScript
库。此外,我们将从GraphQL API
查询数据。为了帮助查询,修改和缓存数据,我们将使用Apollo
客户。Apollo Client
包含用于使用GraphQL
的一系列开源工具。我们将使用库的React
版本,但是Apollo
的团队还开发了Angular
,Vue
,Scala.js
,Native iOS
和Native Android
集成。
其他GraphQL客户端库
尽管我们将在本书中使用Apollo
,但它远远不是唯一一个GraphQL
客户端选项。Facebook
的Relay
和Formiddable
的urql
也是两个受欢迎的选择。
此外,我们将使用parcel
作为我们的代码捆绑器。代码捆绑器使我们可以使用Web
浏览器中可能不具备的功能(例如,较新的语言功能,代码模块,压缩)编写JavaScript
,并将其打包以供在浏览器环境中使用。Parcel
是Webpack
等应用程序构建工具的无配置替代方案。它提供了许多不错的功能,例如代码拆分和在开发过程中自动更新浏览器(又称热模块替换),而无需建立构建。如上一章所述,create-react-app
它还提供了零配置的初始设置,在后台使用Webpack
,但Parcel
允许我们从头开始构建应用程序,这是我发现学习的理想方式。
入门
在开始开发之前,我们需要将项目启动代码文件复制到我们的电脑上。
项目的源代码包含我们开发应用程序所需的所有脚本和对第三方库的引用。要将代码克隆到本地计算机,请打开终端,导航到保存项目的目录,然后git clone
项目存储库。如果你已经遍历了API
章节,则可能已经创建了一个notedly目录来保持项目代码的条理性:
# change into the Projects directory
$ cd
$ cd Projects
$ # type the `mkdir notedly` command if you don't yet have a notedly directory
$ cd notedly
$ git clone [email protected]:javascripteverywhere/web.git
$ cd web
$ npm install
安装第三方依赖项
通过使用本书的入门代码的副本并在目录中运行npm install
,你无需为任何第三方依赖项再次运行npm install
。
该代码的结构如下:
- /
src
这是你随书一起进行开发的目录。
- /
solutions
该目录包含每章的解决方案。如果你卡住了,这些可以供你参考。
- /
final
该目录包含最终的工作项目。
现在,你已经在电脑上安装了代码,复制项目的.env
文件。这个文件保存了我们特殊的工作环境变量。
例如,在本地工作时,我们将指向API
的本地实例,但是在部署应用程序时,我们将指向我们远程的API
。复制.env
文件,从Web
目录在终端中键入以下内容:
$ cp .env.example .env
你现在应该看到一个.env
文件。你无需对该文件做任何事情,但是随着API
后端的开发,我们将向其中添加信息。项目附带的.gitignore
文件将确保你不会无意间提交.env
文件。
求助,我看不到.env文件!
默认情况下,操作系统隐藏以句点开头的文件,因为这些文件通常由系统使用,而不是最终用户使用。如果看不到.env
文件,请尝试在文本编辑器中打开目录。该文件应该在编辑器的文件浏览器中可见。或者,在终端窗口中键入ls -a
将列出当前工作目录中的文件。
构建Web应用程序
在本地克隆了启动代码之后,我们就可以构建React Web
应用程序了。首先让我们看一下src/index.html
文件。这看起来像一个标准的但完全为空的HTML
文件,但请注意以下两行:
这两行对我们的React
应用程序非常重要。
root
将提供整个应用程序的容器。同时,App.js
文件将成为我们JavaScript
应用程序的入口。
现在我们可以开始在src/App.js
文件中开发React
应用程序了。如果你跟随上一章中的React
简介,可能会觉得很熟悉。在src/App.js
中,我们首先导入react
和react-dom
库:
import React from 'react';
import ReactDOM from 'react-dom';
现在,我们将创建一个名为App
的函数,该函数将返回应用程序的内容。现在,这将只是元素中包含的两行HTML
:
const App = () => {
return (
Hello Notedly!
Welcome to the Notedly application
);
};
和所有的div相关的是什么?
如果你只是从React
入手,你可能会想知道用标签包围组件的趋势。React
的组件必须包含于父元素,这往往是一种标签,但也可以是任何其他适当的HTML
标签,例如,,或.
如果感觉包含HTML
的标记多余,我们可以用空的<>标记在我们的JavaScript
代码中包含这些组件。
最后,我们将通过添加以下内容来指示React
在根ID
为root
的元素内渲染应用程序:
ReactDOM.render( , document.getElementById('root'));
现在,我们的src/App.js
文件的完整内容应为:
import React from 'react';
import ReactDOM from 'react-dom';
const App = () => {
return (
Hello Notedly!
Welcome to the Notedly application
);
};
ReactDOM.render( , document.getElementById('root'));
完成此操作后,让我们在Web
浏览器中进行查看。通过在终端应用程序中键入npm run dev
来启动本地开发服务器。编译代码后,请访问 http://localhost:1234
来查看页面(图12-1
)。
图12-1
我们最初在浏览器中运行的React
应用程序
路由
网络的定义特征之一是能够将文档链接在一起。同样,对于我们的应用程序,我们希望用户能够在屏幕或页面之间导航。在HTML
呈现的应用程序中,这将涉及创建多个HTML
文档。每当用户导航到新文档时,即使两个页面上存在共享的方面(例如页眉或页脚),整个文档也会重新加载。
在JavaScript
应用程序中,我们可以利用客户端路由。在许多方面,这将类似于HTML
链接。用户将单击一个链接,URL
将更新,并且他们将导航到新屏幕。不同之处在于我们的应用程序只会使用更改后的内容来更新页面。体验将是“类似于应用程序的”的流畅,这意味着将不会看到页面的刷新。
在React
中,最常用的路由库是Router
。这个库使我们能够向React Web
应用程序添加路由功能。为了将路由引入我们的应用程序,让我们首先创建一个src/pages
目录并添加以下文件:
- /
src/pages/index.js
- /
src/pages/home.js
- /
src/pages/mynotes.js
- /
src/pages/favorites.js
我们的home.js
,mynotes.js
和favorite.js
文件将成为我们单独的页面组件。我们可以为每个文件创建一些初始内容和效果钩子,当用户导航到页面时,它们将更新文档标题。
在src/pages/home.js
中:
import React from 'react';
const Home = () => {
return (
Notedly
This is the home page
);
};
export default Home;
在src/pages/mynotes.js
中:
import React, { useEffect } from 'react';
const MyNotes = () => {
useEffect(() => {
// update the document title
document.title = 'My Notes — Notedly';
});
return (
Notedly
These are my notes
);
};
export default MyNotes;
在src/pages/favorites.js
中:
import React, { useEffect } from 'react';
const Favorites = () => {
useEffect(() => {
// update the document title
document.title = 'Favorites — Notedly';
});
return (
Notedly
These are my favorites
);
};
export default Favorites;
useEffect
在前面的示例中,我们使用React
的useEffect
钩子来设置页面标题。Effect
挂钩使我们在组件中存在副作用,会更新与组件本身无关的内容。如果你有兴趣,可以深入探讨React
的Effect hooks
文档。
现在,在src/pages/index.js
中,我们将使用react-router-dom
包导入React Router
和Web
浏览器路由所需的方法:
import React from 'react';
import { BrowserRouter as Router, Route } from 'react-router-dom';
接下来,我们将导入刚刚创建的页面组件:
import Home from './home';
import MyNotes from './mynotes';
import Favorites from './favorites';
最后,我们将使用特定的URL
指定我们创建为路由的每个页面组件。请注意,对于我们的“Home
”路由使用了完全匹配,这将确保仅针对根URL
呈现主页组件:
const Pages = () => {
return (
);
};
export default Pages;
现在,我们完整的src/pages/index.js
文件应如下所示:
// import React and routing dependencies
import React from 'react';
import { BrowserRouter as Router, Route } from 'react-router-dom';
// import routes
import Home from './home';
import MyNotes from './mynotes';
import Favorites from './favorites';
// define routes
const Pages = () => {
return (
);
};
export default Pages;
最后,我们可以通过导入路由并渲染组件来更新src/App.js
文件以使用我们的路由:
import React from 'react';
import ReactDOM from 'react-dom';
// import routes
import Pages from '/pages';
const App = () => {
return (
);
};
ReactDOM.render( , document.getElementById('root'));
现在,如果你在Web
浏览器中手动更新URL
,则应该能够查看每个组件。例如,键入http:// localhost:1234/favorites
来呈现“收藏夹”页面。
链接
我们已经创建了页面,但是缺少将它们链接在一起的关键部分。因此,让我们从首页添加到其他页面的链接。为此,我们将使用React Router
的Link
组件。
在src/pages/home.js
中:
import React from 'react';
// import the Link component from react-router
import { Link } from 'react-router-dom';
const Home = () => {
return (
Notedly
This is the home page
{ /* add a list of links */ }
-
My Notes
-
Favorites
);
};
export default Home;
这样我们就可以浏览我们的应用程序。
单击主页上的链接之一将导航到相应的页面组件。
浏览器的核心导航功能(如后退和前进按钮)也将继续起作用。
UI组件
我们已经成功创建了单个页面组件,并且可以在它们之间进行导航。在构建页面时,它们将具有几个共享的用户界面元素,例如标题和站点范围的导航。每次使用它们时重写它们都不会非常高效(并且会很烦人)。取而代之的是,我们可以编写可重用的接口组件,并将它们导入到我们需要的任何地方。实际上,将我们的UI
视为由微小的组件组成是React
的核心功能之一,也是我在掌握框架方面的突破。
我们将从为应用程序创建标题和导航组件开始。首先,让我们在src
目录中创建一个名为components
的新目录。在src/components
目录中,我们将创建两个名为Header.js
和Navigation.js
的新文件。React
组件必须大写,因此我们也将遵循大写文件名的惯例。
让我们首先在src/components/Header.js
中编写标头组件。为此,我们将导入logo.svg
文件,并为我们的组件添加相应的标记:
import React from 'react';
import logo from '../img/logo.svg';
const Header = () => {
return (
Notedly
);
};
export default Header;
对于我们的导航组件,我们将导入React Router
的Link
功能并标记一个无序的链接列表。在src/components/Navigation.js
中:
import React from 'react';
import { Link } from 'react-router-dom';
const Navigation = () => {
return (
);
};
export default Navigation;
在屏幕截图中,你会发现我还包括了表情符号字符作为导航图标。
如果你要这样做,包含表情符号字符的可访问标记如下:
完成标题和导航组件后,我们现在可以在应用程序中使用它们了。让我们更新src/pages/home.js
文件以包含这些组件。我们将首先导入它们,然后将组件包括在我们的JSX
标记中。
我们的src/pages/home.js
现在将如下所示(图12-2
):
import React from 'react';
import Header from '../components/Header';
import Navigation from '../components/Navigation';
const Home = () => {
return (
This is the home page
);
};
export default Home;
图12-2
使用React
组件,我们可以轻松地组合可共享的UI
功能。
这是我们能够在我们的应用程序中创建可共享组件所需的一切。有关在UI
中使用组件的更多信息,我强烈建议阅读React
文档页““Thinking in React”.
结论
网络仍然是分发应用程序的无比重要的媒介。它使开发者可以实时更新访问。
在本章中,我们在React
中构建了JavaScript Web
应用程序。在下一章中,我们将使用React
组件和CSS-in-JS
向应用程序添加布局和样式。
如果有理解不到位的地方,欢迎大家纠错。如果觉得还可以,麻烦您点赞收藏或者分享一下,希望可以帮到更多人。