一、React-Router @V5
快速开始
安装
npm i react-router-dom@5
示例:基本路由
import React from "react"
import { BrowserRouter as Router, Route, Link } from "react-router-dom"
function Index(){
return <h2>Home</h2>
}
function About(){
return <h2>About</h2>
}
function Users(){
return <h2>Users</h2>
}
export default function App() {
return (
<Router>
<div>
<nav>
<ul>
<li>
<Link to='/'>Home</Link>
</li>
<li>
<Link to='/about'>About</Link>
</li>
<li>
<Link to='/users'>Users</Link>
</li>
</ul>
</nav>
<Route path='/' exact component={Index}></Route>
<Route path='/about/' component={About}></Route>
<Route path='/users/' component={Users}></Route>
</div>
</Router>
)
}
示例:嵌套路由
这个示例向我们展示来嵌套路由如何工作,路由 '/topics'会加载Topics组件,这个组件会通过':id'的路由来渲染出更多的内容。
import React from 'react'
import {
BrowserRouter as Router,
Route,
Link
} from "react-router-dom"
function Home(){
return <h2>Home</h2>
}
function About(){
return <h2>About</h2>
}
function Topic({match}){
return <h3>Requested Param:{match.params.id}</h3>
}
function Topics({ match }) {
return (
<div>
<h2>Topics</h2>
<ul>
<li>
<Link to={`${match.url}/components`}>Components</Link>
</li>
<li>
<Link to={`${match.url}/props-v-state`}>Props v. State</Link>
</li>
</ul>
<Route path={`${match.path}/:id`} component={Topic} />
<Route
exact
path={match.path}
render={() => <h3>Please select a topic.</h3>}
/>
</div>
);
}
function Header() {
return (
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/topics">Topics</Link>
</li>
</ul>
);
}
export default function App() {
return (
<Router>
<div>
<Header/>
<Route exact path='/' component={Home}/>
<Route path='/about' component={About} />
<Route path='/topics' component={Topics} />
</div>
</Router>
)
}
基本组件
React Router包含三种类型的组件:路由组件、路由匹配组件、导航组件
在你使用这些组件之前,必须先导入他们
import { BrowserRouter, Route, Link } from "react-router-dom"
路由组件
任何一个拥有路由跳转的React应用的核心组件必须是一个路由组件。对于web项目来说,react-router-dom提供了两种路由组件:BrowserRouter、HashRouter,他们会为你创建一个history对象,通常来说,对于一个有服务器响应的web项目,那就使用BrowserRouter,如果你是用静态资源来提供服务的,那就使用HashRouter
import { BrowserRouter } from "react-router-dom";
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
holder
);
路由匹配组件
路由匹配组件有两种:<Route>、<Switch>
路由匹配组件通过匹配Route的path属性与当前页面的地址栏的路径来工作。当一个<Route>匹配成功时,她会渲染出对应的内容,当匹配不成功的时候,任何内容都不会被渲染出来。
当一个<Route>没有path属性时她对任何路径都会匹配成功。
你可以在你想要根据浏览器地址来渲染内容的任何地方使用<Route>,但是我们通常会把一组<Route>放在一起。<Switch>就是用来把多个<Route>组合在一起的。你可以在你想要根据浏览器地址来渲染内容的任何地方使用<Route>,但是我们通常会把一组<Route>放在一起。<Switch>就是用来把多个<Route>组合在一起的。
我们不是必须要用<Switch>把多个<Route>组合在一起,但是这种做法通常是有用的。 <Switch>将迭代其所有子<Route>元素,并仅渲染与当前路径匹配的第一个子元素。 她对于多个path匹配相同的路径、动画路由之间的转换、没有路径匹配时的识别(这样你就可以渲染“404”组件)是有很大帮助的。
import { Route, Switch } from "react-router-dom";
// when location = { pathname: '/about' }
<Route path='/about' component={About}/> // renders <About/>
<Route path='/contact' component={Contact}/> // renders null
<Route component={Always}/> // renders <Always/>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/contact" component={Contact} />
</Switch>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/contact" component={Contact} />
{/* when none of the above match, <NoMatch> will be rendered */}
<Route component={NoMatch} />
</Switch>
路由渲染属性
对于一个<Route>组件,你可以设置三种属性:component, render, children 来渲染出相应的内容。在这里我们只关注component和render,因为她们是经常会用到的
当你有一个已存在的组件(无论是一个React组件还是一个无状态的函数组件)想要渲染时应该使用component。当你必须传递一些参数变量给组件时应该用render属性,她采用内联函数的形式。你不应该使用component属性来渲染一个带有参数变量的内联函数组件,这会导致不必要的组件的挂载和卸载。
component的属性值不能为一个内嵌函数
<Route path="/somewhere" component={() => <Home />} /> // 错误的使用
<Route path="/somewhere" component={Home} /> // 正确使用
当使用component属性时,Route会用React.createElement创建一个子组件,这意味着给component传递一个内嵌函数时,Route会创建一个新组件,原来的组件会被卸载,然后挂载新组件,这样会有性能消耗。
想要给子组件传递一些额外的参数,请使用render
export default function App() {
return (
<Router>
<Switch>
{/* these are good */}
<Route exact path="/" component={Home} />
<Route
path="/about"
render={props => <About {...props} extra={someVariable}/>}
/>
{/* do not do this */}
<Route
path="/contact"
component={props => <Contact {...props} extra={someVariable}/>}
/>
</Switch>
</Router>
)
}
导航组件
React Router提供了<Link>组件用来在你的应用中创建超链接,<Link>会在你的页面的任何地方被渲染成a标签。
<NavLink>是一种特殊的<Link>,当他的to属性匹配地址栏上的路径时,她渲染成的<a>标签会带有'active'的样式。
如果你想要强制跳转,你可以使用<Redirect>。当一个<Redirect>组件被渲染时,她会导航到其to属性匹配的路径。
<Link to="/">Home</Link>
// <a href='/'>Home</a>
// location = { pathname: '/react' }
<NavLink to="/react" activeClassName="hurray">
React
</NavLink>
// <a href='/react' className='hurray'>React</a>
<Redirect to="/login" />
路由的严格匹配与模糊匹配
- 默认使用的时模糊匹配(Route的path属性值是地址栏上的一部分,且顺序要一致)
- 开启严格匹配:
<Route exact path="/about" component={About} /> - 不要随便开启严格匹配,需要时再开,有些时候开启会导致无法继续匹配二级路由
路由传参的三种方式
方式一:向路由组件传递params参数
1. 在导航组件中将params参数拼接在路径后
<Link to={`/home/message/detail/${id}/${title}`}>点击</Link>
2.在路由匹配组件中声明接收 路径/:id/:title
<Route path='/home/message/detail/:id/:title' component={Detail}></Route>
3.在路由组件中props的match属性里拿到params参数
const [id,title] = props.match.params
注意:传递params参数时,一定要在Route的path路径后声明接收,否则在第三步拿不到params值
方式二:传递search参数
1.在路径后拼接query参数
<Link to={`/home/message/detail?id=${id}&title=${title}`}>点我</Link>
2.在路由组件的props里的location对象中接收
方式三:传递state参数
1.在路由链接中加上state属性
<Link to={{ pathname:'/home/message/detail', state:{id, title}}}>点我</Link>
2. 在路由组件中接收
const [id,title] = props.location.state
总结三种传参方式
编程式路由导航
props.history.push(path, state) //保存历史记录
props.history.replace(path, state) //替换历史记录
props.history.goForward() //前进一步
props.history.goBack() //后退一步
props.history.go(steps) // steps>0 前进steps步 steps<0 后退steps步
withRouter
withRouter可以加工一般组件,让一般组件具备路由组件所特有的API,向一般组件内传入以下属性
BrowserRouter与HashRouter的区别
-
底层原理不一样:
BrowserRouter使用的是H5的history API,不兼容IE9及以下版本
HashRouter使用的是URL的哈希值
-
url表现形式不一样
BrowserRouter的路径中没有#,HashRouter的路径中包含#
-
刷新后对路由state参数的影响
BrowserRouter没有任何影响,因为state保存在history对象中
HashRouter刷新后会导致路由state参数的丢失
-
备注:HashRouter可以用于解决一些路径错误相关的问题
二、React Router @V6
安装
$ npm install react-router-dom@6
概述
-
React Router以三个不同的包发布到npm上,它们分别为:
- react-router: 路由的核心库,提供了很多的:组件、钩子
- react-router-dom: 包含react-router所有内容,并添加一些专门用于DOM的组件,例如
<BrowserRouter> - react-router-native: 包括react-router所有内容,并添加一些专门用于ReactNative的API,例如:
NativeRouter等
-
与React Router 5.x版本相比,改变了什么?
- 内置组件的变化:移出
<Switch>,新增<Routes> - 语法的变化:
component={About}变为element={<About/>}等 - 新增多个hook:
useParams、useNavigate、useMatch等
- 内置组件的变化:移出
示例
//index.js
root.render(
<BrowserRouter>
<App />
</BrowserRouter>
);
//App.js
import React from 'react'
import { Routes, Route, Link } from 'react-router-dom'
export default function App() {
return (
<div>
<h1>Welcome to React Router!</h1>
<Routes>
<Route path='/' element={<Home />} />
<Route path='about' element={<About />} />
</Routes>
</div>
)
}
function Home(){
return (
<>
<main>
<h2>Welcome to the homepage!</h2>
<p>You can do this, I believe in you.</p>
</main>
<nav>
<Link to="/about">About</Link>
</nav>
</>
)
}
function About() {
return (
<>
<main>
<h2>Who are we?</h2>
<p>
That feels like an existential question, don't you
think?
</p>
</main>
<nav>
<Link to="/">Home</Link>
</nav>
</>
);
}
<Navigate>
- 作用: 只要
<Navigate>组件被渲染,就会修改路径,切换视图 - replace属性用于控制跳转模式(push或replace,默认是push)
function Home() {
const [sum, setSum] = useState(1)
return (
<div>
个人
{/* 根据sum的值决定是否切换视图 */}
{sum === 2 ? <Navigate to='/about' /> : <h4>当前sum的值是:{sum}</h4> }
<button onClick={()=>setSum(v=>v+1)}>点我+1</button>
</div>
)
}
<Routes/> 与<Route/>
- v6版本中移出了先前
<Switch>,引入了新的替代者:<Routes/> <Routes/>和<Route/>要配合使用,且必须要用<Routes/>包裹<Route/><Route/>相当于一个if语句,如果其路径与当前URL匹配,则呈现其对应的组件<Route caseSensitive/>属性用于指定:匹配时是否区分大小写(默认不区分大小写)- 当URL发送变化时,
<Routes/>都会查看所有子<Route/>元素以找到最佳匹配并呈现组件 <Route/>也可以嵌套使用,且可配合useRoutes()配置‘路由表’,但需要通过<Outlet/>类似Vue中的<Route-view/>组件来渲染其子路由。<Outlet/>类似Vue中的<Route-view/>
<Outlet/>
- 当
<Route/>产生嵌套时,渲染其对应的后续子路由,类似Vue中的<Route-view/>
在V6中,函数式组件的props已经不能接收到history、location、match对象,要想实现接收路由参数、编程式导航需要用useNavigate、useParams、useSearchParams、useLocation
useNavigate() 编程式路由导航
- 作用:返回一个函数用来实现编程式导航,取代
history的路由跳转功能
const navigate = useNavigate()
navigate(path, options)
navigate(steps) steps>0 前进 steps<0 后退
useSearchParams() 获取search参数
- 作用:用于读取和修改当前位置的URL中的查询字符串
- 返回一个包含两个值的数组,内容分别为:当前的search参数、更新search的函数
const [search, setSearch] = useSearchParams()
const id = search.get('id')
const name = search.get('name')
useLocation() 获取state参数
- 作用:获取当前location信息,对标V5中路由组件的location属性
useParams() 获取params参数
useMatch()
- 作用:返回当前匹配信息,对标V5中路由组件的match属性