react路由、NavLink组件封装
-
react路由其实是借助BOM中的History对象实现的,专门有一个history.js的库,可以让操作history对象简单起来。
用history.js可以通过两种方式创建路由对象:
1、History.createBrowserHistory() // 直接使用h5推出的history身上的api (有些老的浏览器会不支持)
2、History.createHashHistory() // 直接使用原生的 hash值(锚点) (所有浏览器都支持)
创建好的对象上有 push、replace、back、forward方法,可以操作路由
historyObj.listen( location => { console.log(location) }); 监听路由变化
react路由的插件库是 react-router-dom
下面以react-router-dom@5实验
整个应用需要用一个路由器包裹,否则每个路由器中的路由是独立的
import React from 'react' import ReactDOM from 'react-dom' import { BrowserRouter } from 'react-router-dom' import App from './App' ReactDOM.render(<BrowserRouter><App /></BrowserRouter>, document.getElementById('root'))
基本的路由配置
import React from 'react' import { Link, Route } from 'react-router-dom' import Home from './components/Home' import About from './components/About' import './App.css' // 创建外壳组件App class App extends React.Component { render() { return ( <div className="container"> <h1>react-router-dom-demo</h1> <div className="main"> <aside className="aside"> { /** * 编写路由链接 * 在react中,靠路由连接切换组件 */} <Link className="btn" to="/home">home</Link> <Link className="btn" to="/about">about</Link> </aside> <div className="content"> {/** * 注册路由 */} <Route path="/home" component={Home}></Route> <Route path="/about" component={About}></Route> </div> </div> </div> ) } } export default App
路由组件和一般组件的区别?
1、接收props不同
路由组件会收到路由传给组件的props:history、location、match
2、写法不同 <Demo/> <Route path="/demo" component={Demo} />
3、存放位置不同
一般组件放在components文件下,路由组件存放在pages或views文件下,名字自己定义
NavLink组件的使用:
import { NavLink } from 'react-router-dom'
<NavLink className="btn" activeClassName="active" to="/home">home</NavLink>
NavLink有个activeClassName属性,用来指定当前选中的类名,默认是active,如果是active可以省略
NavLink组件的封装:
NavLink 上需要传activeClassName 和 class,一个项目中的NavLink组件几乎传的这两个class都一样,把它封装成一个组件,就不用每个都传一遍了
import React, { Component } from 'react'
import { NavLink } from 'react-router-dom'
export default class MyNavLink extends Component {
render() {
// const { title, children } = this.props;
return (
// <NavLink className="btn" activeClassName="active" to={to}>{title}</NavLink>
// <NavLink className="btn" activeClassName="active" {...this.props}>{children}</NavLink>
// 标签体内容是一个特殊的标签属性(children)
// <NavLink className="btn" activeClassName="active" {...this.props} children={children} />
// children也包含在了this.props中,children={children}可以省略
<NavLink className="btn" activeClassName="active" {...this.props} />
)
}
}
使用:
<MyNavLink to="/home">home</MyNavLink>
Switch的使用
<Switch>
<Route path="/home" component={Home}></Route>
<Route path="/about" component={About}></Route>
<Route path="/about" component={Test}></Route>
</Switch>
上面路由/about 对应了两个组件,如果未用Switch标签包裹起来,/about会同时展示 About、Test两个组件,用Switch标签包起来后,路由匹配到第一个组件后,就不会再往下匹配,/about只会匹配About组件。
BrowserRouter模式下,第三方css样式丢失问题,如果多级路由,css文件是用./相对路径引入的话,刷新页面会出现样式丢失的问题,因为相对路径会把路由的路径带上去,导致在public目录页下找不到资源,找不到资源的情况下会默认把index.html返回,解决办法有:1、把./相对路径改成绝对路径/,2、换成hash路由
路由的模糊匹配与精准匹配:
路由默认是模糊匹配
<MyNavLink to="/home/a/b">home</MyNavLink>
可以匹配
<Route path="/home" component={Home}></Route>
开启精准匹配:
<Switch>
<Route exact path="/home" component={Home}></Route>
<Route path="/about" component={About}></Route>
<Route exact={true} path="/home/a/b" component={Test}></Route>
</Switch>
精准匹配不要随便开启,只有不开精确匹配会导致错误时,才开启精确匹配,(开启精确匹配后,将无法访问子路由)
路由重定向 Redirect 的使用:
import { Link, NavLink, BrowserRouter, Route, Switch, Redirect } from 'react-router-dom'
<Switch>
<Route path="/home" component={Home}></Route>
<Route path="/about" component={About}></Route>
<Route path="/home" component={Test}></Route>
<Redirect to="/home"></Redirect>
</Switch>
Redirect写在注册路由的最下方,当没有匹配到任何路由时,会重定向到 Redirect指定的路由
嵌套路由:
在/home路由下写两个子路由
import React, { Component } from 'react'
import { Route, Redirect, Switch } from 'react-router-dom';
import MyNavLink from '../../components/MyNavLink';
import News from './News'
import Message from './Message'
export default class Home extends Component {
render() {
console.log('home组件收到的props是:', this.props);
return (
<div>
<h1>我是Home的内容</h1>
<ul className="tab">
<li className="tab-items">
<MyNavLink to="/home/news">news</MyNavLink>
</li>
<li className="tab-items">
<MyNavLink to="/home/message">message</MyNavLink>
</li>
</ul>
{/** 注册路由 */}
<Switch>
<Route path="/home/news" component={News}></Route>
<Route path="/home/message" component={Message}></Route>
<Redirect to="/home/news"></Redirect>
</Switch>
</div>
)
}
}
向路由组件传递params参数:
1、传递
2、声明
3、接收
import React, { Component } from 'react'
import { Link, NavLink, BrowserRouter, Route, Switch, Redirect } from 'react-router-dom'
import Detail from './Detail';
export default class Message extends Component {
state = {
messageArr: [
{ id: '01', title: '消息1' },
{ id: '02', title: '消息2' },
{ id: '03', title: '消息3' },
]
}
render() {
const { messageArr } = this.state;
return (
<div>
<ul>
{
messageArr.map(msgObj => {
return (
<li key={msgObj.id}>
{/** 向路由组件传递params参数 */}
<Link to={`/home/message/detail/${msgObj.id}/${msgObj.title}`}>{msgObj.title}</Link>
</li>
)
})
}
</ul>
<hr />
{/** 声明接收params参数 */}
<Route path="/home/message/detail/:id/:title" component={Detail}></Route>
</div>
)
}
}
接收
import React, { Component } from 'react'
const Detaildata = [
{ id: '01', content: '你好,中国' },
{ id: '02', content: '你好,尚硅谷' },
{ id: '03', content: '你好,未来的自己' },
]
export default class Detail extends Component {
render() {
console.log(this.props, 'detail的props');
// 接收params参数
const { id, title } = this.props.match.params;
const findResult = Detaildata.find(item => item.id === id);
return (
<ul>
<li>ID: {id}</li>
<li>TITLE: {title}</li>
<li>CONTENT: {findResult.content}</li>
</ul>
)
}
}
向路由传递search参数:
1、传递search参数无需声明
2、接收search参数时需要把查询字符串转换成key-value形式的对象,取值方便,这里借用了一个库 qs 老版本用 querystring,是react脚手架自带的库;
{/* 向路由组件传递search参数 */}
<Link to={`/home/message/detail?id=${msgObj.id}&title=${msgObj.title}`}>{msgObj.title}</Link>
{/* search参数无需声明接收 */}
<Route path="/home/message/detail" component={Detail}></Route>
接收search参数
import qs from 'qs' // urlencode和object转换
const { search } = this.props.location;
const { id, title } = qs.parse(search.slice(1));
向路由组件传递state参数:
1、传递state参数,需要是个对象的形式
2、接收时再props.location.state中获取
{/* 向路由传递state参数 刷新页面时state参数不会丢失,因为state维护在history对象上,如果换了浏览器,此参数会丢失 */}
<Link to={ { pathname: '/home/message/detail', state: { id: msgObj.id, title: msgObj.title } } }>{msgObj.title}</Link>
{/* state参数无需声明接收 */}
<Route path="/home/message/detail" component={Detail}></Route>
// 接受state参数
const { id, title } = this.props.location.state || {};
路由开启replace模式:
路由默认是push模式,会留下记录,可以前进后退,如果开启replace模式,就不会有记录了
<Link replace to={ { pathname: '/home/message/detail', state: { id: msgObj.id, title: msgObj.title } } }>{msgObj.title}</Link> // 或者 <Link replace={true} to={ { pathname: '/home/message/detail', state: { id: msgObj.id, title: msgObj.title } } }>{msgObj.title}</Link>
编程式路由导航:
import React, { Component } from 'react'
import { Link, NavLink, BrowserRouter, Route, Switch, Redirect } from 'react-router-dom'
import Detail from './Detail';
export default class Message extends Component {
state = {
messageArr: [
{ id: '01', title: '消息1' },
{ id: '02', title: '消息2' },
{ id: '03', title: '消息3' },
]
}
// repalce跳转
replaceShow = (id, title) => {
// replace跳转+携带params参数
// this.props.history.replace(`/home/message/detail/${id}/${title}`)
// replace跳转+携带query参数
// this.props.history.replace(`/home/message/detail?id=${id}&title=${title}`)
// replace跳转+携带state参数
this.props.history.replace(`/home/message/detail`, { id, title })
}
// push跳转
pushShow = (id, title) => {
// push跳转+携带params参数
// this.props.history.push(`/home/message/detail/${id}/${title}`)
// push跳转+携带query参数
// 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()
}
// 前进
forword = () => {
this.props.history.goForward()
}
// go
go = () => {
this.props.history.go(-1); // 负数代表后退几步,正数代表前进几步
}
render() {
const { messageArr } = this.state;
return (
<div>
<ul>
{
messageArr.map(msgObj => {
return (
<li key={msgObj.id}>
{/** 向路由组件传递params参数 */}
<Link to={`/home/message/detail/${msgObj.id}/${msgObj.title}`}>{msgObj.title}</Link>
{/* 向路由组件传递search参数 */}
{/* <Link to={`/home/message/detail?id=${msgObj.id}&title=${msgObj.title}`}>{msgObj.title}</Link> */}
{/* 向路由传递state参数 刷新页面时state参数不会丢失,因为state维护在history对象上,如果换了浏览器,此参数会丢失 */}
{/* <Link replace to={ { pathname: '/home/message/detail', state: { id: msgObj.id, title: msgObj.title } } }>{msgObj.title}</Link> */}
<button onClick={() => this.pushShow(msgObj.id, msgObj.title)}>push查看</button>
<button onClick={() => this.replaceShow(msgObj.id, msgObj.title)}>replace查看</button>
</li>
)
})
}
</ul>
<hr />
{/** 声明接收params参数 */}
{/* <Route path="/home/message/detail/:id/:title" component={Detail}></Route> */}
{/* search参数无需声明接收 */}
{/* <Route path="/home/message/detail" component={Detail}></Route> */}
{/* state参数无需声明接收 */}
<Route path="/home/message/detail" component={Detail}></Route>
<hr />
<button onClick={ this.back }>回退</button>
<button onClick={ this.forword }>前进</button>
<button onClick={ this.go }>go</button>
</div>
)
}
}
withRouter的使用:
一般组件像使用路由跳转的功能,需要用withRouter加工一下,使一般组件拥有路由组件的api
import React, { Component } from 'react'
import { withRouter } from 'react-router-dom'
class Header extends Component {
// 回退
back = () => {
this.props.history.goBack()
}
// 前进
forword = () => {
this.props.history.goForward()
}
// go
go = () => {
this.props.history.go(-1); // 负数代表后退几步,正数代表前进几步
}
render() {
console.log(this.props, 'Header组件的props');
return (
<h3>
react-router-dom-demo
<button onClick={ this.back }>回退</button>
<button onClick={ this.forword }>前进</button>
<button onClick={ this.go }>go</button>
</h3>
)
}
}
// withRouter可以加工一般组件,让一般组件拥有路由组件特有的api(props:{ history, location, match })
// withRouter的返回值是一个新组件
export default withRouter(Header)
BrowserRouter和HashRouter的区别:

-

浙公网安备 33010602011771号