# 路由

## SPA

1. 单页Web应用（single page web application，SPA）。
2. 整个应用只有一个完整的页面。
3. 点击页面中的链接不会刷新页面，只会做页面的局部更新。
4. 数据都需要通过ajax请求获取, 并在前端异步展现。

## 路由的作用

一个路由就是一个`k`(路径)与`v`(方法/组件)的映射关系，主要用于实现SPA应用。

**路由分类：**

1. 后端路由。比如javaweb路径映射对应的servlet
2. 前端路由
   1. 浏览器端路由，value是component，用于展示页面内容
   2. 注册路由: `<Route path="/test" component={Test}>`
   3. 工作过程：当浏览器的path变为/test时, 当前路由组件就会变为Test组件

**基本原理**：

1. 触发事件修改浏览器地址
2. 内部监听到浏览器地址发生变化
3. 根据路径对对应的组件进行更新

## 路由组件与一般组件

1. 写法不同
   1. 一般组件：`<Demo />`
   2. 路由组件: `<Route path="/about" component={About}/>`
2. 项目位置不同
   1. 一般组件：`src/components`
   2. 路由组件：`src/pages`
3. `props`属性不同
   1. 一般组件，传递什么props，组件接收的到的就是什么
   2. 路由组件，固定接收三个props：

      ```
      history
      	go(n)
      	goBack()
      	goForward()
      	push(path, state)
      	replace(path, state)
      location
      	pathname: "/about"
      	search: ""
      	state: undefined
      match
      	params: {}
      	path: "/about"
      	url: "/about"
      ```

## 使用react-router-dom

### Link 路由链接

1. 功能：由链接跳转到指定的组件
2. 配置路由器，要想路由生效，必须配置路由器，并且同一应用要使用同一路由器
   1. `BrowserRouter`， 通过H5的histroy对象操作历史记录完成

      ```jsx
      <BrowserRouter>
      	<App />
      </BrowserRouter>
      ```
   2. `HashRouter`，通过锚点`#`完成

      ```jsx
      <HashRouter>
      	<App />
      </HashRouter>
      ```
3. 编写路由链接`<Link/>`

   ```jsx
   <Link className="list-group-item" to="/about">About</Link> <!-- 此链接将会跳转到/about路径下 -->
   <Link className="list-group-item" to="/home">Home</Link> <!-- 此链接将会跳转到/home路径下 -->
   ```
4. 注册路由

   ```jsx
   <Route path="/about" component={About}/> <!--/about路径将会跳转/展示About组件-->
   <Route path="/home" component={About}/> <!--/home路径将会跳跳转/展示Home组件-->
   ```
5. `Link`最终会被转换为HTML的a标签

### NavLink 导航路由链接

`Link`组件不能增加任何的点击事件，可以使用升级版`NavLink`。如果指定的`NavLink`被触发了点击事件，那么就会默认给`NavLink`上增加一个`active`的样式类名，从而增加了点击效果

```jsx
<NavLink className="list-group-item" to="/about">About</Link> <!-- 此链接将会跳转到/about路径下 -->
```

点击了该路由，会变为：

```jsx
<NavLink className="list-group-item active" to="/about">About</Link> <!-- 此链接将会跳转到/about路径下 -->
```

或者通过`activeClassName`属性，修改要增加的样式类名：

```jsx
<NavLink activeClassName="open" className="list-group-item" to="/about">About</Link> 
<!-- ↓ 点击将会变成 -->
<NavLink activeClassName="open" className="list-group-item open" to="/about">About</Link> 
```

> 可以封装NavLink组件，减少代码

### Switch 单一匹配

```jsx
<Switch>
  <Route path="/about" component={About}/>
  <Route path="/home" component={Home}/>
  <Route path="/home" component={Test}/>
</Switch>
```

1. 通常情况下，path和component是一一对应的关系
2. 当出现一个path对应多个component时，会匹配并展示所有匹配的component
3. Switch可以提高路由效率，单一匹配

### 模糊匹配与精准匹配

**默认为模糊匹配（输入路径必须包含路由路径）：**

```jsx
<NavLink className="list-group-item" to="/about">About</Link> 
<NavLink className="list-group-item" to="/home/a/b">Home</Link> 

<Switch>
  <Route path="/about" component={About}/>
  <Route path="/home" component={Home}/>
</Switch>
```

**开启精准匹配（不推荐）：**

```jsx
<Switch>
  <Route exact path="/about" component={About}/>
  <Route exact path="/home" component={Home}/>
</Switch>
```

### Redirect 路由重定向

当所有的路由都匹配不上，就会重定向到`Redirect`定义的路径上：

```jsx
<Switch>
  <Route path="/about" component={About}/>
  <Route path="/home" component={Home}/>
  <Redirect to="/about"/>
</Switch>
```

### 多级路由

1. 在父路由组件下新建文件夹再定义路由
2. 定义子路由path时要带上父路由的path

