React路由

 

单页应用与多页应用

单页应用

只有1个壳页面,使用前端路由控制多个组件切换。优点是:

  1. 所有页面共享公共资源,页面跳转只刷新局部,加载速度快。
  2. 便于页面间共享数据。
    缺点是:
  3. SEO不方便。
  4. 需要特定框架协助开发,以实现路由功能。

多页应用

是传统开发方法,有多个入口文件,使用后端路由决定返回哪个页面。优点是:

  1. 便于SEO。
  2. 开发难度低。
    缺点是:
  3. 每个页面都需要独自加载公共资源,加载速度慢。
  4. 页面间共享数据不方便。

前端路由React Router

routers

解决“全局配置”的问题。

// 写在App.js中
export default () => {
  return (
    <BrowserRouter>
      route matchers...
    </BrowserRouter>
  )
}
<BrowserRouter>

BrowserRouter基于HistoryAPI。
跳转:先调用history.pushState()创建1个history entry,同时改变地址栏,然后调用history.go(1)访问它,触发popstate event,使window.onpopstate执行,从而改变UI。
重定向:先调用history.replaceState()改变当前的history entry,同时改变地址栏,然后调用history.go(0)访问它。

BrowserRouter使用正常的URL,类似于http://example.com/page,这样的URL会全部发送给服务器,所以要配置服务器,使其无论接受什么URL都返回同一页面,否则报404。


```getUserConfirmation(function(message, callback))```:定义在跳转前弹出对话框的实现,默认是基于```window.confirm()```实现。```window.confirm()```显示一个具有一个可选消息和两个按钮(确定和取消)的模态对话框。例如:  

// message 是要在对话框中显示的可选字符串 // result 是一个布尔值,表示是选择确定还是取消 result = window.confirm(message);


```forceRefresh(bool)```:如果为```true```,那么跳转时会整页刷新。默认是```true```。  

```keyLength(number)```:```location.key```的长度。默认是```6```。  

```children(node)```:要渲染的子元素,包括```Switch```、```Route```。

###### \<HashRouter>

HashRouter基于HTMLAnchorElement.hash。

Examples const anchor = document.getElementById(“myAnchor”) // Getter: #Examples string = anchor.hash // Setter anchor.hash = string


HashRouter使用的URL类似于```http://example.com#page```,这样的URL只会发送```#```前面的部分给服务器,所以不需要配置服务器。  

```basename(string)```、```getUserConfirmation()```、```children(node)```同上。  

```hashType(string)```:```window.location.hash```的编码方式。```slash```例如```#/sunshine/lollipops```;```noslash ```例如```#sunshine/lollipops```;```hashbang```例如```#!/sunshine/lollipops```。  

#### route matchers

解决“如何匹配URL”“哪个URL对应哪个组件”的问题。  

// 写在App.js中 export default () => { return ( Component... ) }


###### Switch

如果```Route```在```Switch```中,那么会从当前所在的页面对应的```Route```开始向下寻找,只渲染第一个匹配的```Route```。  
如果不用```Switch```包裹```Route```,那么所有能匹配的```Route```都会被渲染。

```location(object)```:默认时用当前栈顶的```location```进行匹配;如果传了这个值,就用传的值进行匹配。  

```children(node)```:```Route```或```Redirect```。  

###### Route

```routeProps(object)```:```component```、```render```、```children```接受的function都会以它为参数。包含```match```、```location```、```history```。  

```component(function)```:输入routeProps,返回模板。在每次render时,如果```location```匹配,就以这个function为参数调用```React.createElement()```创建一个新组件,并卸载已经存在的组件。  

function Fun(routeProps) { return (模板) }

<Route path=”…” component={Fun} />


```render(function)```:输入routeProps,返回模板。在每次render时,如果```location```匹配,就更新已经存在的组件。  

<Route path=”…” render={routeProps => (模板)} />


```children(function)```:输入routeProps,返回模板。在每次render时,无论```location```是否匹配,都会创建一个新组件,并卸载已经存在的组件。 不匹配时,```routeProps.match```为```null```。  

<Route path=”…” children ={routeProps => (模板)} />


```path(string)```:用于匹配URL的path。默认匹配任何URL。  

```exact(bool)```:当```true```时,匹配整个URL;否则,只匹配URL的begin。  

```strict(bool)```:当```true```时,如果path末尾有```/```,那么URL末尾也要有```/```才能匹配。   

```sensitive(bool)```:当```true```时,大小写敏感。  

#### navigation

###### \<Link>

等价于```a标签```。点击后,默认会添加1个URL为```host[:port]/basename/to```的```history entry```,并访问。  

Home</Link>


```to(string)```:用1个字符串创建```location```用于路由匹配,包括```pathname```、```search```、```hash部分```。  

```to(object)```:创建用于路由匹配的```location```,包括```pathname```、```search```、```hash部分```和```state```。

```to(function(location))```:返回代表新的```location```的对象或字符串。  

```replace(bool)```:当```true```时,会改变当前的```history entry```而不是添加。默认```false```。  

```component(React.Component)```:基于```a标签```自定义地实现导航标签。  

```other```:其它的```a标签```的属性,例如```title```、```id```、```className```等。  

###### \<NavLink>

如果当前URL是匹配```to```的,那么```NavLink```会变成```activeClassName```指定的样式。  

React

```activeClassName(string)```:   。  

```activeStyle(object)```:   。  

