路由
# React-Router的实现原理是什么?
客户端路由实现的思想:
基于
hash的路由:通过监听hashchange事件,感知hash的变化- 改变
hash可以直接通过location.hash=xxx
- 改变
基于
H5 history路由:- 改变
url可以通过history.pushState和resplaceState等,会将URL压入堆栈,同时能够应用history.go()等API - 监听
url的变化可以通过自定义事件触发实现
- 改变
react-router 实现的思想:
- 基于
history库来实现上述不同的客户端路由实现思想,并且能够保存历史记录等,磨平浏览器差异,上层无感知 - 通过维护的列表,在每次
URL发生变化的回收,通过配置的 路由路径,匹配到对应的Component,并且render
# 如何配置 React-Router 实现路由切换
1. 使用<Route> 组件
路由匹配是通过比较 <Route> 的 path 属性和当前地址的 pathname 来实现的。当一个 <Route> 匹配成功时,它将渲染其内容,当它不匹配时就会渲染 null。没有路径的 <Route> 将始终被匹配。
// when location = { pathname: '/about' }
<Route path='/about' component={About}/> // renders <About/>
<Route path='/contact' component={Contact}/> // renders null
<Route component={Always}/> // renders <Always/>
2
3
4
2. 结合使用 <Switch> 组件和 <Route> 组件
<Switch> 用于将 <Route> 分组。
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/contact" component={Contact} />
</Switch>
2
3
4
5
<Switch> 不是分组 <Route> 所必须的,但他通常很有用。 一个 <Switch> 会遍历其所有的子 <Route>元素,并仅渲染与当前地址匹配的第一个元素。
3. 使用 <Link>、 <NavLink>、<Redirect> 组件
<Link> 组件来在你的应用程序中创建链接。无论你在何处渲染一个<Link> ,都会在应用程序的 HTML 中渲染锚(<a>)。
<Link to="/">Home</Link>
// <a href='/'>Home</a>
2
是一种特殊类型的 当它的 to 属性与当前地址匹配时,可以将其定义为"活跃的"。
// location = { pathname: '/react' }
<NavLink to="/react" activeClassName="hurray">
React
</NavLink>
// <a href='/react' className='hurray'>React</a>
2
3
4
5
当我们想强制导航时,可以渲染一个<Redirect>,当一个<Redirect>渲染时,它将使用它的to属性进行定向。
# React-Router怎么设置重定向?
使用<Redirect>组件实现路由的重定向:
<Switch>
<Redirect from='/users/:id' to='/users/profile/:id'/>
<Route path='/users/profile/:id' component={Profile}/>
</Switch>
2
3
4
当请求 /users/:id 被重定向去 '/users/profile/:id':
- 属性
from: string:需要匹配的将要被重定向路径。 - 属性
to: string:重定向的URL字符串 - 属性
to: object:重定向的location对象 - 属性
push: bool:若为真,重定向操作将会把新地址加入到访问历史记录里面,并且无法回退到前面的页面。
# react-router 里的 Link 标签和 a 标签的区别
从最终渲染的 DOM 来看,这两者都是链接,都是标签,区别是∶ <Link>是react-router 里实现路由跳转的链接,一般配合<Route> 使用,react-router接管了其默认的链接跳转行为,区别于传统的页面跳转,<Link> 的“跳转”行为只会触发相匹配的<Route>对应的页面内容更新,而不会刷新整个页面。
<Link>做了3件事情:
- 有
onclick那就执行onclick click的时候阻止a标签默认事件- 根据跳转
href(即是to),用history(web前端路由两种方式之一,history&hash)跳转,此时只是链接变了,并没有刷新页面而<a>标签就是普通的超链接了,用于从当前页面跳转到href指向的另一 个页面(非锚点情况)。
a标签默认事件禁掉之后做了什么才实现了跳转?
let domArr = document.getElementsByTagName('a')
[...domArr].forEach(item=>{
item.addEventListener('click',function () {
location.href = this.href
})
})
2
3
4
5
6
# React-Router 如何获取URL的参数和历史对象?
1. 获取URL的参数
get传值
路由配置还是普通的配置,如:'admin',传参方式如:'admin?id='1111''。通过this.props.location.search获取url获取到一个字符串'?id='1111' 可以用url,qs,querystring,浏览器提供的api URLSearchParams对象或者自己封装的方法去解析出id的值。
- 动态路由传值
路由需要配置成动态路由:如path='/admin/:id',传参方式,如'admin/111'。通过this.props.match.params.id 取得url中的动态路由id部分的值,除此之外还可以通过useParams(Hooks)来获取
- 通过
query或state传值
传参方式如:在Link组件的to属性中可以传递对象{pathname:'/admin',query:'111',state:'111'};。通过this.props.location.state或this.props.location.query来获取即可,传递的参数可以是对象、数组等,但是存在缺点就是只要刷新页面,参数就会丢失。
2. 获取历史对象
- 如果
React >= 16.8时可以使用React Router中提供的Hooks
import { useHistory } from "react-router-dom";
let history = useHistory();
2
- 使用
this.props.history获取历史对象
let history = this.props.history;
# React-Router 4怎样在路由变化时重新渲染同一个组件?
当路由变化时,即组件的props发生了变化,会调用componentWillReceiveProps等生命周期钩子。那需要做的只是: 当路由改变时,根据路由,也去请求数据:
class NewsList extends Component {
componentDidMount () {
this.fetchData(this.props.location);
}
fetchData(location) {
const type = location.pathname.replace('/', '') || 'top'
this.props.dispatch(fetchListData(type))
}
componentWillReceiveProps(nextProps) {
if (nextProps.location.pathname != this.props.location.pathname) {
this.fetchData(nextProps.location);
}
}
render () {
...
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
利用生命周期componentWillReceiveProps,进行重新render的预处理操作。
# React-Router的路由有几种模式?
React-Router 支持使用 hash(对应 HashRouter)和 browser(对应 BrowserRouter) 两种路由规则, react-router-dom 提供了 BrowserRouter 和 HashRouter 两个组件来实现应用的 UI 和 URL 同步:
BrowserRouter创建的 URL 格式:xxx.com/pathHashRouter创建的 URL 格式:xxx.com/#/path
1. BrowserRouter
它使用 HTML5 提供的 history API(pushState、replaceState 和 popstate 事件)来保持 UI 和 URL 的同步。由此可以看出,BrowserRouter 是使用 HTML 5 的 history API 来控制路由跳转的:
<BrowserRouter
basename={string}
forceRefresh={bool}
getUserConfirmation={func}
keyLength={number}
/>
2
3
4
5
6
其中的属性如下:
basename所有路由的基准URL。basename的正确格式是前面有一个前导斜杠,但不能有尾部斜杠;
<BrowserRouter basename="/calendar">
<Link to="/today" />
</BrowserRouter>
2
3
等同于
<a href="/calendar/today" />
forceRefresh如果为true,在导航的过程中整个页面将会刷新。一般情况下,只有在不支持HTML5 history API的浏览器中使用此功能;getUserConfirmation用于确认导航的函数,默认使用window.confirm。例如,当从 /a 导航至 /b 时,会使用默认的 confirm 函数弹出一个提示,用户点击确定后才进行导航,否则不做任何处理;
// 这是默认的确认函数
const getConfirmation = (message, callback) => {
const allowTransition = window.confirm(message);
callback(allowTransition);
}
<BrowserRouter getUserConfirmation={getConfirmation} />
复制代码
2
3
4
5
6
7
需要配合
<Prompt>一起使用。
KeyLength用来设置Location.Key的长度。
2. HashRouter
使用 URL 的 hash 部分(即 window.location.hash)来保持 UI 和 URL 的同步。由此可以看出,HashRouter 是通过 URL 的 hash 属性来控制路由跳转的:
<HashRouter
basename={string}
getUserConfirmation={func}
hashType={string}
/>
2
3
4
5
其参数如下:
basename,getUserConfirmation和BrowserRouter功能一样;hashTypewindow.location.hash使用的hash类型,有如下几种:slash- 后面跟一个斜杠,例如#/和#/sunshine/lollipops;noslash- 后面没有斜杠,例如#和#sunshine/lollipops;hashbang-Google风格的ajax crawlable,例如#!/和#!/sunshine/lollipops。
# React-Router 4的Switch有什么用?
Switch 通常被用来包裹 Route,用于渲染与路径匹配的第一个子 <Route> 或 <Redirect>,它里面不能放其他元素。
假如不加 <Switch> :
import { Route } from 'react-router-dom'
<Route path="/" component={Home}></Route>
<Route path="/login" component={Login}></Route>
2
3
4
Route 组件的 path 属性用于匹配路径,因为需要匹配 / 到 Home,匹配 /login 到 Login,所以需要两个 Route,但是不能这么写。这样写的话,当 URL 的 path 为 “/login” 时,<Route path="/" />和<Route path="/login" /> 都会被匹配,因此页面会展示 Home 和 Login 两个组件。这时就需要借助 <Switch> 来做到只显示一个匹配组件:
import { Switch, Route} from 'react-router-dom'
<Switch>
<Route path="/" component={Home}></Route>
<Route path="/login" component={Login}></Route>
</Switch>
2
3
4
5
6
此时,再访问 “/login” 路径时,却只显示了 Home 组件。这是就用到了exact属性,它的作用就是精确匹配路径,经常与<Switch> 联合使用。只有当 URL 和该 <Route> 的 path 属性完全一致的情况下才能匹配上:
import { Switch, Route} from 'react-router-dom'
<Switch>
<Route exact path="/" component={Home}></Route>
<Route exact path="/login" component={Login}></Route>
</Switch>
2
3
4
5
6