首先,安装create-react-app并用它创建一个新的项目。
npminstall-gcreate-react-appcreate-react-appdemo-appcddemo-app安装你可以用npm或者yarn安装ReactRouter,由于我们在构建一个webapp,所以我们在这个文档中使用react-router-dom
npminstallreact-router-dom接下来,赋值粘贴下面的任意一个列子到项目的/App.js中。
在这个例子中,路由器处理了3个页面:home、about、user。当你点击不同的Link时,路由器会渲染与之匹配的Route
ReactRouter中的组件主要分为三类:
我们也喜欢将导航组件称为"routechangers"。
注意:在webapp中,所有这些组件需要从react-router-dom中引入。
import{BrowserRouter,Route,Link}from"react-router-dom";路由器每个ReactRouter应用程序的核心应该是路由器组件。对于web项目,react-router-dom提供BrowserRouter和HashRouter路由器。两者之间的主要区别是它们存储URL和与Web服务器通信的方式。
要使用路由器,只需要确保将其渲染在根目录下即可,通常,您会将顶级的App元素包装在路由器下,如下所示:
如果没有匹配的Route,Switch将什么都不会渲染(null)。
webpack内置了对动态导入的支持;但是,如果您使用的是Babel(例如,将JSX编译为JavaScript),则需要使用@babel/plugin-syntax-dynamic-import插件。这是仅语法的插件,这意味着Babel不会进行任何其他转换。该插件仅允许Babel解析动态导入,因此webpack可以将它们捆绑为代码拆分。您的.babelrc应该如下所示:
{"presets":["@babel/preset-react"],"plugins":["@babel/plugin-syntax-dynamic-import"]}loadable-components是用于通过动态导入加载组件的库。它自动处理各种边缘情况,并使代码拆分变得简单,下面是有关如何使用loadable-components的示例:
由于浏览器已经开始处理“默认情况”,并且应用具有不同的滚动需求(例如本网站),因此我们不提供默认滚动管理功能。本指南可以帮助您实现所需要的滚动功能。
import{useEffect}from"react";import{useLocation}from"react-router-dom";exportdefaultfunctionScrollToTop(){const{pathname}=useLocation();useEffect(()=>{window.scrollTo(0,0);},[pathname]);returnnull;}如果您尚未使用React16.8,则可以使用React.Component子类执行相同的操作:
importReactfrom"react";import{withRouter}from"react-router-dom";classScrollToTopextendsReact.Component{componentDidUpdate(prevProps){if(this.props.location.pathname!==prevProps.location.pathname){window.scrollTo(0,0);}}render(){returnnull;}}exportdefaultwithRouter(ScrollToTop);然后将其放在您应用的顶部。
在这一点上,我们希望提供一个通用的API。这就是我们想要实现的:
棘手的部分是当你不希望窗口滚动被管理的时候,如何定义一个"退出"的api,例如,如果您在页面内容中浮动了一些标签导航,则可能不想滚动到顶部。
当我们得知Chrome现在可以为我们管理滚动位置,并意识到不同的应用程序将具有不同的滚动需求时,我们有点迷失了我们需要提供某些东西的信念,尤其是当人们只想滚动到顶部时。
本指南的目的是说明使用ReactRouter时要具有的思维模型。我们称之为“动态路由”,它与您可能更熟悉的“静态路由”完全不同
//AngularStylerouting:constappRoutes:Routes=[{path:"crisis-center",component:CrisisListComponent},{path:"hero/:id",component:HeroDetailComponent},{path:"heroes",component:HeroListComponent,data:{title:"HeroesList"}},{path:"",redirectTo:"/heroes",pathMatch:"full"},{path:"**",component:PageNotFoundComponent}];@NgModule({imports:[RouterModule.forRoot(appRoutes)]})exportclassAppModule{}Ember有一个常规的route.js文件,该版本会为您读取并导入到应用程序中。同样,这是在您的应用渲染之前发生的。
//EmberStyleRouter:Router.map(function(){this.route("about");this.route("contact");this.route("rentals",function(){this.route("show",{path:"/:rental_id"});});});exportdefaultRouter;尽管API不同,但它们都共享“静态路由”模型。ReactRouter也跟进了直到v4。为了成功使用ReactRouter,您需要忘记上面这些内容!
当我们讨论动态路由时,我们是指在您的应用渲染时发生的路由,而不是在运行中的应用之外配置或约定的。
这意味着几乎所有内容都是ReactRouter中的一个组件。下面是对该API的60秒回顾,以了解其工作原理:
首先,为您要定位的环境获取一个Router组件,并将其呈现在应用程序的顶部。
许多路由器具有“嵌套路由”的概念。如果您使用了v4之前的ReactRouter版本,那么您也会知道它也是如此,当您从静态路由配置转移到动态渲染的路由时,如何“嵌套路由”?
再进一步学习更复杂的内容吧!
考虑到一个用户导航至/invoices,您的应用程序适应不同的屏幕尺寸,他们屏幕比较小的时候,你只能向他们展示单据清单和跳转单据的dashboard的链接,所以他们可以从这里在跳转到更深的地方。
SmallScreenurl:/invoices+----------------------+|||Dashboard|||+----------------------+|||Invoice01|||+----------------------+|||Invoice02|||+----------------------+|||Invoice03|||+----------------------+|||Invoice04|||+----------------------+在更大的屏幕上,我们希望显示一个主从视图,该视图的左侧是导航,而dashbord或特定的单据则显示在右侧。
LargeScreenurl:/invoices/dashboard+----------------------+---------------------------+||||Dashboard||||Unpaid:5|+----------------------+|||Balance:$53,543.00||Invoice01||||PastDue:2|+----------------------+|||||Invoice02||||+-------------------+|+----------------------+||||||+++|||Invoice03|||+||||||||||+|+||+----------------------+|||||||||||+--+-+--+--+--+--+--+||Invoice04|||||+----------------------+---------------------------+现在暂停一分钟,思考一下/invoices路径如何适配两种屏幕大小,或者更大的屏幕呢,他还能适用吗?我们应该在右边放什么呢?
url:/invoices+----------------------+---------------------------+||||Dashboard|||||+----------------------+|||||Invoice01|||||+----------------------+|||||Invoice02|||||+----------------------+|||||Invoice03|||||+----------------------+|||||Invoice04|||||+----------------------+---------------------------+在大屏幕上,/invoices不是有效的路径,但在小屏幕上是,为了使事情变得更有趣,请考虑使用大型手机的人。他们可能会纵向查看/invoices,然后将手机旋转至横向。突然,我们有足够的空间来显示主从界面,因此您应该立即进行重定向!
ReactRouter带有一些hooks,可让您访问路由器的状态并从组件内部执行导航。
请注意:您需要使用React>=16.8才能使用这些钩子
useHistoryhook使您可以访问可用于导航的历史记录实例。
如下示例:
默认为"slash"
要呈现的单个子元素
允许访问组件的底层引用。
使用React.createRef获取组件的底层引用。
NavLink是Link标签的一个特殊版本,当匹配到当前URL时,可以为渲染的元素添加特定的样式属性。
aria-current属性的值应用在active的link上。有效值为:
默认为page
location.key的长度,默认为6。
要呈现的单个子元素(组件)
渲染一个Redirect组件将跳转至一个新的地址,新地址将覆盖历史记录中的当前条目。就像服务端的重定向。
如果push为true,重定向将会向历史记录中推入新的条目而不是替换当前条目。
路由组件可能是ReactRouter中了解和学习中使用的最重要的组件。他的最基本的职责是在其路径与当前URL匹配时呈现某些UI。
思考下面的代码:
建议使用的Route渲染方式是使用子元素,就像上面的例子上展示的。但是还是有一些其他的方式可用于Router渲染。提供这些方式主要是为了支持引用hooks之前版本的路由器构建的应用程序。
在不同的情况下使用不同的方式。在指定的中,你应该只使用其中的一种。请参阅下面的解释,了解为什么有三个选项。
所有的渲染方式都会提供相同的三个路由属性。
Reactcomponent仅当路径匹配时才会渲染。它将会与routeprops一起渲染。
使用render可以方便地进行内联渲染和包装,而无需进行上文解释的不必要的组件重装。
你可以传入一个函数,以在位置匹配时调用,而不是使用component创建一个新的React元素。render渲染方式接收所有与component方式相同的routeprops。
有些情况下,不管路径是否与位置匹配,你都需要去渲染一些东西,在这种情况下,你可以使用childenprop,除了不论是否匹配它都会被调用以外,它的工作原理与render完全一样。
children属性与component和render属性接收相同的routeprops,除法路由与路径不匹配,不匹配时match为null。它允许你根据路由是否匹配动态的调整你的UI。如下所示,如果路线匹配,我们会添加一个激活类。
可以是path-to-regexp能够理解的任何有效的URL路径。
当为true时,只有在path完全匹配location.pathname时才匹配。
使用低阶Router的最常见用例是同步一个自定义历史记录与一个状态管理库,比如Redux或Mobx。请注意,将ReactRouter和状态管理库一起使用并不是必需的,它仅用于深度集成。
这与仅仅使用一系列Route有何不同?
Switch只会渲染一个路由。相反,仅仅定义一系列Route时,每一个与路径匹配的Route都将包含在渲染范围内。考虑如下代码:
但是,有时候我们只想选择一个Route来呈现。比如我们在URL为/about时不想匹配/:user(或者显示我们的404页面),这该怎么实现呢?以下就是如何使用Switch做到这一点:
这对于动画转换也很有用,因为匹配的Route与前一个渲染位置相同。
所有Switch的子元素都应该是Route或Redirect。只有第一个匹配当前路径的子元素将被呈现。
Route组件使用path属性进行匹配,而Redirect组件使用它们的from属性进行匹配。没有path属性的Route或者没有from属性的Redirect将始终与当前路径匹配。
当在Switch中包含Redirect时,你可以使用任何Route拥有的路径匹配属性:path、exact和strict。from只是path的别名。
如果给Switch提供一个location属性,它将覆盖匹配的子元素上的location属性
下面几种术语也有涉及:
historyobject通常具有以下属性和方法
history对象是可变的,所以我们不推荐从history.location中获取location,推荐从props中获取。这可以确保您对React的假设在生命周期挂钩中是正确的。例如:
位置表示应用程序当前位置,您希望其运行的位置,甚至是之前的位置。看起来像这样
{key:'ac3df4',//notwithHashHistory!pathname:'/somewhere',search:'some=search-string',hash:'#howdy',state:{[userDefined]:true}}路由器将在下面几个地方为您提供位置对象:
它也可以在history.location上获取到,但您不应该使用从history.location上获取到的location对象,因为它是可变的。
位置对象永远不会发生突变,因此您可以在生命周期挂钩中使用它来确定何时进行导航,这对于数据获取和动画处理非常有用。
通常情况下,您只需要传入字符串,但是如果您需要添加一些“位置状态”,只要应用返回到该特定位置,该状态便可用,则可以使用位置对象代替。如果您想基于导航历史而不是仅基于路径来区分UI,这将非常有用。
这样可以防止他们在路由器状态下使用实际位置。这对于动画和待处理的导航很有用,或者在您想要诱使组件在与真实位置不同的位置进行渲染时,这很有用。
可以在下面几个地方获取match对象:
如果Route没有路径,那它将始终匹配,你会获得最接近的父项匹配。这个原理同样适用于withRouter。
解析URL的默认方式是将match.url字符串连接到relative-path。
import{matchPath}from'react-router';constmatch=matchPath('/users/123',{path:'/users/:id',exact:true,strict:false});pathname第一个参数是要匹配的路径名。如果您在服务器上通过Node.js使用,它将是req.path。
静态方法和属性
封装组件的所有无反应的特定静态方法和属性都会自动复制到connected组件。
被包装的组件被公开为返回组件上的静态属性WrappedComponent,它可用于隔离测试组件等等。