```exact(bool)```:当```true```时,只有精确匹配才会显示指定的样式。  

```strict(bool)```:同```Route strict```。 

```isActive(function(match, location))```:决定链接是否激活的额外逻辑。  

```location(object)```:默认是比较栈顶的```history entry```的```location```。当传这个参数时,就是与传的```location```作比较。  

###### \<Redirect>

不需要模板,因为不渲染任何东西,只重定向。  

```to(string)```:要重定向到的```path```。  

```to(object)```:要重定向到的```location```。  

```push(bool)```:当```true```时,会创建新的```location```而不是修改当前的。   

```from(string)```:当```Redirect```用在```Switch```中时,相当于```path```,用于匹配。   

```exact(bool)```:同上。   

```strict(bool)```:同上。   

```sensitive(bool)```:同上。

#### 官方文档中的其它问题

###### Scroll Restoration

解决“跳转之后Scroller应该滚动到哪”的问题。  

###### Server Rendering

解决“服务端渲染的路由应该如何做”的问题。

## 动态路由

###### 静态路由

在APP初始化阶段声明“组件与URL的匹配关系”,匹配关系一旦声明就不可改变。  

###### 动态路由

在APP运行阶段,为组件的```props```添加```path```,说明本组件与哪个URL匹配,匹配关系可以在运行时动态改变的。  
```动态路由```可实现```嵌套路由```、```响应式路由```。

###### 嵌套路由

嵌套路由可实现局部刷新。  
URL的path部分嵌套,形如:/path1/path2;其中,path1与component1对应,path2与component2对应,component1与component2嵌套。  
当访问/path1/other时,刷新component1和other component;再访问/path1/path2时,只刷新component2。

###### 响应式路由

相同的```path```在不同尺寸的屏幕上应该显示不同的UI/组件。例如:  

<Route path=”/invoices” component={Invoices} />

const Invoices = () => ( <div> {screenIsSmall => screenIsSmall ? ( // 小屏的UI ... ) : ( // 大屏的UI ... ) } </div> )


## History

```history```管理一个```history stack```,栈中有```history entry```,用来记录用户的“访问历史”。  
当访问1个```entry```时,哪个组件能匹配它的```location```,就渲染哪个组件。

#### 访问

// 方式一: const history = useHistory()

// 方式二: const history = props.history

// 方式三: <Route path=”…” render={routeProps.history => {…}} />

// 方式四: <Route path=”…” children ={routeProps.history => {…}} />


#### 字段

```length(number)```:```stack```中的```entry```数量。  
```action(string)```:栈顶```entry```的```action```,是```PUSH```、```REPLACE```、```POP```中的一个。  
```location(object)```:栈顶```entry```的```location对象```。   

#### 方法

```push(path, [state])```:向栈中添加1个```entry```,会改变地址栏中的URL,但不访问它。  
```replace(path, [state])```:替换栈顶的```entry```,会改变地址栏中的URL,但不访问它。  
```go(n)```:会问向前或向后的第n个```entry```。  
```goBack()```:等价于go(-1)。  
```goForward()```:等价于go(1)。  

## Location

```location```看起来是这样的:  

{ key: ‘ac3df4’, // not with HashHistory! pathname: ‘/somewhere’, search: ‘?some=search-string’, hash: ‘#howdy’, state: { [userDefined]: true } }


#### 访问

// 方式一: const location = useLocation()

// 方式二: const location = props.location

// 方式三: <Route path=”…” render={routeProps.location => {…}} />

// 方式四: <Route path=”…” children ={routeProps.location => {…}} />


#### 使用

当```目标UI/组件```不只依赖于```path```还依赖于```state```时,可以把```location```作为参数,实现单页传参。例如:  

<Link to={location}/> <Redirect to={location}/> history.push(location) history.replace(location)


## Match

#### 访问

// 方式一: const match = useRouteMatch(path)

// 方式二: const match = useRouteMatch({path:…, exact:…, strict:…, sensitive:… })

// 方式三: const match = props.match

// 方式四: <Route path=”…” render={routeProps.match => {…}} />

// 方式五: <Route path=”…” children ={routeProps.match => {…}} />


#### 字段

```params(object)```:页面间数据传输来的键值对。    
```isExact(boolean)```:是否能匹配整个URL。   
```path(string)```:用于匹配URL的path。  
```url(string)```:被匹配上的URL。  

## Path

```path="/"```匹配任何URL,所以这个```Route```应该放在最后。  
```path```越短的```Route```应该越靠后。 

## 单页传参

#### Query Parameters

数据会在URL中以query形式显示。  

// Component1.js 中写数据

SAM</Link>

// /account 对应的 Component2.js 中读数据 let query = useQuery() query.get(“name”) // sam


#### Path Parameters

数据会在URL中以relactivepath形式显示。

// Component1.js 中写数据 SAM</Link> // /account 对应的 Component2.js 中读数据 let params = useParams() params.name // sam ``` #### Location State 数据不会显示在URL中。 ``` // Component1.js 中写数据 const location = { pathname: '/account', state: {name: 'sam'} } <Link to={location}>SAM</Link> // /account 对应的 Component2.js 中读数据 let location = useLocation() location.state.name // sam ``` ## 反馈重定向 例如,点击按钮后重定向、服务器返回数据后重定向。 ``` <Button onclick={event => { // 方式一: window.location.assign(...) // 方式二: }}>确定</Button> ``` ## 设置默认页面 ``` <Route .../> ... ```