React-Router 02 Route组件

React-Router 02 Route组件

Route组件以前还真没这么仔细地看过

Route组件可以说是React-router的核心,Route的功能有两大块,一是路径匹配,二是渲染组件。

Route路径匹配 - Path属性

Path属性可以传入一个字符串或正则表达式(React-Router库使用path-to-regexp@^1.7.0这个库来解析),或者字符串(正则表达式)的数组。和后端很类似,这个匹配可以是动态的,也可以动态的获取匹配成功的动态路径。

exact

和Path搭配使用的属性还有exact,这个只要加上就表示true,不加上就是false。加上exact表示完全相同才会匹配成功,不加上的话,只要开头URL匹配成功的就算是匹配成功。如果多个路径匹配成功,会把匹配的组件全部都渲染出来。

如果不想使用exact标记,可以使用Switch组件套在Route组件外边,然后将匹配根路径的Route放到最后,一样可以达到目的,此时的App.js如下:

function App() {
    return (
        <BrowserRouter>
            <Nav/>
            <Switch>
                <Route path="/about" component={About}/>
                <Route path="/contact" component={Contact}/>
                <Route path="/user/:user" component={User}/>
                <Route path="/" component={Home}/>
            </Switch>
        </BrowserRouter>
    );
}

Path动态路径写法

之前的例子里是字符串形式的路径,纯粹是静态匹配。实际上很常用的是动态匹配,需要动态的匹配的话,要在Path属性中采取特别的写法,并在组件中使用match对象加上URL路径变量名来获取实际匹配成功的URL字符串。

来写一个组件看一下:

<Route path="/user/:user" exact component={User}/>

export const User = (props) => (
    <p>User name: </p>
);

path属性中的:user部分,就是动态匹配URL的变量名称,匹配成功的部分会被装载到user变量中,但是如何获取呢?

此时如果访问/user/user/,都不会匹配成功,只有URL为/user/*的时候才会显示出User组件。不过此时我们还没有获取动态的路径。

match对象

在每个Route匹配成功的时候,在component属性要渲染的组件中,会被React-router传递一个match对象到props中。match对象非常重要,基于URL的逻辑基本上都和match有关,来看看match的属性。

match.params

通过match.params对象的属性就可以访问到动态路径的内容,就是URL中我们定义的动态路径的变量名,在这个例子中,就是user。

修改一下User组件来显示动态内容:

export const User = (props) => (
    <p>User name: {props.match.params.user}</p>
);

就成功显示出了动态路径中的用户名。

match的其他属性

match还有三个属性如下:

  1. isExact,如果Path上标记了excat,就为true。如果Switch中的内容匹配成功,该属性也为true。
  2. path,原始的匹配路径,即带有变量的URL,就是Route组件中的path属性内容
  3. url,实际匹配成功的完整URL(不含域名和端口)

在这个例子中,可以打印出来看一下:

export const User = (props) => (
    <>
    <p>User name: {props.match.params.user}</p>
    <p>Path : {props.match.path}</p>
    <p>url : {props.match.url}</p>
    <p>isExact : {props.match.isExact? "精确匹配":"非精确匹配"}</p>
    </>
);

Route组件渲染

路径匹配成功之后的Route组件,就要渲染对应的组件。
在最开始的基础例子里,使用了Route的component属性,传递了一个组件让其进行渲染。这只是方式之一。来系统的看一下渲染组件相关的内容,主要有三种方式:

使用component属性

component属性在之前已经使用了,传递了一个组件进去。

由于组件就是一个函数,所以传递函数也同样可以执行,只要函数返回一个组件即可:

<Route path="/about" component={(props)=>(<About {...props}/>)}/>

传递函数的缺点在于:每次渲染的时候,传递的函数每次都会重新挂载和执行,因为传递的函数每次都要新建这个函数对象。所以传递函数不如传递已经定义好的组件效率高。

使用render属性

render属性只接受函数类型的值,render函数可以避免在component属性中传递函数的反复挂载问题。

写法其实是一样的:

<Route path="/about" render={(props)=>(<About {...props}/>)}/>

这个组件的状态会在不同的render函数中保存,不会出现component属性的问题。

要注意的是,同时使用component和render属性的时候,component优先级高于render,会导致render失效。

传递函数的方法有的时候比直接传组件更好用,因为可以传递额外的参数,或者创建更加复杂的业务逻辑,比如传个自定义属性:

<Route path="/about" render={(props)=>(<About {...props} myattr={"saner"}/>)}/>

使用children属性

children属性也接受一个返回组件的函数,但是和前两个方法相比,这个属性的不同之处在于:

即使所在的Route组件没有匹配成功,children属性中的逻辑也会被执行

三种渲染方式,都会把三个属性传递给函数中的props,分别是match,location,history:

  1. location可以从其中取出app当前的位置
  2. history是一个可变对象,实际是React-router依赖的history包包装后的原始history对象
  3. children对象的独特之处在于传进去的props.match在匹配失败的时候是null。另外两种渲染都是成功后才渲染,所以props.match一定不是null。

比如我们来控制,让Contact组件不管什么时候都显示一些东西:

<Route path="/contact" children={(props) => <div>{props.match? "是contact路径":"不是contact路径"}</div>}/>

这种情况下,不管如何,都会显示出当前是不是contact路径。注意,Switch组件内无法触发这个效果

Route组件的核心内容就是路径匹配和渲染。通过路径匹配可以获取实际的匹配路径,从而动态的执行逻辑,比如从state中获取和计算结果并展示。渲染则提供了三种方式用于各种不同逻辑的跳转。

现在根据不同的URL渲染不同的组件这一个核心要素已经知道了,接下来看看链接与跳转,有了链接和跳转搭配之后,就更加方便了。

LICENSED UNDER CC BY-NC-SA 4.0
Comment