If you have just started with React, you are probably still wrapping your head around the whole Single Page Application concept.
如果您刚开始使用React,那么您可能仍会围绕整个“单页面应用程序”概念进行思考。
Traditionally routing works like this: let's say you type in /contact
in the URL. The browser will make a GET request to the server, and the server will return an HTML page as the response.
传统上,路由的工作方式是这样的:假设您在URL中输入/contact
。 浏览器将向服务器发出GET请求,服务器将返回HTML页面作为响应。
But, with the new Single Page Application paradigm, all the URL requests are served using the client-side code.
但是,在新的“单页应用程序”范例中,所有URL请求都使用客户端代码来提供。
Applying this in the context of React, each page will be a React component. React-Router matches the URL and loads up the component for that particular page.
将其应用到React上下文中,每个页面将是一个React组件。 React-Router匹配URL并为该特定页面加载组件。
Everything happens so fast, and seamlessly, that the user gets a native app-like experience on the browser. There is no flashy blank page in between route transitions.
一切都如此快速,无缝地进行,以使用户在浏览器上获得类似于本机应用程序的体验。 路由转换之间没有浮华的空白页。
In this article, you'll learn how to use React-Router and its components to create a Single Page Application. So open up your favorite text editor, and let's get started.
在本文中,您将学习如何使用React-Router及其组件来创建单页应用程序。 因此,打开您喜欢的文本编辑器,让我们开始吧。
设置项目 (Setup the project)
Create a new React project by running the following command.
通过运行以下命令创建一个新的React项目。
yarn create react-app react-router-demo
I'll be using yarn to install the dependencies, but you can use npm as well.
我将使用yarn安装依赖项,但您也可以使用npm。
Next, let's install react-router-dom
.
接下来,让我们安装react-router-dom
。
yarn add react-router-dom
For styling the components, I'm going to use the Bulma CSS framework. So let's add that as well.
为了设计组件的样式,我将使用Bulma CSS框架。 因此,我们还要添加它。
yarn add bulma
Next, import bulma.min.css
in the index.js
file and clean up all the boilerplate code from the App.js
file.
接下来,将bulma.min.css
导入index.js
文件,并从App.js
文件中清除所有样板代码。
import "bulma/css/bulma.min.css";
Now that you have the project set up let's start by creating a few page components.
现在您已经设置了项目,让我们开始创建一些页面组件。
创建页面组件 (Creating the Page Components)
Create a pages directory inside the src folder where we will park all the page components.
在src文件夹中创建一个页面目录,我们将在其中存放所有页面组件。
For this demo, create three pages - Home, About, and Profile.
对于此演示,请创建三个页面-主页,关于和个人资料。
Paste the following inside the Home and About components.
将以下内容粘贴到Home和About组件中。
// pages/Home.js
import React from "react";
const Home = () => (
<div>
<h1 className="title is-1">This is the Home Page</h1>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras gravida,
risus at dapibus aliquet, elit quam scelerisque tortor, nec accumsan eros
nulla interdum justo. Pellentesque dignissim, sapien et congue rutrum,
lorem tortor dapibus turpis, sit amet vestibulum eros mi et odio.
</p>
</div>
);
export default Home;
// pages/About.js
import React from "react";
const About = () => (
<div>
<h1 className="title is-1">This is the About Page</h1>
<p>
Class aptent taciti sociosqu ad litora torquent per conubia nostra, per
inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus
et ultrices posuere cubilia curae; Duis consequat nulla ac ex consequat,
in efficitur arcu congue. Nam fermentum commodo egestas.
</p>
</div>
);
export default About;
We will create the Profile page later on in the article.
我们将在本文后面创建“个人资料”页面。
创建导航栏组件 (Create the Navbar Component)
Let's start by creating the navigation bar for our app. This component will make use of the <NavLink />
component from react-router-dom
.Create a directory called "components" inside the src folder.
首先,为我们的应用程序创建导航栏。 该组件将使用react-router-dom
中的<NavLink />
组件。在src文件夹中创建一个名为“ components”的目录。
// components/Navbar.js
import React, { useState } from "react";
import { NavLink } from "react-router-dom";
const Navbar = () => {
const [isOpen, setOpen] = useState(false);
return (
<nav
className="navbar is-primary"
role="navigation"
aria-label="main navigation"
>
<div className="container">
{/* ... */}
</div>
</nav>
);
};
export default Navbar;
The isOpen
state variable will be used to trigger the menu on mobile or tablet devices.
isOpen
状态变量将用于触发移动或平板设备上的菜单。
So let's add the hamburger menu.
因此,让我们添加汉堡菜单。
const Navbar = () => {
const [isOpen, setOpen] = useState(false);
return (
<nav
className="navbar is-primary"
role="navigation"
aria-label="main navigation"
>
<div className="container">
<div className="navbar-brand">
<a
role="button"
className={`navbar-burger burger ${isOpen && "is-active"}`}
aria-label="menu"
aria-expanded="false"
onClick={() => setOpen(!isOpen)}
>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
</div>
{/* ... */}
</div>
</nav>
);
};
To add the link in the menu, use the <NavLink />
component by react-router-dom
.
要在菜单中添加链接,请使用react-router-dom
的<NavLink />
组件。
The NavLink
component provides a declarative way to navigate around the application. It is similar to the Link
component, except it can apply an active style to the link if it is active.
NavLink
组件提供了一种声明性的方式来浏览应用程序。 它与“ Link
组件相似,不同之处在于它可以在链接处于活动状态时将活动样式应用于链接。
To specify which route to navigate to, use the to
prop and pass the path name. The activeClassName
prop will add an active class to the link if it's currently active.
要指定导航到的路线,请使用to
支持并传递路径名。 如果activeClassName
当前处于活动状态,则该属性将在链接中添加一个活动类。
<NavLink
className="navbar-item"
activeClassName="is-active"
to="/"
exact
>
Home
</NavLink>
On the browser, the NavLink
component is rendered as an <a>
tag with an href
attribute value that was passed in the to
prop.
在浏览器上,所述NavLink
分量呈现为<a>
与标签href
已传递中的属性值to
支撑。
Also, here you need to specify the exact
prop so that it is matched precisely with the URL.
另外,在这里您需要指定exact
prop,以使其与URL exact
匹配。
Add all the links and finish up the Navbar
component.
添加所有链接并完成Navbar
组件。
import React, { useState } from "react";
import { NavLink } from "react-router-dom";
const Navbar = () => {
const [isOpen, setOpen] = useState(false);
return (
<nav
className="navbar is-primary"
role="navigation"
aria-label="main navigation"
>
<div className="container">
<div className="navbar-brand">
<a
role="button"
className={`navbar-burger burger ${isOpen && "is-active"}`}
aria-label="menu"
aria-expanded="false"
onClick={() => setOpen(!isOpen)}
>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
</div>
<div className={`navbar-menu ${isOpen && "is-active"}`}>
<div className="navbar-start">
<NavLink className="navbar-item" activeClassName="is-active" to="/">
Home
</NavLink>
<NavLink
className="navbar-item"
activeClassName="is-active"
to="/about"
>
About
</NavLink>
<NavLink
className="navbar-item"
activeClassName="is-active"
to="/profile"
>
Profile
</NavLink>
</div>
<div className="navbar-end">
<div className="navbar-item">
<div className="buttons">
<a className="button is-white">Log in</a>
</div>
</div>
</div>
</div>
</div>
</nav>
);
};
export default Navbar;
If you notice here, I've added a Login button. We will come back to the Navbar
component again when we discuss protected routes later on in the guide.
如果您在这里注意到,我已经添加了一个登录按钮。 当我们稍后在指南中讨论受保护的路线时,我们将再次回到Navbar
组件。
渲染页面 (Rendering the pages)
Now that the Navbar
component is set up let's add that to the page and start with rendering the pages.
现在已经设置了Navbar
组件,让我们将其添加到页面并开始渲染页面。
Since the navigation bar is a common component across all the pages, instead of calling the component in each page component, it will be a better approach to render the Navbar
in a common layout.
由于导航栏是所有页面上的通用组件,而不是在每个页面组件中调用该组件,因此这是一种在通用布局中呈现Navbar
的更好方法。
// App.js
function App() {
return (
<>
<Navbar />
<div className="container mt-2" style={{ marginTop: 40 }}>
{/* Render the page here */}
</div>
</>
);
}
Now, add the page components inside of the container.
现在,将页面组件添加到容器内。
// App.js
function App() {
return (
<>
<Navbar />
<div className="container mt-2" style={{ marginTop: 40 }}>
<Home />
<About />
</div>
</>
);
}
If you check out the results now, you'll notice that both the Home and the About page component gets rendered onto the page. That's because we haven't added any routing logic yet.
如果现在检查结果,您会注意到“主页”和“关于”页面组件都已呈现到页面上。 那是因为我们还没有添加任何路由逻辑。
You need to import the BrowserRouter
component from React Router to add the ability to route the components. All you need to do is wrap all the page components inside of the BrowserRouter
component. This will enable all the page components to have the routing logic. Perfect!
您需要从React Router导入BrowserRouter
组件以添加路由组件的功能。 您需要做的就是将所有页面组件包装在BrowserRouter
组件内。 这将使所有页面组件具有路由逻辑。 完善!
But again, nothing's going to change with the results – you are still going to see both the pages rendered. You need to render the page component only if the URL matches a particular path. That's where the Route
component from React Router comes into play.
但是同样,结果不会改变–您仍然会看到两个页面都已呈现。 仅当URL匹配特定路径时,才需要呈现页面组件。 这就是来自React Router的Route
组件发挥作用的地方。
The Router
component has a path
prop that accepts the page's path, and the page component should be wrapped with the Router
, as shown below.
Router
组件具有一个接受页面路径的path
属性,并且页面组件应使用Router
包裹,如下所示。
<Route path="/about">
<About />
</Route>
So let's do the same for the Home
component.
因此,让我们对Home
组件执行相同的操作。
<Route exact path="/">
<Home />
</Route>
The exact
prop above tells the Router
component to match the path exactly. If you don't add the exact
prop on the /
path, it will match with all the routes starting with a /
including /about
.
上面的exact
道具告诉Router
组件完全匹配路径。 如果您没有在/
路径上添加exact
prop,它将与所有以/
开头/
包括/about
的路线匹配。
If you go check out the results now, you'll still see both the components rendered. But, if you go to /about
, you'll notice that only the About
component gets rendered. You see this behavior because the router keeps matching the URL with the routes even after it had matched a route already.
如果您现在检查结果,您仍然会看到两个已渲染的组件。 但是,如果转到/about
,则会注意到只有About
组件被渲染。 您会看到此现象,因为即使路由器已经匹配了路由,它仍将URL与路由匹配。
We need to tell the router to stop matching further once it matches a route. This is done using the Switch
component from React Router.
一旦路由匹配,我们需要告诉路由器停止进一步匹配。 这是使用React Router的Switch
组件完成的。
function App() {
return (
<BrowserRouter>
<Navbar />
<div className="container mt-2" style={{ marginTop: 40 }}>
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route path="/about">
<About />
</Route>
</Switch>
</div>
</BrowserRouter>
);
}
There you go! You have successfully configured routing in your React app.
你去! 您已经在React应用程序中成功配置了路由。
受保护的路线和重定向 (Protected Routes and Redirect)
When working on Real-world applications, you will have some routes behind an authentication system. You are going to have routes or pages that can only be accessed by a logged-in user. In this section, you'll learn how to go about implementing such routes.
在实际应用程序上工作时,身份验证系统后面会有一些路由。 您将拥有只能由登录用户访问的路由或页面。 在本节中,您将学习如何实现这些路线。
Please note that I'm not going to create any login form or have any back-end service authenticate the user. In a real application, you wouldn't implement authentication the way demonstrated here.
请注意 ,我不会创建任何登录表单或让任何后端服务对用户进行身份验证。 在实际的应用程序中,您将不会像此处演示的那样实现身份验证。
Let's create the Profile page component that should only be accessed by the authenticated user.
让我们创建“配置文件”页面组件,该组件只能由经过身份验证的用户访问。
// pages/Profile.js
import { useParams } from "react-router-dom";
const Profile = () => {
const { name } = useParams();
return (
<div>
<h1 className="title is-1">This is the Profile Page</h1>
<article className="message is-dark" style={{ marginTop: 40 }}>
<div className="message-header">
<p>{name}</p>
</div>
<div className="message-body">
Lorem ipsum dolor sit amet, consectetur adipiscing elit.{" "}
<strong>Pellentesque risus mi</strong>, tempus quis placerat ut, porta
nec nulla. Vestibulum rhoncus ac ex sit amet fringilla. Nullam gravida
purus diam, et dictum <a>felis venenatis</a> efficitur. Aenean ac{" "}
<em>eleifend lacus</em>, in mollis lectus. Donec sodales, arcu et
sollicitudin porttitor, tortor urna tempor ligula, id porttitor mi
magna a neque. Donec dui urna, vehicula et sem eget, facilisis sodales
sem.
</div>
</article>
</div>
);
};
We will grab the user's name from the URL using route parameters.
我们将使用路由参数从URL中获取用户名。
Add the Profile route into the router.
将配置文件路由添加到路由器。
<Route path="/profile/:name">
<Profile />
</Route>
Currently the profile page can be accessed directly. So to make it an authenticated route, create a Higher-Order component (HOC) to wrap the authentication logic.
当前可以直接访问个人资料页面。 因此,要使其成为经过身份验证的路由,请创建一个高阶组件(HOC)以包装身份验证逻辑。
const withAuth = (Component) => {
const AuthRoute = () => {
const isAuth = !!localStorage.getItem("token");
// ...
};
return AuthRoute;
};
To determine if a user is authenticated or not, grab the authentication token that is stored in the browser when the user logs in. If the user is not authenticated, redirect the user to the Home page. The Redirect
component from React Router can be used to redirect the user to another path.
要确定用户是否已通过身份验证,请获取用户登录时存储在浏览器中的身份验证令牌。如果用户未通过身份验证,则将用户重定向到主页。 来自React Router的Redirect
组件可以用来将用户重定向到另一个路径。
const withAuth = (Component) => {
const AuthRoute = () => {
const isAuth = !!localStorage.getItem("token");
if (isAuth) {
return <Component />;
} else {
return <Redirect to="/" />;
}
};
return AuthRoute;
};
You can also pass in other user information like name and user ID using props to the wrapped component.
您还可以使用道具将其他用户信息(例如名称和用户ID)传递给包装的组件。
Next, use the withAuth
HOC in the Profile component.
接下来,在Profile组件中使用withAuth
HOC。
import withAuth from "../components/withAuth";
const Profile = () => {
// ...
}
export default withAuth(Profile);
Now, if you try to visit /profile/JohnDoe
, you will get redirected to the Home page. That's because the authentication token is not yet set in your browser storage.
现在,如果您尝试访问/profile/JohnDoe
,您将被重定向到主页。 这是因为您的浏览器存储中尚未设置身份验证令牌。
Alright, so, let's return to the Navbar
component and add the login and logout functionalities. When the user is authenticated, show the Logout button and when the user is not logged in show the Login button.
好了,让我们回到Navbar
组件并添加登录和注销功能。 用户通过身份验证后,显示“注销”按钮,而在用户未登录时显示“登录”按钮。
// components/Navbar.js
const Navbar = () => {
// ...
return (
<nav
className="navbar is-primary"
role="navigation"
aria-label="main navigation"
>
<div className="container">
{/* ... */}
<div className="navbar-end">
<div className="navbar-item">
<div className="buttons">
{!isAuth ? (
<button className="button is-white" onClick={loginUser}>
Log in
</button>
) : (
<button className="button is-black" onClick={logoutUser}>
Log out
</button>
)}
</div>
</div>
</div>
</div>
</nav>
);
}
When the user clicks on the login button, set a dummy token in the local storage, and redirect the user to the profile page.
当用户单击登录按钮时,请在本地存储中设置一个虚拟令牌,然后将用户重定向到配置文件页面。
But we cannot use the Redirect component in this case – we need to redirect the user programmatically. Sensitive tokens used for authentication are usually stored in cookies for security reasons.
但是在这种情况下,我们不能使用重定向组件–我们需要以编程方式重定向用户。 出于安全原因,用于身份验证的敏感令牌通常存储在cookie中。
React Router has a withRouter
HOC that injects the history
object in the props of the component to leverage the History API. It also passes the updated match
and location
props to the wrapped component.
React Router有一个withRouter
HOC,它将history
对象注入组件的props中,以利用History API。 还将更新的match
和location
道具传递给包装的组件。
// components/Navbar.js
import { NavLink, withRouter } from "react-router-dom";
const Navbar = ({ history }) => {
const isAuth = !!localStorage.getItem("token");
const loginUser = () => {
localStorage.setItem("token", "some-login-token");
history.push("/profile/Vijit");
};
const logoutUser = () => {
localStorage.removeItem("token");
history.push("/");
};
return (
{/* ... */}
);
};
export default withRouter(Navbar);
And voilà! That's it. You have conquered the land of authenticated routes as well.
和瞧 ! 而已。 您还征服了经过身份验证的路线的土地。
Check out the live demo here and the complete code in this repo for your reference.
结论 (Conclusion)
I hope by now you have a fair idea of how client-side routing works in general and how to implement routing in React using the React Router library.
我希望到目前为止,您对客户端路由一般如何工作以及如何使用React Router库在React中实现路由有了一个清晰的了解。
In this guide, you learned about the vital components in React Router like Route
, withRouter
, Link
, and so on, along with some advanced concepts like authenticated routes, that are required to build an application.
在本指南中,您了解了React Router中至关重要的组件,例如Route
, withRouter
, Link
等,以及构建应用程序所需的一些高级概念(例如,经过身份验证的路由)。
Do check out the React Router docs to get a more detailed overview of each of the components.
请查看React Router 文档以获取每个组件的更详细概述。
翻译自: https://www.freecodecamp.org/news/react-router-tutorial/