使用 React Router 优化单页应用程序
React 通常用于构建单页应用 (SPA)。SPA 通常包含多个页面视图。当从一个页面视图导航到另一个页面视图时,重新加载整个页面视图是一项繁琐且效率低下的任务。事实上,这削弱了 SPA 的优势。为了正常工作,SPA 必须在需要时渲染部分视图,而不是重新加载整个页面。
在 SPA 应用中,从一个页面导航到另一个页面时,路由是必不可少的。路由可以分为两种:静态路由和动态路由。SPA 应用采用动态路由。在本教程中,我们将讨论 React 应用中使用的一种流行路由库,即React Router。
目录
- 要求
- 入门
- 使用 React Router 的第一个路由
- 什么是 BrowserRouter?
- 链接组件
- 使用 NavLink 进行活动链接
- 向路由添加参数
- 结论
要求
- NodeJS
v8.x.x
或更高版本以及 npm/yarn 一起安装 create-react-app
在本地开发机器上全局安装,生成新的 React 项目
奖励:您还可以使用它npx
来生成新的 React 项目,而无需安装create-react-app
。
入门
要创建新的 React 项目,请在本地计算机上的所需位置运行以下命令。
create-react-app react-router-v4-demo
项目生成后,遍历新创建的目录。此策略是生成新 React 应用的默认方法。
React Router作为一个库包含三个不同的 npm 包。
- 反应路由器
- react-router-dom
- React-router-native
每个包都有不同的用例。第一个react-router
是核心包,与上面列出的后两个包一起使用。react-router-dom
构建 Web 应用程序时必须使用。这也是我们将在本教程中使用的。最后一个react-router-native
通常用于React Native应用程序。
要在 React 应用中添加 React Router,请从终端窗口执行以下命令。
yarn add react-router-dom
请注意,在本教程的其余部分,我们将使用yarn
JavaScript 包管理器来添加或删除依赖项。如果您想使用npm
,也没人会阻止您。
要运行 React 应用,请转到终端窗口并执行命令 npm start。这将启动开发服务器。您将在 URL 上的 Web 浏览器窗口中看到默认的样板项目屏幕http://localhost:3000/
。
使用 React Router 的第一个路由
为了在 React 应用程序中创建第一条路由,让我们BrowserRouter
从react-router
库中导入。
import React from "react"
import { BrowserRouter as Router, Route } from "react-router-dom"
要创建路由,我们使用<Route>
from react-router-dom
。路由逻辑就放在此处。它渲染组件的 UI。A<Route>
有一个名为 的 prop path
,它始终与应用程序的当前位置匹配。基于此 prop,所需的组件将被渲染。当组件未渲染时,Route
返回 null。组件名称也作为 prop 传递component
。请看下面的代码片段。
function App() {
return (
<Router>
<Route path='/' component={Home} />
</Router>
)
}
有一个函数App
组件,它返回一个BrowserRouter
包含第一个Route
组件的 a 。 apath
当前指向Home
具有以下 UI 逻辑的组件。
function Home() {
return (
<div>
<h1>Home Component</h1>
</div>
)
}
现在,访问端口上的 URL 3000
,您将看到Home
正在渲染的组件。
这是一个最简单的示例。现在让我们添加另一个与 具有相同属性的路由。使用与 类似的渲染逻辑Home
调用此路由。About
Home
function About() {
return (
<div>
<h1>About Component</h1>
</div>
)
}
现在将这个功能组件添加为第二个路由,位于Home
路由下方。另外,将这两个路由添加到一个div
元素内。路由器组件只能容纳一个子元素,添加一个可以div
解决这个问题,并允许路由器组件拥有任意数量的子元素。
function App() {
return (
<Router>
<div>
<Route path='/' component={Home} />
<Route path='/about' component={About} />
</div>
</Router>
)
}
尝试访问该 URL http://localhost:3000/about
。您会注意到两个组件现在都在路径上渲染/about
。
造成这种情况的原因是,React Router 内部使用的正则表达式引擎会将以正斜杠开头的两个路由视为/
相等。为了解决这个问题,我们可以在 Home 路由上使用另一个名为 的重要 prop exact
。
<Router>
<div>
<Route path='/' exact component={Home} />
<Route path='/about' component={About} />
</div>
</Router>
这个 exact 也称为限定符,它规定路径必须与 完全匹配,/
并且后面没有任何内容,例如/about
。现在,如果您访问该 URL 的浏览器窗口,http://localhost:3000/about
您会注意到这次只有 about 组件被渲染了。
什么是 BrowserRouter?
你还记得之前读到过,它只react-router-dom
用于 Web 应用程序吗?其实,react-router-dom
这个库包含两种类型的路由器 API,供 React 应用程序使用。一种叫做BrowserRouter
,你在上一节中已经看到过它的工作原理。另一种叫做HashRouter
。
ABrowserRouter
总是监听像 这样的 URL ,http://localhost:3000/about
而 则HashRouter
会有http://localhost:3000/#/about
,顾名思义,它#
在 和 之间使用了哈希值。那么我们为什么要使用 呢BrowserRouter
?
BrowserRouter
在现代 Web 应用中非常流行。其主要原因是它使用 HTML5 History API 来跟踪 React 应用的路由历史记录。它HashRouter
有一个在旧版浏览器中的用例,在旧版浏览器中window.location.hash
仍然用于跟踪 SPA 中的路由。
DIY锻炼👇
这里有个小任务需要你完成。修改目录结构,如下图所示,并将两个功能组件Home
和about
组件文件分开,以便将来它们随着更多 JSX 的出现而需要渲染。
如果你愿意,完全可以跳过这一步,直接进入下一部分。但完成这项小任务将有助于你理解上述概念。
链接组件
要在 HTML 网页之间导航,可以使用<a href=""></a>
锚标记。然而,使用这种传统方法会导致浏览器刷新。为了解决这个问题,React Router API 提供了一个Link
组件,可用于导航到特定的 URL 或组件。
让我们尝试利用这些新知识创建一个导航菜单。从文件react-router-dom
中导入链接App.js
。以下是修改后的 App 组件代码片段。
// App.js
import React from "react"
import { BrowserRouter as Router, Route, Link } from "react-router-dom"
import Home from "./components/Home"
import About from "./components/About"
function App() {
return (
<Router>
<div>
<nav style={{ margin: 10 }}>
<Link to='/' style={{ padding: 10 }}>
Home
</Link>
<Link to='/about' style={{ padding: 10 }}>
About
</Link>
</nav>
<Route path='/' exact component={Home} />
<Route path='/about' component={About} />
</div>
</Router>
)
}
export default App
在上面的代码片段中,请注意所有链接都添加到所有路由组件之前。其中的样式属性style
目前是可选的。启动开发服务器并访问浏览器窗口,您会注意到顶部弹出一个导航菜单。尝试点击链接在不同组件之间导航。
无论在 React 项目中的何处Link
渲染,锚点<a>
都会在应用程序的 HTML 中渲染。
使用 NavLink 进行活动链接
在 React Router API 中,NavLink
是组件的扩展版本Link
。你可以说它是一种特殊类型的组件,Link
当匹配到当前路由时,它可以将自身样式设置为表示活动状态。
为了证明这一点,首先,让我们Link
用文件NavLink
中的所有标签替换它App.js
。
// App.js
import React from "react"
import { BrowserRouter as Router, Route, NavLink } from "react-router-dom"
import Home from "./components/Home"
import About from "./components/About"
function App() {
return (
<Router>
<div>
<nav style={{ margin: 10 }}>
<NavLink to='/' style={{ padding: 10 }}>
Home
</NavLink>
<NavLink to='/about' style={{ padding: 10 }}>
About
</NavLink>
</nav>
<Route path='/' exact component={Home} />
<Route path='/about' component={About} />
</div>
</Router>
)
}
export default App
此时,每个NavLink
链接的行为都与普通Link
组件相同,这意味着目前没有任何变化。要激活链接,请activeClassName
为其添加一个 prop。如下所示。
<NavLink to='/' style={{ padding: 10 }} activeClassName='active'>
为了设置相应的 CSS 使其工作,请打开App.css
文件并添加以下内容。
a {
padding: 10px;
}
a,
a:visited {
color: blue;
}
a.active {
color: red;
}
不要忘记在里面导入这个文件App.js
。另外,修改about
路由以使其具有activeClassName
。
import "./App.css"
// ...
return (
{/* */}
<nav style={{ margin: 10 }}>
<NavLink to='/' activeClassName='active'>
Home
</NavLink>
<NavLink to='/about' activeClassName='active'>
About
</NavLink>
</nav>
{/* */}
)
回到浏览器,打开如下所示的开发工具,您会注意到,首先,该Home
路线有一个类名active
。
尝试导航到该About
路线并观察会发生什么。
在导航到About
路由时,您是否注意到 active 类名也添加到了相应的路由中?但是,Home
即使 URL 与 匹配,路由仍然保留着 active 类名/about
。为什么?
其工作方式NavLink
与 React Router API 中的 Route 组件基本相似。为了确保只有一个路由具有 active 类状态,请尝试修改导航菜单中的 home 路由,如下所示。
// App.js
<NavLink to='/' exact activeClassName='active'>
Home
</NavLink>
这次您将获得所需的输出。
向路由添加参数
在本节中,您将学习如何基于查询参数(例如)创建和管理动态路由:id
。我们首先在文件中创建一个静态数组App.js
,作为模拟数据。
这个想法是为了演示一个路由,/posts
它显示来自数组的所有帖子。但是,数组中的每篇帖子都会有一个 id 或唯一标识符。使用该唯一标识符,您将通过编写 URL 的逻辑(例如,/posts/:id
where:id
将由帖子的特定 id 表示)来理解动态内容渲染的概念。
首先,让我们在名为 的新组件文件中添加一堆模拟帖子components/posts.js
。
// Posts.js
import React from "react"
import "../App.css"
class Posts extends React.Component {
state = {
posts: [
{ id: 1, title: "Hello Blog World!" },
{ id: 2, title: "My second post" },
{ id: 3, title: "What is React Router?" }
]
}
render() {
return (
<div className='posts'>
<h1>Posts List</h1>
</div>
)
}
}
export default Posts
为了简洁起见,在文件中添加了与上述对应的样式App.css
。
.posts ul {
list-style: none;
margin: 0;
margin-bottom: 20px;
padding: 0;
}
.posts ul li {
padding: 10px;
}
.posts a {
text-decoration: none;
}
现在,将新创建的组件导入到App.js
其他路由已经存在的地方。
//App.js
// ...
import Posts from "./components/Posts"
function App() {
return (
<Router>
<div>
<nav style={{ margin: 10 }}>
<NavLink to='/' exact activeClassName='active'>
Home
</NavLink>
<NavLink to='/about' activeClassName='active'>
About
</NavLink>
<NavLink to='/posts' activeClassName='active'>
Posts
</NavLink>
</nav>
<Route path='/' exact component={Home} />
<Route path='/about' component={About} />
<Route path='/posts' component={Posts} />
</div>
</Router>
)
}
export default App
现有的导航菜单有一条新路线,称为帖子。
打开Posts.js
以呈现帖子列表,并在 Web 浏览器中的当前位置匹配时将其显示为列表/posts
。
import React from "react"
import { Link, Route } from "react-router-dom"
import "../App.css"
function Child({ match }) {
return (
<div>
<h3>ID: {match.params.id}</h3>
</div>
)
}
class Posts extends React.Component {
state = {
posts: [
{
id: 1,
title: "Hello Blog World!"
},
{
id: 2,
title: "My second post"
},
{
id: 3,
title: "What is React Router?"
}
]
}
render() {
const { posts } = this.state
return (
<div className='posts'>
<h1>Posts List</h1>
<ul>
{posts.map(post => (
<li key={post.id}>
<Link to={`/posts/${post.id}`}>{post.title}</Link>
</li>
))}
</ul>
<Route path='/posts/:id' component={Child} />
</div>
)
}
}
export default Posts
此外,该Child
组件还会读取来自 URL 参数的任何内容,例如,在上面的例子中,读取id
每篇帖子的 。一个match
对象包含有关 如何<Route path>
匹配 URL 的信息,因此,在我们的例子中,就是每篇帖子的 ID。
结论
希望您现在已经熟悉了 React Router 库的基本工作原理。它是一个功能强大的库,可以帮助您构建更好的 React 应用。如果您想了解更多关于 React Router 的信息,请访问其官方文档。
我经常撰写 Nodejs、Reactjs 和 React Native 相关的代码。您可以访问我的amanhimself.dev页面,也可以订阅我的每周新闻邮件,这样就能直接在邮箱里收到我的所有教程和更新💌。
文章来源:https://dev.to/amanhimself/using-react-router-to-optimize-single-page-applications-4mim