Skip to content

Latest commit

 

History

History
529 lines (408 loc) · 23.9 KB

File metadata and controls

529 lines (408 loc) · 23.9 KB

二、配置路由——使用路由组件中的各种选项

React Router 允许您使用<Route>组件以声明方式定义路由。它是 React 路由的主要构建块,当path道具中提到的路径值与浏览器的 URL 位置匹配时,呈现component道具中提到的组件。<Route>组件与任何其他 React 组件一样,接受一组道具。这些道具提供了对浏览器 URL 路径如何匹配<Route>组件路径的更精确控制,以及一些其他呈现选项

在前一章中,我们简要介绍了如何使用<Route>组件匹配 URL 路径并呈现组件。在本章中,我们将了解以下内容:

  • 深入研究可添加到<Route>组件的各种道具,如exactstrictrenderchildrensensitive
  • 路由组件道具:该组件作为<Route>路径匹配的结果进行渲染,作为道具接收数据,然后可用于创建嵌套路由。
  • 路由参数<Route>组件的路径可以配置为接受来自 URL 段的附加参数,这些参数可以在呈现组件中读取。
  • 嵌套或动态路由:可以在呈现组件内部添加<Route>组件,而不是在应用级别定义路由。因此,呈现的组件提供了应用旅程中的下一步。
  • 从 JSON 配置生成路由:JSON 对象中可用的路由信息可用于向应用添加路由。

路由道具

当您查看 React Router 的源代码时,<Route>组件接受以下道具:

Route.propTypes = {
    computedMatch: PropTypes.object, // private, from <Switch>
    path: PropTypes.string,
    exact: PropTypes.bool,
    strict: PropTypes.bool,
    sensitive: PropTypes.bool,
    component: PropTypes.func,
    render: PropTypes.func,
    children: PropTypes.oneOfType([PropTypes.func, PropTypes.node]),
    location: PropTypes.object
};

下面让我们来看看这些道具中的每一个。

确切的道具

在前面的<Route>示例中,我们将'/home'路由路径更改为'/',如下所示:

<div className="container">
     <Route
         path="/"
         component={HomeComponent} 
     />
     <Route
         path="/dashboard"
         component={DashboardComponent} 
     />
 </div>

有了这些路由,当浏览器的 URL 设置为/dashboard时,您会注意到两个组件的内容显示如下:

Inside Home route
Inside Dashboard route

这里,'/dashboard'中的'/'<Route>路径'/''/dashboard'都匹配;因此,它呈现来自这两个组件的内容。要使浏览器的location.pathname<Route>组件的路径完全匹配,请将精确的道具添加到<Route>,如下所示:

..
 <Route
     path="/"
     component={HomeComponent}
     exact
 />
 ..

类似地,当您尝试访问'/dashboard''/dashboard/portfolio'路径时,您会注意到在这两种情况下,DashboardComponent都被渲染。为防止'/dashboard/portfolio'<Route>组件与'/dashboard'路径匹配,请添加exact道具。

React-Router uses the path-to-regexp library internally to determine whether a route element's path prop matches the current location.

严格的道具

<Route>路径有尾随斜杠,并且您希望将此路径(包括尾随斜杠)与浏览器的 URL 相匹配时,则包括strict属性。例如,将<Route>路径从'/dashboard'更改为'/dashboard/'后,<Route>组件仍将匹配 URL 路径,而不带尾随斜杠。换句话说,'/dashboard'<Route>组件与'/dashboard/'路径匹配。

但是,在添加了strict属性之后,React Router 确保<Route>仅在 URL 后面有斜杠时匹配:

<Route
    path="/dashboard/"
    component={DashboardComponent}
    strict
/>

有了此<Route>配置,'/dashboard'路径将不匹配。但是,当您向 URL 添加尾随斜杠时,如在'/dashboard/'中,带有strict道具的<Route>组件将匹配,DashboardComponent将被呈现。

Please note, if you mention additional URL segments, then it would still match the path prop mentioned in the <Route> component. For example, if the URL path is '/dashboard/123', it would match the '/dashboard/' path with a <Route> component that has the strict prop. To match a path including the additional URL segments, you can specify the exact prop along with the strict prop.

敏感道具

