反应路由器是反应的事实标准路由库。 当您需要导航通过阵营与多个视图的应用程序,你需要一个路由器进行管理的URL。 反应路由器需要照顾的是,使您的应用程序的UI和同步的网址。
本教程介绍了反应路由器V5和一大堆的东西,你可以用它做。
介绍
反应是如何生成客户端上呈现单页面应用程序(水疗)一个通俗图书馆。 一个SPA可能有多个视图 (又名页 ),而不像传统的多页的应用程序,通过这些视图导航不应导致整个页面被重新加载。 相反,我们要的意见,在当前页面内呈现内联。 最终用户,谁习惯于多页的应用程序,预计以下功能存在于一个SPA:
- 应用程序中的每个视图应该有一个唯一指定视图的URL。 这是为了让用户可以在稍后的时间书签,以供参考URL。 例如,
www.example.com/products
。 - 该浏览器的前进和后退按钮应该按预期工作。
- 动态生成的嵌套视图最好有自己的网址了。 例如,
example.com/products/shoes/101
,其中101是产品ID。
路由选择是保持同步的浏览器的URL与什么正在页面上呈现的过程。 反应路由器,您可以处理路由声明 。 该声明路由方法允许你控制你的应用程序中的数据流,通过说“的路线应该是这样的”:
<Route path="/about" component={About} />
你可以把你的<Route>
你希望你的路线被渲染组件的任何地方。 由于<Route>
, <Link>
和所有其他阵营路由器的API,我们会处理的只是组件,你可以很容易地使用在阵营路由。
之前的注意事项开始。 还有那个阵营路由器是Facebook开发的官方路由解决方案一种常见的误解。 在现实中,它是一个第三方库这是它的设计和简洁广为流行。 如果您的要求仅限于路由器的导航,你可以从头开始实现一个自定义的路由器没有太多的麻烦。 然而,理解的基础知识,如何应对路由器会给你更好的洞察路由器应该如何工作。
概观
本教程分为不同的部分。 首先,我们将建立反应,并使用NPM阵营路由器。 然后,我们将直接进入阵营路由器基础知识。 你会发现在行动做出反应路由器不同的代码演示。 包括在本教程中的例子包括:
- 基本的导航路由
- 嵌套路由
- 嵌套路由与路径参数
- 保护路由
以建设这些线路连接的所有概念将沿途进行讨论。 该项目的整个代码可以在此GitHub库 。 一旦你一个特定的demo目录里面,运行npm install
到安装依赖。 为了满足开发服务器,运行的应用程序npm start
和头部到http://localhost:3000/
在行动中看到的演示。
让我们开始吧!
设置路由器的反应
我假设你已经有了一个开发环境和运行。 如果不是,头部到“ 入门反应,JSX ”。 另外,您也可以使用创建应用程序做出反应 ,生成用于创建一个基本要求的文件作出反应的项目。 这是通过创建应用程序做出反应生成的默认目录结构:
react-router-demo
├── .gitignore
├── package.json
├── public
│ ├── favicon.ico
│ ├── index.html
│ └── manifest.json
├── README.md
├── src
│ ├── App.css
│ ├── App.js
│ ├── App.test.js
│ ├── index.css
│ ├── index.js
│ ├── logo.svg
│ └── registerServiceWorker.js
└── yarn.lock
该阵营路由器库包括三个包: react-router
, react-router-dom
和react-router-native
。 react-router
是核心包路由器,而其他两个是环境特定的。 你应该使用react-router-dom
,如果你建立一个网站,并react-router-native
,如果你使用阵营本地移动应用开发环境。
使用NPM安装react-router-dom
:
npm install --save react-router-dom
反应路由器基础知识
下面是我们的路线会如何看一个例子:
<Router>/* App component */
class App extends React.Component {
render() {
return (
<div>
<nav className="navbar navbar-light">
<ul className="nav navbar-nav">
/* Link components are used for linking to other views */
<li>
<Link to="/">Homes</Link>
</li>
<li>
<Link to="/category">Category</Link>
</li>
<li>
<Link to="/products">Products</Link>
</li>
</ul>
</nav>
/* Route components are rendered if the path prop matches the current URL*/
<Route path="/" component={Home} />
<Route path="/category" component={Category} />
<Route path="/products" component={Products} />
</div>
);
}
}
<Route exact path="/" component={Home} />
<Route path="/category" component={Category} />
<Route path="/login" component={Login} />
<Route path="/products" component={Products} />
</Router>
路由器
你需要一个路由器组件和若干线路零部件建立一个基本的路线上面所例举。 由于我们正在构建一个基于浏览器的应用程序,我们可以使用两种类型的路由器从阵营路由器API:
-
<BrowserRouter>
-
<HashRouter>
它们之间的主要区别是在网址显然,他们创造:
// <BrowserRouter>
http://example.com/about
// <HashRouter>
http://example.com/#/about
该<BrowserRouter>
是众多两个更受欢迎,因为它使用了HTML5 API历史,保持跟踪你的路由器的历史。 所述<HashRouter>
在另一方面,使用URL(的散列部window.location.hash
)记住的东西。 如果你打算支持旧版浏览器,你应该坚持<HashRouter>
包裹<BrowserRouter>
周围的应用组件的组成部分。
index.js
/* Import statements */
import React from "react";
import ReactDOM from "react-dom";
/* App is the entry point to the React code.*/
import App from "./App";
/* import BrowserRouter from 'react-router-dom' */
import { BrowserRouter } from "react-router-dom";
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById("root")
);
注意:路由器组件只能有一个子元素。 子元素可以是一个HTML元素-诸如DIV -或反应组件。
对于做出反应路由器的工作,你需要从导入相关的API react-router-dom
库。 在这里,我已经导入了BrowserRouter
到index.js
。 我还导入了App
的组件App.js
。 App.js
,因为你可能已经猜到,是的入口点反应的组分。
上面的代码创建历史对我们整个应用程序组件的一个实例。 让我正式向你介绍历史。
历史
history
是一个JavaScript库,让您轻松管理会话历史记录随时随地的JavaScript运行。history
提供了一个最小的API,让你管理历史堆栈,导航,确认导航和会话之间保持状态。 - 阵营培训文档
每个路由器组件创建跟踪当前位置(史对象history.location
)以及呈堆前面的位置。 在当前位置的变化,该视图重新渲染,你会得到导航感。 如何在当前位置的变化? 历史对象有方法,如history.push()
和history.replace()
采取的照顾。 history.push()
当你点击一个被调用<Link>
组件,和history.replace()
当您使用被称为<Redirect>
。 其他方法-如history.goBack()
和history.goForward()
-被要回用于通过历史堆栈导航或转发页面。
移动的,我们有链接和路线。
链接和路线
所述<Route>
组分是在阵营路由器的最重要的组成部分。 它使得一些UI如果当前位置的路线的路径相匹配。 理想情况下,一个<Route>
部件应该有一个支柱命名path
,并且如果路径与当前位置匹配,它就会被渲染。
所述<Link>
部件,在另一方面,用于在页面之间导航。 这是堪比HTML锚元素。 然而,使用锚链接会导致浏览器刷新,这是我们不想要的。 因此,相反,我们可以使用<Link>
导航到特定的URL,并有不刷新浏览器视图重新渲染。
我们已经介绍你需要知道创建一个基本的路由器的一切。 让我们建立一个。
演示1:基本路由
SRC / App.js
/* Import statements */
import React, { Component } from "react";
import { Link, Route, Switch } from "react-router-dom";
/* Home component */
const Home = () => (
<div>
<h2>Home</h2>
</div>
);
/* Category component */
const Category = () => (
<div>
<h2>Category</h2>
</div>
);
/* Products component */
const Products = () => (
<div>
<h2>Products</h2>
</div>
);
export default function App() {
return (
<div>
<nav className="navbar navbar-light">
<ul className="nav navbar-nav">
<li>
<Link to="/">Homes</Link>
</li>
<li>
<Link to="/category">Category</Link>
</li>
<li>
<Link to="/products">Products</Link>
</li>
</ul>
</nav>
/* Route components are rendered if the path prop matches the current URL */
<Route path="/" component={Home} />
<Route path="/category" component={Category} />
<Route path="/products" component={Products} />
</div>
);
}
我们已经宣布了主页,类别和产品内部的部件App.js
。 虽然这是正常的,现在,当组件开始变大,这是更好地为每个组件单独的文件。 作为一个经验法则,我通常如果占用超过10行代码创建的组件的新文件。 从第二个演示开始,我将创建已经长得太大,不适合内部组件的单独文件App.js
文件。
该应用程序组件里面,我们已经写逻辑路由。 所述<Route>
的路径与当前位置匹配,并且组件被渲染。 应呈现的成分在通过作为第二支柱。
这里/
同时匹配/
和/category
。 因此,无论是路由匹配和渲染。 我们如何避免这种情况? 你应该通过exact= {true}
道具与路由器path='/'
:
<Route exact={true} path="/" component={Home} />
如果你想有一个路由仅如果路径是完全一样的呈现,你应该用准确的道具。
嵌套路由
要创建嵌套的路线,我们需要有一个更好的了解如何<Route>
的作品。 让我们做到这一点。
<Route>
有三个道具,你可以用它来定义哪些获取呈现:
- 组件 。 我们已经在行动中看到了这一点。 当URL匹配时,路由器创建从使用给定成分的阵营元件
React.createElement
。 - 渲染 。 这是非常方便的在线渲染。 渲染道具预计,返回时位置的路线的路径相匹配的元素的功能。
- 儿童 。 孩子们的道具类似于呈现,它需要一个函数返回一个作出反应的元素。 然而,孩子被不管是否路径与位置或不匹配的渲染。
路径和匹配
的路径是用来识别该路由器应该匹配的URL的部分。 它使用Path对正则表达式库把一个路径字符串为正则表达式。 它随后将与当前位置匹配。
如果路由器的路径和位置匹配成功,将创建一个对象,我们把它叫做匹配对象。 匹配对象携带的URL和路径的详细信息。 这些信息是通过它的属性,下面列出的访问:
-
match.url
。 返回URL的匹配部分的字符串。 这对于建立嵌套特别有用<Link>
小号 -
match.path
。 一个字符串,返回该路由的路径串-即,<Route path="">
我们将使用它来构建嵌套的<Route>
秒。 -
match.isExact
。 的布尔值返回TRUE如果匹配是精确的(不带任何尾随字符)。 -
match.params
。 从URL含对象的键/值对解析由Path到正则表达式包。
现在我们知道所有关于<Route>
S,令我们构建嵌套路由的路由器。
开关元件
我们前往演示代码之前,我想向您介绍的<Switch>
的组成部分。 当多个<Route>
s的一起使用时,所有路由相匹配的被包含地呈现。 考虑这个代码演示1.我添加了一个新的途径来证明为什么<Switch>
是有用的:
<Route exact path="/" component={Home}/>
<Route path="/products" component={Products}/>
<Route path="/category" component={Category}/>
<Route path="/:id" render = {()=> (<p> I want this text to show up for all routes other than '/', '/products' and '/category' </p>)}/>
如果URL /products
相匹配的位置,在所有的路由/products
呈现。 因此, <Route>
与路径:id
获取与呈现沿Products
组件。 这是由设计。 然而,如果这不是你期望的行为,你应该添加<Switch>
组件添加到您的路线。 随着<Switch>
中,只有第一个孩子<Route>
的位置相匹配获取呈现。
演示2:嵌套路由
早前,我们创建一个路由/
, /category
和/products
。 如果我们想要的形式的URL /category/shoes
?
SRC / App.js
import React, { Component } from "react";
import { Link, Route, Switch } from "react-router-dom";
import Category from "./Category";
export default function App() {
return (
<div>
<nav className="navbar navbar-light">
<ul className="nav navbar-nav">
<li>
<Link to="/">Homes</Link>
</li>
<li>
<Link to="/category">Category</Link>
</li>
<li>
<Link to="/products">Products</Link>
</li>
</ul>
</nav>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/category" component={Category} />
<Route path="/products" component={Products} />
</Switch>
</div>
);
}
/* Code for Home and Products component omitted for brevity */
不像阵营路由器,在版本4和下面的,嵌套的早期版本<Route>
S的关系优选去父组件内。 也就是说,分类部件是父母在这里,我们会宣布路线category/:name
父组件内。
SRC / Category.jsx
import React from "react";
import { Link, Route } from "react-router-dom";
const Category = ({ match }) => {
return (
<div>
{" "}
<ul>
<li>
<Link to={`${match.url}/shoes`}>Shoes</Link>
</li>
<li>
<Link to={`${match.url}/boots`}>Boots</Link>
</li>
<li>
<Link to={`${match.url}/footwear`}>Footwear</Link>
</li>
</ul>
<Route
path={`${match.path}/:name`}
render={({ match }) => (
<div>
{" "}
<h3> {match.params.name} </h3>
</div>
)}
/>
</div>
);
};
export default Category;
首先,我们已经宣布了几个为嵌套的路线链接。 正如前面提到的, match.url
将用于建设嵌套链接和match.path
嵌套路线。 如果你无法理解比赛的概念, console.log(match)
提供了一些有用的信息,可能有助于澄清。
<Route
path={`${match.path}/:name`}
render={({ match }) => (
<div>
<h3> {match.params.name} </h3>
</div>
)}
/>
这是我们在动态路由的第一次尝试。 而不是硬编码的路线,我们使用了路径中的变量。 :name
是路径参数后捕捉一切category/
,直到遇到另一个斜杠。 因此,像一个路径products/running-shoes
将创建一个params
对象,如下所示:
{
name: "running-shoes";
}
捕获的数据应该是在访问match.params
或props.match.params
根据道具的传递方式。 另一个有趣的事情是,我们使用的render
道具。 render
道具是不要求自己的一个组件内联函数非常方便。
演示3:嵌套与路径路由参数
让我们复杂的东西多一点,好吗? 现实世界中的路由器将要处理的数据和动态显示。 假设我们有通过下面的表格的服务器API返回的产品数据。
SRC / Products.jsx
const productData = [
{
id: 1,
name: "NIKE Liteforce Blue Sneakers",
description:
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin molestie.",
status: "Available",
},
{
id: 2,
name: "Stylised Flip Flops and Slippers",
description:
"Mauris finibus, massa eu tempor volutpat, magna dolor euismod dolor.",
status: "Out of Stock",
},
{
id: 3,
name: "ADIDAS Adispree Running Shoes",
description:
"Maecenas condimentum porttitor auctor. Maecenas viverra fringilla felis, eu pretium.",
status: "Available",
},
{
id: 4,
name: "ADIDAS Mid Sneakers",
description:
"Ut hendrerit venenatis lacus, vel lacinia ipsum fermentum vel. Cras.",
status: "Out of Stock",
},
];
我们需要创建以下路径路线:
-
/products
。 这应该显示的产品列表。 -
/products/:productId
。 如果与一个产品:productId
存在,它应该显示的产品数据,并且如果没有,它应该显示一个错误消息。
SRC / Products.jsx
/* Import statements have been left out for code brevity */
const Products = ({ match }) => {
const productsData = [
{
id: 1,
name: "NIKE Liteforce Blue Sneakers",
description:
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin molestie.",
status: "Available",
},
//Rest of the data has been left out for code brevity
];
/* Create an array of `<li>` items for each product */
const linkList = productsData.map((product) => {
return (
<li>
<Link to={`${match.url}/${product.id}`}>{product.name}</Link>
</li>
);
});
return (
<div>
<div>
<div>
<h3> Products</h3>
<ul> {linkList} </ul>
</div>
</div>
<Route
path={`${match.url}/:productId`}
render={(props) => <Product data={productsData} {...props} />}
/>
<Route
exact
path={match.url}
render={() => <div>Please select a product.</div>}
/>
</div>
);
};
首先,我们创建的列表<Links>
■使用productsData.id
S和其存储在linkList
。 路线发生在其对应于产品ID的路径串的参数。
<Route
path={`${match.url}/:productId`}
render={(props) => <Product data={productsData} {...props} />}
/>
你可能预期component = { Product }
,而不是内联渲染功能。 问题是,我们需要传递下来productsData
与所有现有的道具一起到产品组件。 虽然还有其他方法可以做到这一点,我觉得这种方法是最简单的。 {...props}
使用ES6的传播语法通过整个道具反对组件。
下面是产品组件的代码。
SRC / Product.jsx
/* Import statements have been left out for code brevity */
const Product = ({ match, data }) => {
var product = data.find(p => p.id == match.params.productId);
var productData;
if (product)
productData = (
<div>
<h3> {product.name} </h3>
<p>{product.description}</p>
<hr />
<h4>{product.status}</h4>{" "}
</div>
);
else productData = <h2> Sorry. Product doesn't exist </h2>;
return (
<div>
<div>{productData}</div>
</div>
);
};
的find
方法是用来查询的阵列用于与等于一个ID属性的对象match.params.productId
。 如果产品存在, productData
显示。 如果没有,“产品不存在”的消息被渲染。
保护路由
对于最终的演示中,我们将讨论关注保护路由技术。 所以,如果有人试图访问/admin
,他们会被要求先进行登录。 然而,有一些事情我们需要盖之前,我们可以保护途径。
重定向
像服务器端重定向, <Redirect>
将与新的位置替换历史堆栈的当前位置。 新位置被指定to
支撑。 下面是我们如何将使用<Redirect>
:
<Redirect to={{pathname: '/login', state: {from: props.location}}}
因此,如果有人试图访问/admin
,同时注销时,他们会被重定向到/login
路线。 关于当前位置的信息通过州通过,因此,如果认证成功,用户可以重定向回到原来的位置。 子组件内部,你可以访问这个信息this.props.location.state
。
自定义路线
自定义路由是嵌套在一个组件内的路线看中了字。 如果我们需要做出决定的路径是否应呈现与否,编写定制的路线是要走的路。 这里的其他路线中声明的自定义路由。
SRC / App.js
/* Add the PrivateRoute component to the existing Routes */
<nav className="navbar navbar-light">
<ul className="nav navbar-nav">
...
<li><Link to="/admin">Admin area</Link></li>
</ul>
</nav>
<Switch>
<Route exact path="/" component={Home} data={data} />
<Route path="/category" component={Category} />
<Route path="/login" component={Login} />
<PrivateRoute path="/admin" component={Admin} />
<Route path="/products" component={Products} />
</Switch>
fakeAuth.isAuthenticated
如果用户登录,否则为false返回true。
下面是PrivateRoute的定义:
SRC / App.js
/* PrivateRoute component definition */
const PrivateRoute = ({ component: Component, ...rest }) => {
return (
<Route
{...rest}
render={props =>
fakeAuth.isAuthenticated === true ? (
<Component {...props} />
) : (
<Redirect
to={{ pathname: "/login", state: { from: props.location } }}
/>
)
}
/>
);
};
路线呈现管理部件,如果用户已登录,否则,用户被重定向到/login
。 这种方法的好处是,它显然更具声明和PrivateRoute
可重复使用。
最后,这里是为登录组件的代码:
SRC / Login.jsx
import React, { useState } from "react";
import { Redirect } from "react-router-dom";
export default function Login(props) {
const { from } = props.location.state || { from: { pathname: "/" } };
console.log(from);
const [redirectToReferrer, setRedirectToReferrer] = useState(false);
const login = () => {
fakeAuth.authenticate(() => {
setRedirectToReferrer(true);
});
};
if (redirectToReferrer) {
return <Redirect to={from} />;
}
return (
<div>
<p>You must log in to view the page at {from.pathname}</p>
<button onClick={login}>Log in</button>
</div>
);
}
/* A fake authentication function */
export const fakeAuth = {
isAuthenticated: false,
authenticate(cb) {
this.isAuthenticated = true;
setTimeout(cb, 100);
}
};
下面的行演示对象解构 ,这是ES6说明书的一部分:
const { from } = this.props.location.state || { from: { pathname: "/" } };
让我们的拼图组合在一起,好吗? 下面是我们使用阵营路由器内置的应用程序的最终演示。
演示4:保护路由
src="https://codesandbox.io/embed/react-router-demo-final-gobf6?autoresize=1&fontsize=14&theme=dark&view=preview" style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;" title="反应-路由器演示决赛" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"/>
摘要
正如你在本文中看到,阵营路由器是一个功能强大的库,补充反应建立更好的,声明的路线。 不同阵营路由器,在V5的先前版本,一切“只是组件”。 此外,新的设计模式,完美地融入了阵营的做事方式。
在本教程中,我们了解到:
- 如何设置和安装路由器作出反应
- 路由的基础知识,如一些基本组分
<Router>
,<Route>
和<Link>
- 如何创建用于导航和嵌套的路线最小的路由器
- 如何建立与路径参数动态路由
最后,我们学到了一些先进的路由技术用于创建保护路由最终演示。