React Router v5:完整指南
反应的路由器是React事实上的标准路由库。当你需要在一个有多个视图的React应用程序中导航时,你需要一个路由器来管理url。React Router负责这个,让你的应用UI和URL保持同步。
本教程向你介绍React Router v5,以及你可以用它做的很多事情。
简介
React是用于创建在客户端呈现的单页应用程序(spa)的流行库。一个SPA可能有多个的观点(又名页面),而且与传统的多页面应用不同,在这些视图中导航不会导致整个页面被重新加载。相反,我们希望视图内联呈现在当前页面中。习惯了多页面应用程序的最终用户希望在SPA中出现以下功能:
- 每个视图都应该有一个唯一指定该视图的URL。这样用户就可以将URL收藏起来,以便以后参考。例如,
www.example.com/products
.李><李class="">浏览器的后退和前进按钮应该可以正常工作。李><李class="">动态生成的嵌套视图最好也有自己的URL,例如example.com/products/shoes/101
,其中101是产品ID。李>
路由是保持浏览器URL与页面上呈现的内容同步的过程。React路由器让你处理路由以声明的方式.声明式路由方法允许你控制应用程序中的数据流,通过说“路由应该是这样的”:
<路线路径=“/”><关于/></路线>
你可以把你的<路径>
组件在任何你想渲染你的路由的地方。自<路径>
,<链接>
我们将要处理的所有其他React路由器api都只是组件,你可以轻松地在React中启动和运行路由。
不是来自Facebook
概述
本教程分为不同的部分。首先,我们将使用npm设置React和React路由器。然后我们将直接进入一些React路由器基础知识。你会发现React路由器的不同代码演示。本教程中涉及的示例包括:
- 基本导航路由李><李class="">嵌套的路由李><李class="">带有路径参数的嵌套路由李><李class="">保护路由李>
所有与建设这些路线有关的概念都将在途中进行讨论。
项目的全部代码可在这个GitHub回购.
让我们开始吧!
设置React路由器
要学习本教程,您需要在您的PC上安装Node的最新版本。如果不是这样,那么转到Node主页并为您的系统下载正确的二进制文件.或者,您可以考虑使用版本管理器来安装Node。我们有一个这里是使用版本管理器的教程.
Node与npm捆绑在一起,npm是JavaScript的包管理器,我们将用它安装一些我们将使用的库。你可以点击这里了解更多关于使用NPM的知识.
您可以通过从命令行发出以下命令来检查两者是否正确安装:
节点- v>12.19.0npm- v>6.14.8
完成这些后,让我们开始创建一个新的React项目创建React应用工具。您可以全局安装它,也可以使用它npx
,像这样:
create-react-app react-router-demo
完成后,切换到新创建的目录:
cdreact-router-demo
React Router库包含三个包:react-router,react-router-dom,react-router-native.路由器的核心包是react-router
,而其他两个则是特定于环境的。你应该使用react-router-dom
如果你要建一个网站react-router-native
如果你在一个使用React Native的移动应用程序开发环境中。
使用npm进行安装react-router-dom
:
npm安装react-router-dom
然后启动开发服务器:
npm运行开始
恭喜你!你现在有一个工作的React应用程序,安装了React路由器。您可以查看运行的应用程序http://localhost:3000/.
React路由器基础知识
现在让我们熟悉一个基本的React路由器设置。为此,我们将创建一个具有三个独立视图的应用程序:Home、Category和Products。
的路由器
组件
我们要做的第一件事是把我们的<应用>
组件<路由器>
组件(由React Router提供)。由于我们正在构建一个基于浏览器的应用程序,我们可以使用React router API中的两种类型的路由器:
它们之间的主要区别是它们创建的url:
/ / < BrowserRouter >http://例子.com/关于/ / < HashRouter >http://例子.com/#/关于
的< BrowserRouter >
这两者中比较受欢迎是因为它使用了theHTML5历史API以保持UI与URL同步,而< HashRouter >
使用URL的哈希部分(window.location.hash
).如果您需要支持不支持History API的旧浏览器,则应该使用< HashRouter >
.否则< BrowserRouter >
对于大多数用例来说是更好的选择。你可以点击这里阅读更多不同之处.
让我们导入BrowserRouter
组件,并将其围绕应用程序
组件:
/ / src / index.js进口反应从“反应”;进口ReactDOM从“react-dom”;进口应用程序从”。/应用程序”;进口{BrowserRouter}从“react-router-dom”;ReactDOM.渲染(<BrowserRouter><应用程序/></BrowserRouter>,文档.getElementById(“根”));
上面的代码创建一个历史
整个实例<应用>
组件。让我们看看这意味着什么。
《一点点历史
的
历史
库可以让你轻松地管理任何JavaScript运行的会话历史。一个历史
对象抽象了各种环境中的差异,并提供了一个最小的API,让您可以管理历史堆栈、导航和在会话之间保持状态。- - - - - -React培训文档
每一个<路由器>
组件创建历史
对象,该对象跟踪当前位置(history.location
)和堆栈中先前的位置。当当前位置发生变化时,视图将被重新渲染,您将获得一种导航感。当前位置如何变化?历史对象具有history.push
而且history.replace
来解决这个问题。的history.push
方法时调用<链接>
组件,history.replace
调用< >重定向
.其他方法——例如history.goBack
而且history.goForward
-用于通过向后或向前浏览历史堆栈。
继续,我们有链接和路线。
链接
而且路线
组件
的<路径>
组件是React路由器中最重要的组件。如果当前位置与路由路径匹配,它会呈现一些UI。理想情况下,一个<路径>
组件应该有一个名为路径
,如果路径名与当前位置匹配,则会呈现该路径。
的<链接>
另一方面,组件用于在页面之间导航。它可以与HTML锚元素相比较。然而,使用锚链接会导致整个页面刷新,这是我们不希望看到的。所以我们可以用<链接>
导航到一个特定的URL,并重新渲染视图而不刷新。
现在我们已经涵盖了使我们的应用程序工作所需的所有内容。更新src / App.js
如下:
进口反应从“反应”;进口{链接,路线,开关}从“react-router-dom”;常量首页=()= >(<div><h2>首页</h2></div>);常量类别=()= >(<div><h2>类别</h2></div>);常量产品=()= >(<div><h2>产品</h2></div>);出口默认的函数应用程序(){返回(<div><导航名称=“navbar navbar-light”><ul类名=“nav navbar-nav”><李><链接来=“/”>首页</链接></李><李><链接来=“/类别”>类别</链接></李><李><链接来=“/产品”>产品</链接></李></ul></导航>{/*如果路径道具与当前URL */匹配,则呈现路由组件}<路线路径=“/”><首页/></路线><路线路径=“/类别”><类别/></路线><路线路径=“/产品”><产品/></路线></div>);}
这里,我们声明了for的组件首页
,类别
而且产品
内部App.js
.虽然现在这样做还可以,但是当一个组件开始变大时,最好为每个组件创建一个单独的文件。根据经验,如果组件占用的代码超过10行,我通常会为它创建一个新文件。从第二个演示开始,我将为组件创建一个单独的文件,这些组件由于太大而无法放入App.js
文件。
在应用程序
组件,我们已经编写了路由的逻辑。的<路径>
的路径与当前位置匹配,并呈现一个组件。以前,应该呈现的组件是作为第二个道具传入的。然而,React Router的最新版本引入了一种新的路由呈现模式,即要呈现的组件是<路径>
.
在这里/
匹配两个/
而且/类别
.因此,两条路由都被匹配和呈现。我们如何避免这种情况?你应该通过确切的
道具到<路径>
与路径= ' / '
:
<路线具体的路径=“/”><首页/></路线>
如果您希望仅在路径完全相同时才呈现路由,则应该使用确切的道具.
嵌套的路由
要创建嵌套路由,我们需要更好地理解如何创建<路径>
的工作原理。我们现在来看一下。
正如你可以读到的React路由器文档的推荐方法<路径>
就是使用孩子们
元素,如上所示。但是,还有一些其他方法可以使用<路径>
.这些主要用于支持在钩子引入之前用路由器的早期版本构建的应用程序:
组件
:当URL匹配时,路由器使用React.createElement
.李><李class="">渲染
:方便内联渲染。的渲染
Prop期望一个函数在位置与路由路径匹配时返回一个元素。李><李class="">孩子们
:这与渲染
,因为它期望一个返回React组件的函数。然而,孩子们
无论路径是否与位置匹配,都将呈现。李>
路径和匹配
的路径
prop用于识别路由器应该匹配的URL部分。它使用Path-to-RegExp图书馆将路径字符串转换为正则表达式。然后它将与当前位置进行匹配。
如果路由器的路径和位置成功匹配,则会创建一个名为a的对象匹配对象.的匹配
对象包含有关URL和路径的更多信息。该信息可通过下面列出的属性访问:
match.url
:返回URL匹配部分的字符串。这对于构建嵌套尤其有用<链接>
组件。李><李class="">match.path
:返回路由路径字符串的字符串,即,<路由路径= " " >
.我们会用这个来构建嵌套<路径>
组件。李><李class="">match.isExact
:一个布尔值,如果匹配是精确的(没有任何尾随字符)则返回true。李><李class="">match.params
:包含由Path-to-RegExp包解析的URL中的键/值对的对象。李>
道具的隐式传递
注意,当使用组件
道具以呈现路线,则匹配
,位置
而且历史
路由道具隐式地传递给组件。当使用较新的路由呈现模式时,情况并非如此。
例如,以这个组件为例:
常量首页=(道具)= >{控制台.日志(道具);返回(<div><h2>首页</h2></div>);};
现在像这样渲染路由:
<路线具体的路径=“/”组件={首页}/>
这将记录以下内容:
{历史:{...}位置:{...}匹配:{...}}
但是现在要像这样渲染路由:
<路线具体的路径=“/”><首页/></路线>
这将记录以下内容:
{}
这可能一开始看起来很不利,但不用担心!React v5.1引入了几个钩子来帮助你在需要的地方访问你需要的东西。这些钩子为我们提供了管理路由器状态的新方法,并在一定程度上整理了我们的组件。
我将在本教程中使用这些钩子中的一些,但如果您想更深入地了解,请查看React路由器v5.1发布公告.请注意,钩子是在React的16.8版本中引入的,所以你至少需要在那个版本上才能使用它们。
的开关
组件
在开始演示代码之前,我想向您介绍开关组件.当多个<路径>
S被一起使用,所有匹配的路由都被包含呈现。考虑演示1中的代码。我添加了一条新路线来说明原因<转>
是有用的:
<路线具体的路径=“/”><首页/></路线><路线路径=“/类别”><类别/></路线><路线路径=“/产品”><产品/></路线><路线路径=/: id”><p>这文本将被渲染为其他路线' / '</p></路线>
如果URL为/产品
,所有与该位置匹配的路线/产品
被渲染。所以,<路径>
与路径/: id
属性一起呈现<产品>
组件。这是有意为之。但是,如果这不是您所期望的行为,则应该添加<转>
组件的路由。与<转>
,只有第一个孩子<路径>
匹配的位置被渲染:
<开关><路线具体的路径=“/”><首页/></路线><路线路径=“/类别”><类别/></路线><路线路径=“/产品”><产品/></路线><路线路径=/: id”><p>这文本将被渲染为除上述定义的路由之外的任何路由</p></路线></开关>
的: id
的一部分路径
用于动态路由。它将匹配斜杠之后的任何内容,并使该值在组件中可用。在下一节中,我们将看到一个实例。
既然我们都知道了<路径>
而且<转>
组件,让我们在演示中添加嵌套路由。
动态嵌套路由
之前,我们为/
,/类别
而且/产品
.但是如果我们想要一个形式为/类别/鞋
?
让我们从更新开始src / App.js
如下:
进口反应从“反应”;进口{链接,路线,开关}从“react-router-dom”;进口类别从”。/类别”;常量首页=()= >(<div><h2>首页</h2></div>);常量产品=()= >(<div><h2>产品</h2></div>);出口默认的函数应用程序(){返回(<div><导航名称=“navbar navbar-light”><ul类名=“nav navbar-nav”><李><链接来=“/”>首页</链接></李><李><链接来=“/类别”>类别</链接></李><李><链接来=“/产品”>产品</链接></李></ul></导航><开关><路线路径=“/”><首页/></路线><路线路径=“/类别”><类别/></路线><路线路径=“/产品”><产品/></路线></开关></div>);}
你会注意到我们移动了类别
变成它自己的组件。这是我们的嵌套路由应该去的地方。
让我们创建Category.js
现在:
/ / src / Category.js进口反应从“反应”;进口{链接,路线,useParams,useRouteMatch}从“react-router-dom”;常量项=()= >{常量{的名字}=useParams();返回(<div><h3>{的名字}</h3></div>);}常量类别=()= >{常量{url,路径}=useRouteMatch();返回(<div><ul><李><链接来={`$ {url}/鞋`}>鞋子</链接></李><李><链接来={`$ {url}/靴子`}>靴子</链接></李><李><链接来={`$ {url}/鞋类`}>鞋子</链接></李></ul><路线路径={`$ {路径}/:名称`}><项/></路线></div>);};出口默认的类别;
这里,我们用useRouteMatch钩获得访问权限匹配
对象。如前所述,match.url
将用于构建嵌套链接和match.path
对于嵌套路由。如果你在理解匹配的概念上有困难,console.log (useRouteMatch ())
提供一些有用的信息,可能有助于澄清它。
<路线路径={`$ {路径}/:名称`}><项/></路线>
这是我们对动态路由的第一次正确尝试。中使用了一个变量,而不是硬编码路由路径
道具。:名称
是一个路径参数和捕获一切之后类别/
直到遇到另一个正斜杠。路径名是产品/跑鞋
将创建一个参数个数
对象如下:
{的名字:“跑步鞋”;}
方法中访问此值<项目>
组件,我们使用useParams钩,它返回一个URL参数的键/值对对象。
在浏览器中试试这个。Category部分现在应该有三个子部分,每个子部分都有自己的路由。
带有路径参数的嵌套路由
让我们把事情复杂化一点,好吗?现实世界中的路由器必须处理数据并动态显示。让我们假设我们有一些由API返回的产品数据,格式如下:
常量productData=[{id:1,的名字:“NIKE Liteforce蓝色运动鞋”,描述:"我与你同在,神圣的至尊。Proin molestie。”,状态:“可用”,},{id:2,的名字:“时尚人字拖和拖鞋”,描述:"美好的生活,美好的生活,美好的生活",状态:“缺货”,},{id:3.,的名字:“ADIDAS Adispree跑鞋”,描述:“Maecenas condimentum porttitor auctor。Maecenas viverra fringilla felis, eu pretium。”,状态:“可用”,},{id:4,的名字:“ADIDAS中号运动鞋”,描述:"这是威尼斯的伤口,这是发酵的伤口",状态:“缺货”,},];
让我们还假设我们需要为以下路径创建路由:
/产品
:这应该显示一个产品列表。李><李class="">/ /产品:productId
:如果产品带有: productId
如果存在,则应该显示产品数据,如果不存在,则应该显示错误消息。李>
创建一个新文件src / Products.js
并添加以下内容(请确保从上面复制产品数据):
进口反应从“反应”;进口{链接,路线,useRouteMatch}从“react-router-dom”;进口产品从”。/产品”;常量产品=({匹配})= >{常量productData=[...];常量{url}=useRouteMatch();/*为每个产品创建一个' '项的数组*/ 常量linkList=productData.地图((产品)= >{返回(<李的关键={产品.id}><链接来={`$ {url}/$ {产品.id}`}>{产品.的名字}</链接></李>);});返回(<div><div><div><h3>产品</h3><ul>{linkList}</ul></div></div><路线路径={`$ {url}/: productId`}><产品数据={productData}/></路线><路线具体的路径={url}><p>请选择产品.</p></路线></div>);};出口默认的产品;
首先,我们使用useRouteMatch
钩子从匹配
对象。然后我们建立一个列表<链接>
组件使用id
属性,我们将其存储在linkList
变量。
方法中的变量路径
prop,它与产品ID对应。当它匹配时,我们渲染<产品>
组件(我们将在一分钟内定义),将我们的产品数据传递给它:
<路线路径={`$ {url}/: productId`}><产品数据={productData}/></路线>
第二条路线有一个确切的
prop,所以只在URL为时才会渲染/产品
什么都没有被选中。
现在,这是<产品>
组件。您需要在src / Product.js
:
进口反应从“反应”;进口{useParams}从“react-router-dom”;常量产品=({数据})= >{常量{productId}=useParams();常量产品=数据.找到(p= >p.id= = =数量(productId));让productData;如果(产品){productData=(<div><h3>{产品.的名字}</h3><p>{产品.描述}</p><人力资源/><h4>{产品.状态}</h4></div>);}其他的{productData=<h2>对不起.产品不存在</h2>;}返回(<div><div>{productData}</div></div>);};出口默认的产品;
的找到
方法用于在数组中搜索ID属性为的对象match.params.productId
.如果产品存在,则productData
会显示出来。如果不存在,则呈现“产品不存在”消息。
最后,更新你的<应用>
组成部分如下:
进口反应从“反应”;进口{链接,路线,开关}从“react-router-dom”;进口类别从”。/类别”;进口产品从”。/产品”;常量首页=()= >(<div><h2>首页</h2></div>);出口默认的函数应用程序(){返回(<div><导航名称=“navbar navbar-light”><ul类名=“nav navbar-nav”><李><链接来=“/”>首页</链接></李><李><链接来=“/类别”>类别</链接></李><李><链接来=“/产品”>产品</链接></李></ul></导航><开关><路线具体的路径=“/”><首页/></路线><路线路径=“/类别”><类别/></路线><路线路径=“/产品”><产品/></路线></开关></div>);}
现在,当您在浏览器中访问应用程序并选择“Products”时,您将看到呈现的子菜单,该菜单依次显示产品数据。
试玩一下demo。向自己保证一切正常,并理解代码中发生的事情。
保护线路
许多现代web应用程序的一个共同要求是确保只有登录用户才能访问网站的某些部分。在下一节中,我们将研究如何实现受保护路由,以便如果有人试图访问/管理
,他们将被要求登录。
然而,React路由器有几个方面我们需要先介绍一下。
的重定向
组件
与服务器端重定向一样,React路由器的重定向组件将用新位置替换历史堆栈中的当前位置。方法指定新位置来
道具。下面是我们使用的方法< >重定向
:
<重定向来={{路径名:/登录的,状态:{从:位置}}}
如果有人试图访问/管理
路径,它们将被重定向到/登录
路线。控件传递有关当前位置的信息状态
道具,这样如果身份验证成功,用户就可以重定向到他们最初试图访问的页面。
定制的路线
自定义路由是描述嵌套在组件中的路由的一种奇特方式。如果我们需要决定是否应该呈现一个路由,那么编写一个自定义路由是可行的方法。
创建一个新文件PrivateRoute.js
在src
目录并添加以下内容:
进口反应从“反应”;进口{重定向,路线,useLocation}从“react-router-dom”;进口{fakeAuth}从”。/登录的;常量PrivateRoute=({组件:组件,...休息})= >{常量位置=useLocation();返回(<路线{...休息}>{fakeAuth.isAuthenticated= = =真正的?<组件/>:<重定向来={{路径名:“/登录”,状态:{从:位置}}}/>}</路线>);};出口默认的PrivateRoute;
如您所见,在函数定义中,我们将接收到的props解构为a组件
道具和休息
道具。的组件
Prop将包含我们的任何组件< PrivateRoute >
保护(在我们的例子中,管理
).的休息
Prop将包含我们已经传递的任何其他道具。
然后返回<路径>
组件,它呈现受保护的组件或将我们重定向到我们的/登录
路由,取决于用户是否登录。这由a决定fakeAuth.isAuthenticated
属性导入的登录> <
组件。
这种方法的优点是,它显然更具有声明性< PrivateRoute >
是可重用的。
重要保安须知
在现实应用中,您需要在服务器上验证对受保护资源的任何请求.这是因为在客户端中运行的任何东西都可能被逆向工程和篡改。例如,在上面的代码中,你可以打开React的开发工具并更改的值isAuthenticated
,从而获得进入保护区的权利。
React应用程序中的身份验证值得专门的教程,但实现它的一种方法是使用JSON Web令牌.例如,您可以在服务器上设置一个端点,该端点接受用户名和密码组合。当它收到这些(通过Ajax)时,它检查凭证是否有效。如果是,它会响应一个JWT, React应用程序将其保存(例如在sessionStorage
),如果不是,则发送一个401年未经授权
向客户端返回响应。
假设成功登录,客户端随后将JWT作为头部连同对受保护资源的任何请求一起发送。然后,服务器在发送响应之前对其进行验证。
存储密码时,服务器不会以明文形式存储它们.相反,它会加密它们——例如,使用bcryptjs.
实现受保护路由
现在让我们实现受保护的路由。改变src / App.js
像这样:
进口反应从“反应”;进口{链接,路线,开关}从“react-router-dom”;进口类别从”。/类别”;进口产品从”。/产品”;进口登录从”。/登录的;进口PrivateRoute从”。/ PrivateRoute”;常量首页=()= >(<div><h2>首页</h2></div>);常量管理=()= >(<div><h2>欢迎管理!</h2></div>);出口默认的函数应用程序(){返回(<div><导航名称=“navbar navbar-light”><ul类名=“nav navbar-nav”><李><链接来=“/”>首页</链接></李><李><链接来=“/类别”>类别</链接></李><李><链接来=“/产品”>产品</链接></李><李><链接来=“/管理”>管理区域</链接></李></ul></导航><开关><路线具体的路径=“/”><首页/></路线><路线路径=“/类别”><类别/></路线><路线路径=“/产品”><产品/></路线><路线路径=“/登录”><登录/></路线><PrivateRoute路径=“/管理”组件={管理}/></开关></div>);}
如你所见,我们添加了一个<管理>
组件添加到文件顶部,并包括我们的< PrivateRoute >
在<转>
组件。如前所述,此自定义路由呈现<管理>
组件,如果用户已登录。否则,用户被重定向到/登录
.
最后,这是Login组件的代码:
进口反应,{useState}从“反应”;进口{重定向,useLocation}从“react-router-dom”;出口默认的函数登录(){常量{状态}=useLocation();常量{从}=状态||{从:{路径名:“/”}};常量[redirectToReferrer,setRedirectToReferrer]=useState(假);常量登录=()= >{fakeAuth.进行身份验证(()= >{setRedirectToReferrer(真正的);});};如果(redirectToReferrer){返回<重定向来={从}/>;}返回(<div><p>你必须记录在浏览网页{从.路径名}</p><按钮onClick={登录}>日志在</按钮></div>);}/*一个伪认证函数*/出口常量fakeAuth={isAuthenticated:假,进行身份验证(cb){这.isAuthenticated=真正的;setTimeout(cb,One hundred.);}};
到目前为止,希望没有太棘手的事情发生。我们使用useLocation钩访问路由器的位置
道具,我们从中获取状态
财产。然后我们使用对象解构来获取用户在被要求登录之前试图访问的URL的值。如果这个不存在,我们将它设置为{pathname: "/"}
.
然后我们使用React的useState
钩子来初始化redirectToReferrer
财产假
.根据这个属性的值,用户或者被重定向到他们要去的地方(也就是说,用户已经登录),或者显示一个按钮让用户登录。
按钮被单击后,fakeAuth.authenticate
方法,该方法设置fakeAuth.isAuthenticated
来真正的
和(在回调函数中)更新的值redirectToReferrer
来真正的
.这将导致组件重新呈现,用户被重定向。
演示工作
让我们把这些拼图拼在一起,好吗?下面是我们使用React路由器构建的应用程序的最终演示。
总结
正如你在本指南中所看到的,React Router是一个功能强大的库,它可以补充React来构建更好的声明性路由。与React Router的早期版本不同,在v5中,一切都“只是组件”。此外,新的设计模式完全符合React的做事方式。
在本教程中,我们学习了:
- 如何设置和安装React路由器李><李class="">路由的基础知识和一些基本组件,如
<路由器>
,<路径>
而且<链接>
- 如何创建导航和嵌套路由的最小路由器李><李class="">如何建立具有路径参数的动态路由李><李class="">如何使用React路由器的钩子和它更新的路由渲染模式李>
最后,我们学习了一些高级路由技术,用于创建受保护路由的最终演示。