Router
组件会创建一个上下文,并且向上下文中注入一些信息(该信息对开发者是隐藏的)。
当 Route
组件匹配到了路径,则会将这些上下文信息作为 props
传入对应的组件。
function A(props) {
console.log(props);
return <h1>A组件</h1>;
}
function B() {
return <h1>B组件</h1>;
}
export default function App() {
return (
<Router>
<Route path="/a" component={A}></Route>
<Route path="/b" component={B}></Route>
</Router>
);
}
当访问 http://localhost:5173/a
会输出:
并不是 window.history
对象。主要利用 history
来实现无刷新跳转路由。
function A(props) {
console.log(props);
return (
<h1
onClick={() => {
props.history.push("/b");
}}
>
跳转路由/b
</h1>
);
}
push
也可以直接传递 search 参数:会从当前地址,跳转到带参数的地址。
props.history.push("?a=c");
其他的方法 replace
,go
,forward
,back
不多赘述。
window.history
不支持 Hash 模式。window.history.pushState
等方法的跳转,React 无法感应到地址变化,所以无法重新渲染页面。和上面的 history.location
是同一个对象,放到外层更方便使用。
props.history.location === props.location // true
该对象记录了当前地址的相关信息,不过一般不使用它。如果要解析地址栏的数据,会用第3方库比如 query-string。
重点!
该对象保存了,匹配路由的相关信息。几个重要的属性:
当前路径和路由配置的路径,是否完全匹配。比如:
<Route path="/a" component={A}>Route>
/a
,isExact: true
,(此时 Route.exact = false
)/a/b/c
,isExact: false
,注意区分和
Route
组件中的exact
,它们并没有直接关系。
获取路径规则中对应的数据。
在使用 Route.path
属性时,除了直接写路径外,还可以写一个 string pattern
(字符串正则):
<Route path="/a/:id" component={A}></Route>
此时,只有访问 /a/xxx
时才会匹配到 A 组件。
path="/news/:year/:month/:day"
则路径必须是 /news/xx/xx/xx
才能匹配到对应路由。
path="/news/:year?/:month/:day"
则路径必须是 /news/xx/xx/xx
或 /news/xx/xx
。
path="/news/:year(\d+)/:month(\d+)/:day(\d+)"
则路径必须是 /news/11/22/33
。
path="/news/:year(\d+)/*"
则路径必须是 /news/11/xx
。这个 xx
必须存在,可以是任意合法字符。
/
path="/news-:year=:month"
则路径必须是 /news-xx=xx
,比如 /news-12=34
react-router 使用了第3方库 path-to-regexp 来将字符串正则 转换为真正的正则表达式。
下面4种方式,刷新页面数据都不会丢失。
history.push()
,第2个参数可传参,可在 location.state
中获取数据。
search
传参,通过第3方库 query-string 获取数据。
hash
传参,同样可使用 query-string 获取数据。
params
传参,可在 match.params
中获取数据。
文章一开始就说了,只有当 Route 组件匹配到了路径,对应的组件才会有路由的上下文信息。
那非路由组件如何获取这些信息?
withRouter
。将目标组件传递,它会返回一个新组件,新组件将向目标组件注入路由信息。路由的上下文信息,对开发者是隐藏的。而在 React-router 自己提组件
withRouter
中是可以获取的。
import { BrowserRouter as Router, Route, withRouter } from "react-router-dom";
function A() {
return (
<>
<h1>A组件</h1>
<B />
</>
);
}
function B() {
return (
<>
<h1>B组件</h1>
<CWrapper />
</>
);
}
function C(props) {
console.log(props);
return <h1>C组件</h1>;
}
const CWrapper = withRouter(C);
export default function App() {
return (
<Router>
<Route path="/a" component={A}></Route>
</Router>
);
}
注意,
withRouter
的组件参数,必须是Router
组件的子元素,这样才能获取到路由的上下文信息注入到目标组件中。比如 React路由守卫实现 时就遇到了这样的问题。
以上。