<Route>组件的路径不区分大小写,即其路径属性值设置为'/Dashboard'<Route>组件将匹配'/dashboard''/DASHBOARD'URL 路径。要使<Route>组件的路径区分大小写,请添加sensitive属性:

<Route
    path="/Dashboard"
    component={DashboardComponent}
    sensitive
/>

sensitive道具确保在将路径道具的大小写与浏览器的 URL 路径进行匹配时考虑到路径道具的大小写。通过添加sensitive属性,可以使用相同的路径名定义路由,但使用不同的大小写:

<Route
    path="/Dashboard"
    component={DashboardComponent}
    sensitive
/>
<Route
    path="/dashboard"
    component={StockListComponent}
    sensitive
/>

此代码将创建两个不同的路由,并在<Route>组件的区分大小写路径与浏览器的 URL 路径匹配时呈现相应的组件

使用渲染属性进行内联渲染

我们已经了解了当<Route>路径与浏览器的location.pathname路径匹配时,如何使用component道具渲染视图。还有两个其他道具可用于渲染视图:renderchildren

render道具用于内联渲染。作为render道具的一个值提到的函数应返回一个类似于以下内容的 React 元素:

<Route
    path="/user"
    render={() => (
 <div> Inside User Route </div>
 )}
/>

从前面的代码片段中,当'/user'路径与浏览器的 URL 匹配时,将执行指定为render属性值的函数,并呈现从该函数返回的 React 元素。

When you specify both component and render props in the same <Route> component, the component prop will take precedence.

使用子属性的内联渲染

无论是否存在路径匹配,都应在希望渲染视图的情况下使用children道具。children道具的语法与render道具相似,如下所示:

<Route
    path="/sidenav"
    children={() => (
 <div> Inside Sidenav route </div>
 )}
/>

即使未指定path道具,也会渲染带有children道具的<Route>组件。此外,exactstrict道具不会对带有children道具的<Route>组件产生任何影响。

Both the component and render props take precedence over the children prop. Also, when either the component or render props are mentioned, the view is rendered only if the path matches the requested URL.

带有children道具的<Route>组件根据其在路由列表中的位置进行渲染。例如,如果将上一个<Route>组件指定为路由列表中的最后一个条目,则将在呈现所有前面的匹配路由之后呈现该组件。此外,如果前面的<Route>组件列在匹配路由之前,则在呈现匹配路由的内容之前呈现路由的内容,如下所示:

<Route
    path="/sidenav"
    children={() => (
        <div> Inside Sidenav route </div>
    )}
/>

<Route
    path="/user"
    render={() => (
        <div> Inside User route </div>
    )}
/>

在这里,当您尝试访问'/user'路径时,带有children道具的<Route>组件将在使用'/user'路径渲染路径之前进行渲染。

路由组件道具

<Route>路径与浏览器的 URL 路径匹配时呈现的组件接收到某些props,例如historylocationmatchstaticContext。这些道具提供的数据包括与路由有关的信息。这些道具可用于使用<Route>组件的componentrenderchildren道具进行渲染的组件。