```jsx
<!-- App 父组件下的路由 src/pages/home about -->
<Switch>
  <Route path="/about" component={About}/>
  <Route path="/home" component={Home}/>
  <Redirect to="/about"/>
</Switch>

<!-- Home 子组件下的路由， src/pages/home/news message -->
<Switch>
  <Route path="/home/news" component={News}/>
  <Route path="/home/message" component={Message}/>
  <Redirect to="/home/news"/>
</Switch>
```

### 路由传参

#### params参数

1. 注册路由并声明接收

   ```jsx
   <Route path="/demo/test/:name/:title" component={Test} />
   ```
2. 路由链接携带参数

   ```jsx
   <Link to='/demo/test/tom/18' />
   ```
3. 接收参数

   ```jsx
   const {name, title} = this.props.match.params;  // 这里可以只传入id，然后发送接口请求数据
   ```
4. 最后会落实到`url path params`参数上，所以称为params参数

#### search参数

1. 注册路由无需声明参数

   ```jsx
   <Route path="/demo/test" component={Test} >Test</Link>
   ```
2. 路由链接携带参数

   ```jsx
   <Link to='/demo/test?name=tom&age=19' >Test</Link>
   ```
3. 接收参数

   ```jsx
   // 接收search参数
   const {search} = this.props.location  // ?name=tom&age=19
   const {id,title} = qs.parse(search)   // 使用querystring解析
   ```

#### state 参数

1. 这个state参数不是组件的state
2. 注册路由无需声明参数

   ```jsx
   <Route path="/demo/test/:name/:title" component={Test} />
   ```
3. 路由链接携带参数

   ```jsx
   <Link to={{path:'/demo/test', test:{name: "tom", age: 18}}} >Test</Link>
   ```
4. 接收参数

   ```jsx
   this.props.location.state
   ```

### `push`和`replace`

```jsx
<li>
  <MyNavLink replace to="/home/news">News</MyNavLink> <!-- 默认就是push，这里开启replace，历史记录栈顶将会被替换，而不是新压栈-->
</li>
```

### 编程式路由

当路由时间不可以由人触发时，Link和NavLink就无法生效，所以借助路由组件的`props.history`对象的api进行操作，让路由组件中的普通组件可以触发相应的路由操作。

> 注意，只有路由组件有history对象

```jsx
replaceShow = (id,title)=>{
  //replace跳转+携带params参数
  //this.props.history.replace(`/home/message/detail/${id}/${title}`)

  //replace跳转+携带search参数
  // this.props.history.replace(`/home/message/detail?id=${id}&title=${title}`)

  //replace跳转+携带state参数
  this.props.history.replace(`/home/message/detail`,{id,title})
}

pushShow = (id,title)=>{
  //push跳转+携带params参数
  // this.props.history.push(`/home/message/detail/${id}/${title}`)

  //push跳转+携带search参数
  // this.props.history.push(`/home/message/detail?id=${id}&title=${title}`)

  //push跳转+携带state参数
  this.props.history.push(`/home/message/detail`,{id,title})

}

back = ()=>{
  this.props.history.goBack()
}

forward = ()=>{
  this.props.history.goForward()
}

go = ()=>{
  this.props.history.go(-2)
}

render() {
  const {messageArr} = this.state
  return (
    <div>
      <ul>
        {
          messageArr.map((msgObj)=>{
            return (
              <li key={msgObj.id}>
                <Link to={{pathname:'/home/message/detail',state:{id:msgObj.id,title:msgObj.title}}}>{msgObj.title}</Link>
								{/*这里通过onClick事件触发*/}
                &nbsp;<button onClick={()=> this.pushShow(msgObj.id,msgObj.title)}>push查看</button>
                &nbsp;<button onClick={()=> this.replaceShow(msgObj.id,msgObj.title)}>replace查看</button>
              </li>
            )
          })
        }
      </ul>
      <hr/>
      {/* 声明接收params参数 */}
      {/* <Route path="/home/message/detail/:id/:title" component={Detail}/> */}

      {/* search参数无需声明接收，正常注册路由即可 */}
      {/* <Route path="/home/message/detail" component={Detail}/> */}

      {/* state参数无需声明接收，正常注册路由即可 */}
      <Route path="/home/message/detail" component={Detail}/>

      <button onClick={this.back}>回退</button>&nbsp;
      <button onClick={this.forward}>前进</button>&nbsp;
      <button onClick={this.go}>go</button>

    </div>
  )
}
```

### withRouter 普通组件操作路由

只有路由组件有`history`对象，一般组件需要通过`withRouter`函数：

```jsx
import {withRouter} from 'react-router-dom'

export default withRouter(Header) // 暴露的是一个withRouter包装后的组件
//withRouter可以加工一般组件，让一般组件具备路由组件所特有的API
//withRouter的返回值是一个新组件
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://yangsx95.gitbook.io/notes/front-end/react/lu-you.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
