React笔记(七)
1.React路由理解
-
React实现的是SPA应用,即单页Web页面,整个应用中只有一个完整的页面,点击页面中的链接也不会刷新页面,只会对页面进行局部的刷新。因此在更换页面时需要使用路由实现。
-
路由就是一个键值对映射关系,键为访问路径,值为函数或者是组件,后端路由代表的是函数,用来处理不同的请求,前端路由代表的则是组件或页面,通过路径的不同,展示不同的页面内容。下面的这两个路径就代表了不同的页面。通过改变链接路径,来显示不同内容。
https://blog.csdn.net/nav/web
https://blog.csdn.net/nav/back-end
2.React-Router5基本使用
-
React内不提供内置的路由,但是可通过一个插件来完成react路由的使用。首先安装
react-router-dom
插件库。并进行使用。 -
在需要路由切换时,可以从下载的插件库中引入
Link
组件。代表着路由链接,实现组件切换。并且还需要将想要切换的路由组件注册在BrowserRouter
组件(代表着浏览器的路由)或者HashRouter
组件(使用锚点路由)内。// 引入BrowserRouter:路由;Link:跳转;Route:路由组件 import { BrowserRouter, Link, Route } from 'react-router-dom' export default function DemoR(props) { console.log('render'); // 在最外侧用路由组件包裹住,在实际开发中推荐在App.jsx中包裹住。 return <BrowserRouter> <div> {/* 点击link路由跳转,相当于HTML中的a标签 */} <Link to='/A'>点我看页面A</Link> <br/> <Link to='/B'>点我看页面B</Link> </div> {/* 将组件注册进路由,创建路径与组件的映射关系,component内放置组件,path内放置路径 */} <Route component={RouteA} path='/A'/> <Route component={RouteB} path='/B'/> </BrowserRouter> } // 需要注册进路由的组件 function RouteA(props){ console.log(props); return <p>我是页面A</p> } function RouteB(){ return <p>我是页面B</p> }
在上面示例中,通过点击了链接可以达到切换页面的效果,并且值得注意的是,主页面只进行了一次渲染(可以通过查看控制台来判断),并且页面的路径也随着点击而更改。
-
注册进路由的组件相对于一般组件,其存在着一定的区别
-
一般组件在使用时,直接引入组件对应的标签即可。而路由组件则需要将组件引入
<Route component={组件名} path='路径'/>
中的组件名处。 -
一般组件接收到的props是父组件传递了什么,子组件就会接收到什么。而路由组件中会接收到三个特定的属性,而这三个属性将在路由传值处尤为重要。
{ //浏览器的历史信息,包含有路由如何切换,路径信息等 "history": { "length": 13, "action": "PUSH", "location": { "pathname": "/A", "search": "", "hash": "", "key": "p3jxy2" } }, //当前的路径信息 "location": { "pathname": "/A", "search": "", "hash": "", "key": "p3jxy2" }, //匹配路径参数的对象 "match": { "path": "/A", "url": "/A", "isExact": true, "params": {} } }
-
-
相比于父子直接传值,由于注册进路由的组件没有以标签的形式被使用,因此传值与普通的父子传值不同。存在着三种传值方式。
-
params传值,在路由链接中携带参数,并在注册路由时进行声明。当路由组件接收参数时,参数会存在于
props.match.params
中。这一传值方式的优势是,即使刷新地址栏,参数依然存在;缺点是:只可以传递字符串,并且传值过多时,url会比较长。 -
search传值,类似于get请求中的filter或select参数,其在注册时无需声明。接收到的参数会存在于
props.location.search
中。在使用时由于是urlencode
后的值,因此需要借助querystring
进行解析。优缺点同params传值。 -
state传值,在路由链接中携带参数to做为需要传递的对象,注册时也无需声明,接收到的参数会存储在
props.location.search
中。优点很明显,可以传递对象,缺点则是重新打开页面时,传递的state会丢失,但刷新不会丢失(仅限BrowserRouter
)。//引入queryString const queryString = require('query-string'); export default function DemoR(props) { const obj = { id:1, name:'小明' }; console.log('render'); return <BrowserRouter> <div> {/* Params传值跳转方式,形为A/参数1/参数2…… */} <Link to={`/A/${obj.id}/${obj.name}`}>点我看页面A</Link> <br/> {/* Search传值跳转方式,形为A?参数1名=参数1&参数2名=参数2…… */} <Link to={`/B/?id=${obj.id}&name=${obj.name}`}>点我看页面B</Link> <br/> {/* State传值跳转方式,to传递一个pathname与state组成的对象 */} <Link to={{pathname:`/C`,state:obj}}>点我看页面C</Link> </div> {/* Params传值注册方式,形为A/:参数1名/:参数2名…… */} <Route component={RouteA} path='/A/:id/:name'/> <Route component={RouteB} path='/B'/> <Route component={RouteC} path='/C'/> </BrowserRouter> } function RouteA(props){ // 直接接收传值 return <> <h3>我是页面A,params传值,接收到的参数为</h3> <p>id:{props.match.params.id}</p> <p>name:{props.match.params.name}</p> </> } function RouteB(props){ // 使用queryString处理后再接收 var obj = queryString.parse(props.location.search); return <> <h3>我是页面B,search传值,接收到的参数为</h3> <p>id:{obj.id}</p> <p>name:{obj.name}</p> </> } function RouteC(props){ // 直接接收传值 return <> <h3>我是页面C,state传值,接收到的参数为</h3> <p>id:{props.location.state.id}</p> <p>name:{props.location.state.name}</p> </> }
-
-
在props中存在着action,代表着路由跳转是
push
行为还是replace
行为。push代表着在路由跳转时以添加的方式添加进浏览器的历史中,即点击浏览器的后退按钮会返回上一个页面,默认为push。replace则代表替换掉了上一个页面,点击后退按钮不会返回到上一个页面。 -
当需要使用嵌套路由时,即多级的路由时,这时可以直接书写双层路由,但要注意的是,注册子路由时需要写上父路由的值,路由的匹配则是按照注册路由的顺序进行的。
export default function DemoR(props) { return <BrowserRouter> <div> <Link to='/A'>点我显示A页面</Link> </div> <Route component={RouteA} path='/A'/> {/* 定义嵌套子路由 */} <Route component={RouteB} path='/A/BChild'/> <Route component={RouteC} path='/A/CChild'/> </BrowserRouter> } function RouteA(){ return <> <h3>我是页面A</h3> {/* 跳转嵌套子路由 */} <Link to='/A/BChild'>点我看B子路由</Link> <Link to='/A/CChild'>点我看C子路由</Link> </> } function RouteB(){ return <h3>我是页面B</h3> } function RouteC(){ return <h3>我是页面C</h3> }
3.React-Router5扩展使用
-
在使用了link之后,尽管可以实现点击link路由跳转,但是却无法查看当前点击了哪个按钮,如果通过操作state状态来完成按钮样式的更改,也可以实现。但React-Router中存在着一个组件即
NavLink
,其可以完成显示为当前所在的路由link提供特殊的样式。import { BrowserRouter, NavLink, Route } from 'react-router-dom' import './Demo.css' export default function DemoR(props) { console.log('render'); return <BrowserRouter> <div> {/* 通过为activeClassName赋予特定类名可令其在活性时改变样式 */} <NavLink to='/A' className='linkbox' activeClassName='active'>点我看页面A</NavLink> {/* 通过为activeStyle赋予特定样式可令其在活性时改变样式 */} <NavLink to='/B' className='linkbox' activeStyle={{backgroundColor:'yellow'}}>点我看页面B</NavLink> </div> <Route component={RouteA} path='/A'/> <Route component={RouteB} path='/B'/> </BrowserRouter> } function RouteA(props){ console.log(props); return <p>我是页面A</p> } function RouteB(){ return <p>我是页面B</p> }
-
由于React-Router匹配路由时是从头到尾逐个匹配,因此当路由多时,效率减低,因此可以使用
Switch
将注册的路由组件包裹起来,一旦匹配到后就停止继续匹配,提高效率。但需要注意的是,由于一旦匹配到就停止继续匹配,因此要将父路由放于子路由的下面,因为当父路由一旦匹配上时,就会停止继续匹配,画面显示错误。并且如果需要嵌套时,则需要在父路由组件中用Switch包裹子路由。export default function DemoR(props) { return <BrowserRouter> <div> <Link to='/A'>点我显示A页面</Link> </div> <Switch> {/* <Route component={RouteB} path='/A/BChild'/> <Route component={RouteC} path='/A/CChild'/> */} <Route component={RouteA} path='/A'/> </Switch> </BrowserRouter> } function RouteA(){ return <> <h3>我是页面A</h3> <Link to='/A/BChild'>点我看B子路由</Link> <Link to='/A/CChild'>点我看C子路由</Link> <Switch> <Route component={RouteB} path='/A/BChild'/> <Route component={RouteC} path='/A/CChild'/> </Switch> </> } function RouteB(){ return <h3>我是页面B</h3> } function RouteC(){ return <h3>我是页面C</h3> }
另外,在实际书写时,也可以为特定的Route标签添加
exact
属性来实现精确匹配(只有路径完全一样才行)。 -
在路由书写不规范时,想让其跳转到一个默认页,这时可以使用
Redirect
重定向路由,使用方法为只需要为注册的部分添加<Redirect to='/A'/>
,并将其放在所有路由注册的最下方,即可完成重定向。为to属性赋的值为重定向跳往的目标链接。 -
编程式路由导航可以作为在组件内完成某些事务中,进行页面跳转,又或者是在一般组件内进行页面跳转,这时需要使用到
withRouter
这个高阶组件来将一般组件转换为路由组件。并且还需要借助于路由组件上props.history
的五个方法,go
方法代表前几页,replace
完成replace方式的路由跳转,push
完成push方式的路由跳转,goBack
回退函数,goForward
前进函数。export default function DemoR(props) { return <BrowserRouter> <div> <Link to='/A'>点我显示A页面</Link> </div> <Switch> <Route component={RouteA} path='/A'/> <Route component={RouteB} path='/B'/> <Redirect to='/A'/> </Switch> </BrowserRouter> } function RouteA(){ return <> <h3>我是页面A</h3> <RouteC/> </> } function RouteB(){ return <h3>我是页面B</h3> } // 将一般组件转换为路由组件 const RouteC = withRouter(ComponentC); function ComponentC(props){ // 调用方法进行编程式路由跳转 return <button onClick={()=>props.history.push('/B')}>点我跳转至页面B</button> }
4.React-Router6的变化
-
内置组件移除了
<Switch/>
标签,新增<Routes/>
标签,并且原本注册路由的标签内书写方式也进行了更改,可以为Route标签指定caseSensitive
属性来配置匹配时是否区分大小写。{/* 原写法 */} <Switch> <Route path="/A" component={RouteA}> </Switch> {/* 新写法,去掉component属性,添加element属性,并为其传递标签 */} <Routes> <Route path="/A" element={<RouteA/>}> </Routes>
-
在重定向时写法也进行了一定改变,并可以使用replace属性调节跳转模式。
{/* 原写法 */} <Redirect to='/A'/> {/* 新写法,为element属性传递Navigate标签进行重定向,Navigate标签书写方式与原写法相像 */} <Route path='/' element={<Navigate to='/A'/>}>
-
NavLink高亮样式的设置也进行了更改。通过一个回调函数进行设置
<NavLink to='/A' className={(isActive)=>{return isActive?'active link':'link'}}>点我看页面A</NavLink>
-
允许使用useRoutes方法加载路由表。并可以将嵌套路由配置在父路由的Children属性中。
-
在三种传值方式中,可以分别使用
useParams
,useSearchParams
,useLocation
进行解构赋值,获取到传给路由组件的值。 -
可以使用
useNavigate
进行编程式路由导航,其具备两个参数,第一个参数为目标链接地址,第二个参数为配置项目,包含有替换模式与State。
参考文章