staticContext属性是在服务器端呈现应用时设置的,在客户端路由(即使用<BrowserRouter>接口)中不可用(如中,设置为undefined。接下来的章节将介绍应用的服务器端呈现。

历史

React 路由依赖于history包。history是一个 JavaScript 库,用于维护任何 JavaScript 应用中的会话。考虑下面的历史文献的引用 https://github.com/ReactTraining/history

"history is a JavaScript library that lets you easily manage session history anywhere JavaScript runs. history abstracts away the differences in various environments and provides a minimal API that lets you manage the history stack, navigate, confirm navigation, and persist state between sessions."

history对象有几个属性和方法:

  • 动作:当前动作、PUSHPOPREPLACE
  • 长度:历史堆栈中条目的计数
  • 位置:当前位置,包括hashpathnamesearchstate属性
    • 散列:散列片段
    • 路径名:URL 路径
    • 搜索:URL 查询字符串
    • 状态:使用location.pushState从一条航线导航到另一条航线时提供的状态信息
  • block():注册一条提示消息的功能,当用户试图离开当前页面时,该消息将显示。
  • createHref():构造 URL 段的函数;它接受具有pathnamesearchhash属性的对象。
  • go(n):在历史堆栈中导航的函数。在history堆栈中history.go(-1)将指针向后移动一个位置,history.go(1)将指针向前移动一个位置。
  • goBack():在history堆栈中将指针向后导航一个位置的函数;与history.go(-1)相同。
  • goForward():在history栈中将指针向前导航一个位置的函数;与history.go(1)相同。
  • listen(listenerFn):一个注册侦听器函数的函数,每当history.location发生变化时,该函数就会被调用。
  • push(path, state?):导航到给定路径名的函数,在history堆栈中添加一个条目。它可以选择接受一个state参数,该参数可用于传递应用状态数据。
  • replace(path, state?):导航到给定路径名的函数,替换history堆栈中的当前条目。它还接受一个可选的state参数。

当用户尝试在页面之间导航时,React Router 内部使用history对象更新历史堆栈中的条目。它作为道具提供给呈现组件,以便用户可以使用history对象中的上述方法导航到不同的页面。在下一章中,我们将了解 React Router 提供的各种 API,它们可以帮助您导航到应用中定义的不同路由。

位置对象

location对象提供表示应用当前状态的数据快照。它包括以下属性:pathnamehashsearchstate。导航组件可以为这些道具提供值,然后匹配浏览器 URL 的呈现组件可以读取这些值。如前所述,我们将在第 3中查看各种导航组件,使用链接和导航链接组件导航到路由

位置信息也可以在history对象中找到;但是,history对象是可变的,因此应该避免访问history对象中的位置。

匹配对象

match对象包含<Route>路径如何匹配当前 URL 的信息。包括urlpathisExactparams属性。

让我们参考前面使用render道具的路由之一:

<Route
    path="/user"
    render={({ match }) => {
        console.log(match);
        return (
            <div> Inside User route </div>
        );
    }}
/>

当您尝试访问/user路径时,match对象的属性将具有以下值:

url - '/user'
path - '/user'
params - {}
isExact - true
  • url:返回 URL 匹配部分的字符串
  • path:返回路由路径字符串的字符串,即<Route>组件的路径属性中提到的路径模式
  • params:包含传递到路由的路径参数列表的对象(在接下来的章节中将有更多关于参数的内容)
  • isExact:布尔值;如果 URL 与提供的path道具完全匹配,则为true

如果只有 URL 段的一部分与<Route>组件的路径匹配,isExact属性为false。例如,/user路径的<Route>组件与/user/123的 URL 不完全匹配,在本例中,isExact为 false。

如前所述,无论path道具是否匹配浏览器的 URL 路径,都会呈现带有children道具的<Route>组件。此处,如果路径与 URL 段不匹配,match对象将设置为 null:

<Route
    path="/sidenav"
    children={({ match }) => {
        console.log(match)
        return (
            <div> Inside Sidenav route </div>
        );
    }}
/>

使用此<Route>配置,当您尝试访问/user路径时,与/sidenav路径匹配的<Route>组件,因为它具有children属性。然而,这里的match对象被设置为 null。这有助于确定路径是否与带有children道具的<Route>组件的 URL 段匹配。

路由参数

React 路由中的<Route>组件可以配置为接受给定对象更改的 URL 参数。例如,为了显示给定userID的用户信息,对于userID'1'的用户,URL 路径可以类似于'/user/1',对于userID'123'的用户,URL 路径可以类似于'/user/123'。URL 的最后一部分是动态的;然而,在每个实例中,渲染组件将对给定的userID执行相同的操作

这种用例的一个例子是 Twitter 的个人资料页面。页面接受twitterID并显示给定用户的提要。

<Route>组件可以配置为接受 URL 中的动态部分,方法是在'to'属性中添加一个附加路径,前缀为冒号(:),如下所示:

<Route
    to='/github/:githubID'
    component={GitHubComponent}
/>

这里,'/:githubID'路径是动态的,可以匹配'/github/ryanflorence''/github/mjackson'等路径(React Router 创建者的 GitHub ID)

然后可以使用match.params在呈现组件中使用这些匹配的 URL 参数:

export class GitHubComponent extends Component {
    render() {
        const { match: { params } } = this.props;
        return (
            <div>
                In GitHubComponent <br />
                GitHub ID - {params.githubID}
            </div>
        )
    }
}

当您尝试访问'/github/mjackson'URL 路径时,您将看到以下消息:

In GitHubComponent
GitHub ID - mjackson

match.params对象包含路由中匹配参数的键值对。<Route>组件也可以接受 URL 中的多个参数,如下图:

<Route
    path="/github/:githubID/:twitterID"
    component={GitHubComponent}
/>

这里,githubIDtwitterID参数是动态的,可以匹配'/github/ryanflorence/mjackson'等 URL 路径。可以使用match.params.twitterID在组件中读取第二个参数twitterID

在前面的<Route>配置中,githubIDtwitterID参数是必需的参数,即如果 URL 路径中不存在这两个参数,则路由将不匹配。要将参数标记为可选,请在参数后面加一个问号(?,如以下代码段所示:

<Route
    path="/github/:githubID/:twitterID?"
    component={GitHubComponent}
/>

在前面的<Route>配置中,twitterID参数被标记为可选。这意味着,当您尝试访问'/github/ryanflorence'路径时,即在不向 URL 中的twitterID参数提供值的情况下访问该路径,则该路径将与 URL 匹配,并且组件将被呈现。但是,当组件尝试使用match.params.twitterID访问参数时,它将返回undefined

<Route>路径也可以配置为接受与正则表达式匹配的参数,如下所示:

...
<Route
    path="/github/:githubID(\w+)"
    component={GitHubComponent}
/>
<Route
    path="/user/:userID(\d+)"
    component={UserComponent}
/>
...

此处,githubID参数仅限于字母数字字符串,userID参数仅限于数字值。param 以 regex 模式作为后缀,以定义<Route>param 将接受的值的类型,即限制可提供给 param 的值的模式。

嵌套路由与动态路由

React Router 的早期版本要求预先定义路由,子路由嵌套在另一个路由中,如下所示:

<Router>
    <Route path='/' component={Container}>
        <IndexRoute component={Home} />
        <Route path='user' component={User}>
            <IndexRoute component={Twitter} />
            <Route path='instagram' component={Instagram} />
        </Route>
    </Route>
</Router>

此代码可以被视为静态路由,其中当应用初始化时,库需要路由配置。这里,具有'/'路径的路由作为所有路由的父路由,具有'user'路径的路由是'/'的子路由,具有'instagram'路径的路由是父路由。

在 React Router v4 中,可以在呈现的组件中定义嵌套路由,即当用户在应用中导航时注册路由。在 v4 中重写时,<Route>是 React 组件,因此可以包含在任何组件的render方法中。

考虑一个父路径,如在 EndoT0*中所定义的:

<Route
    path="/category"
    component={CategoryComponent}
/>

这里,'/category'路径映射到CategoryComponent组件。

CategoryComponent可以依次使用相同的<Route>组件渲染其他路由。但是,在呈现组件(CategoryComponent内定义路由时,需要在<Route>组件的to属性中指定对当前匹配 URL 的引用。例如,可以使用<Route>组件创建具有'/pictures'路径的子路由;但是,需要在to属性中指定绝对路径,即'/category/pictures'或更一般的'/<current_matching_url>/pictures'

如前所述,传递给呈现组件的match属性包含路径如何匹配当前 URL 的信息。match道具的 URL 属性可用于引用父 URL:

export const CategoryComponent = ({ match }) => {
    return (
        <div className="nested-route-container">
            <div className="root-info">
                <h4> Root: </h4>
                <h5> path: {match.path}, isExact: {match.isExact.toString()}</h5>
            </div>
            <Route
                path={`${match.url}/pictures`}
                render={({ match }) => {
                    return (
                        <div>
                            <h4> Viewing pictures: </h4>
                            <h5> 
                                 path: {match.path}, 
                                 isExact: 
                               {match.isExact.toString()}
                            </h5>                   
                        </div>
                    )
                }}
            />
            <Route
                path={`${match.url}/books`}
                render={({ match }) => {
                    return (
                        <div>
                            <h4> Viewing books: </h4>
                            <h5> 
                                 path: {match.path},
                                 isExact: 
                               {match.isExact.toString()}
                            </h5>                       
                            <Route                   
                          path={`${match.url}/popular`}
                                render={({ match }) => (
                                    <div> 
                                          Inside popular, 
                                          path: 
                                          {match.path}
                                    </div>
                                )} />
                        </div>
                    )
                }}
            />
        </div>
    )
}

前面代码段中定义的CategoryComponent接受match属性,组件中定义的路由具有'${match.url}/<child_route_path>'格式的路径值。match.url模板变量包含父路由的 URL 值,在本例中为/category。使用相同的原理,还定义了路径为'/category/pictures''/category/books'的路由。

让我们测试一下这些路由:

  • 场景 1location.pathname'/category'

在这里,将呈现父路由,页面将呈现路由信息,如下所示:

         Root:
         path: /category, isExact: true

这里,match.isExact是正确的,因为/category路径之后没有额外的 URL 段。

  • 场景二:location.pathname'/category/pictures''/category/books'

呈现'/category'父路由后,库查找路径为'/category/pictures''/category/books'<Route>组件。它会找到一个并渲染相应的组件:

            Root:
            path: /category, isExact: false
            Viewing pictures:
           path: /category/pictures, isExact: true

现在,父路由中的match.isExact(带有'/category'路径的<Route>组件)为 false;然而,这在儿童路由中是真实的。

  • 场景 3location.pathname'/category/books/popular'

可以根据需要嵌套任意多条路由。这里,'/books'是一个嵌套路由,还有另一个嵌套路由'/popular',与'/category/books/popular'路径匹配:

              Root:path: /category, 
              isExact: false
              Viewing books:
             path: /category/books, isExact: false
             Inside popular, 
             path: /category/books/popular

match道具在创建嵌套管线时非常有用。这些嵌套管线只有在呈现其父管线时才可访问,从而允许您动态添加管线。

来自 JSON 的动态路由

还可以通过查找包含路由配置选项集合的数组来生成一组<Route>组件。每个路由选项应包含必要的详细信息,如'path''component'

路由的集合可能如下所示:

const STOCK_ROUTES = [
    {
        path: 'stats',
        component: StatsComponent,
    },
    {
        path: 'news',
        component: NewsComponent
    },
    {
        path: 'trending',
        component: TrendingComponent
    }
];

前面数组中的每个对象都包含一个指定路由路径的'path'键和一个包含用户访问路由时要渲染的组件引用的'component'键。然后,可以在组件的render方法中使用前面的集合来生成<Route>组件的列表,如下所示:

...
render() {
    const { match } = this.props;
    return (
        <div>
            Inside Stocks, try /stocks/stats or /stocks/news or /stocks/trending 
            {
                STOCK_ROUTES.map((route, index) => {
                    return (
                        <Route
                            key={index}        
                      path={`${match.url}/${route.path}`}
                            component={route.component}
                        />
                    ) })
            }
        </div>
    );
}
...

STOCK_ROUTES中定义的路由配置用于在StockComponent呈现时添加<Route>组件列表,父<Route>组件在'/stocks'路径呈现,因此在'/stocks'路径下生成<Route>组件时在路径中使用match.url

总结

在本章中,我们了解到<Route>组件可以使用各种道具进行配置。这包括仅当浏览器的 URL 路径与<Route>组件路径中提到的值匹配时,才使用exact道具呈现组件;在<Route>组件中使用strict属性,以确保 URL 路径与path属性中提到的尾部斜杠匹配;包括sensitive道具,使path道具值区分大小写;使用renderchildren道具进行内联渲染。带有children道具的<Route>组件将呈现,与path道具中指定的值无关。如果页面布局中有多个视图组件,并且无论path属性中指定的值如何,都应呈现这些组件,则此选项非常有用

作为<Route>路径匹配结果呈现的组件可以作为道具接收数据。包括historylocationmatchstaticContext等道具。match道具可用于创建嵌套路由,也就是说,match道具中的url属性包含可以在渲染组件中包含的<Route>组件的path道具中使用的信息。<Route>组件也可以通过查找对象中指定的配置来添加。然后,可以使用包含pathcomponent信息的数组在应用中添加多条路由。

<Route>组件的path属性可以配置为接受 URL 段作为路径参数。然后,渲染组件可以使用match.params读取这些参数。通过将正则表达式指定为path参数的后缀,可以将参数配置为接受某些值。