1. 一级路由与多级路由
通过npm下载好'react-router-dom'后建立一个文件写这么一段结构:
<HashRouter>
<Switch>
<Route path={'/film'} component={Film} />
<Route path={'/cinema'} component={Cinema} />
<Route path={'/center'} component={Center} />
{/* 404页 */}
<Route component={notFound}/>
</Switch>
</HashRouter>
- HashRoute:最外层的结构,使得url自带/#,如:http://localhost:3000/#
- Switch:如果没有Switch组件,当一个path匹配到多个component时,会渲染出多个组件。一个路径在匹配到了对应的component之后,还会接着向下匹配 ,会大大的影响效率。
将Route组件包裹在Switch组件之中,当path匹配到一个component之后,将不会再想下继续匹配,提高了程序效率。 - Route:提供path参数,即url路径;component参数,代表对应路径要渲染的组件
2. 路由重定向
比如url不加后缀时需求自动跳转到film页面,可以使用Redirect重定向。
<HashRouter>
{props.children}
<Switch>
<Route path={'/film'} component={Film} />
<Route path={'/cinema'} component={Cinema} />
<Route path={'/center'} component={Center} />
{/* 重定向 精确匹配 */}
<Redirect from='/' to='/film' exact/>
{/* 404页 */}
<Route component={notFound}/>
</Switch>
</HashRouter>
语义比较清晰,简单实现了从无后缀跳转到film页
exact代表精确匹配 也就是url中必须刚好无后缀才跳转 不加的话所有路径都会跳转到film页 因为所有路径都以/开头
3. 嵌套路由
比如film页中嵌套两个小组件,正在上映和即将上映。
在组件中再写两个路由跳转以及若有需要写重定向即可:
export default function Film() {
return (
<div>
film
<Switch>
{/* 嵌套路由 */}
<Route path={'/film/nowplaying'} component={nowPlaying}></Route>
<Route path={'/film/comingsoon'} component={comingSoon}></Route>
{/* 嵌套重定向 */}
<Redirect from='/film' to='/film/nowplaying'></Redirect>
</Switch>
</div>
)
}
4. 声明式导航和编程式导航
比如需要实现一个tabbar组件,通过点击就可以实现跳转到不同页面,可以直接使用NavLink标签声明:
export default function tabBar() {
return (
<div>
<ul>
<li>
<NavLink to='/film'>film</NavLink>
</li>
<li>
<NavLink to='/cinema'>cinema</NavLink>
</li>
<li>
<NavLink to='/center'>center</NavLink>
</li>
</ul>
</div>
)
}
要注意的是NavLink必须放在Router标签中使用,因此可以在App.js中留下插槽,再将tabbar组件放进去。
export default class App extends Component {
render() {
return (
<div>
// 留下插槽
<AppRouter>
<TabBar></TabBar>
</AppRouter>
</div>
)
}
}
export default function indexRouter(props) {
return (
<div>
<HashRouter>
// 在此插入
{props.children}
<Switch>
<Route path={'/film'} component={Film} />
<Route path={'/cinema'} component={Cinema} />
<Route path={'/center'} component={Center} />
{/* 重定向 精确匹配 */}
<Redirect from='/' to='/film' exact/>
{/* 404页 */}
<Route component={notFound}/>
</Switch>
</HashRouter>
</div>
)
}
然后再尝试在正在热映组件中实现两种导航方式,比如现在已经请求到了一批电影的数据,要实现点击电影名字就跳转到对应页面
return (
<div>
{
list.map(item =>
<li key={item.filmId}>
{/* 用声明式导航实现跳转 */}
<NavLink to={'/detail/'+item.filmId}>{item.name}</NavLink>
</li>
)
}
</div>
)
也可以通过编程式导航实现
//编程式导航
const history = useHistory()
const changePage = (id) => {
//方法1 原生js方法
// window.location.href="#/detail/"+id
// 方法2 通过route传过来的props提供的history跳转
//props.history.push(`/detail/${id}`)
//方法3 通过react-router-dom提供的useHistory() 其实和方法2一样 写法不同而已
history.push(`/detail/${id}`)
}
return (
<div>
{
list.map(item =>
<li key={item.filmId} onClick={() => changePage(item.filmId)}>
{item.name}
</li>
)
}
</div>
)
5. 动态路由
第4点中实现了跳转,但是还没有实现接收到不同电影对应的不同filmId并进行相应处理,因此需要用到动态路由。
动态路由有两种方案:
建议使用方案一,方案二如果他人复制detail的url到浏览器中会报错,因为方案2需要一步一步跳转下来传参,其url是不带参数的;而方案一参数就在url内。
5.1 方案一
在path中加入/:xxx(xxx为自定义的名称)
<HashRouter>
{props.children}
<Switch>
<Route path={'/film'} component={Film} />
<Route path={'/cinema'} component={Cinema} />
<Route path={'/center'} component={Center} />
{/* 动态路由方案1 */}
<Route path={'/detail/:filmId'} component={Detail} />
{/* 重定向 精确匹配 */}
<Redirect from='/' to='/film' exact/>
{/* 404页 */}
<Route component={notFound}/>
</Switch>
</HashRouter>
history.push()通过模板字符串拼接起来即可
history.push(`/detail/${id}`)
对应的detail组件中通过该方法获取信息
export default function Detail(props) {
return (
<div>
{/* 动态路由方案1 */}
{props.match.params.filmId}
</div>
)
}
5.2 方案二
path中不加/:xxx后缀,正常写
{/* 动态路由方案2 */}
<Route path={'/detail'} component={Detail} />
通过history.push()时加上对应参数
// 动态路由方案2 query传参(这里可以把query换成state)
history.push({pathname : '/detail' , query:{filmId:id}})
最后在detail组件中获取
export default function Detail(props) {
return (
<div>
{/* 动态路由方案2(那么这里的query也要换成state) */}
{props.location.query.filmId}
</div>
)
}
6. 路由拦截
比如点击center页面时,需要先登录才能进行更多的交互,比如查看我的信息,更换头像等等,则需要用到路由拦截。
先写一个鉴权函数(实际开发中具体分析)
function isLogin(){
return localStorage.getItem('token')
}
然后将center的路由写法改成使用render()去执行,这样就可以在其中作表达式判断。同时也要新建个login的路由:
要注意的是这种写法不会自动Route传来的props,要得到的话需要在render参数中自己写上props,然后通过展开运算符传递属性。
{/* <Route path={'/center'} component={Center} /> */}
<Route path='/center' render={(props) => {
return isLogin() ? <Center {...props}/> : <Redirect to='/login' />
}} />
<Route path='/login' component={Login} />
最后写一下login组件
export default function Login(props) {
return (
<div>
<input></input>
// 点击按钮后设置'token'并重定向到center页面
<button onClick={() =>{
localStorage.setItem('token' , 123)
props.history.push(`/center`)
}}>login</button>
</div>
)
}
7. withRouter
比如上面那种情况,子组件需要通过父组件传过来才能使用history等函数,那么假如父组件也没有Route给过来的props呢?可以通过withRouter来获取。
举个例子
return (
<div>
{
list.map(item =>
// 将item和props都传到子组件中
<FilmItem key={item.filmId} {...item} {...props}/>
)
}
</div>
)
相当于
const WithFilmItem = withRouter(FilmItem)
return (
<div>
{
list.map(item =>
// 将item传到子组件中
<WithFilmItem key={item.filmId} {...item}/>
)
}
</div>
)
8. 路由模式
React-Router支持使用hash(对应 HashRouter)和browser(对应 BrowserRouter)两种路由规则:
- BrowserRouter 创建的 URL 格式:xxx.com/path
- HashRouter 创建的 URL 格式:xxx.com/#/path
8.1 BrowserRouter
它使用 HTML5 提供的 history API(pushState、replaceState 和 popstate 事件)来保持 UI 和 URL 的同步。由此可以看出,BrowserRouter 是使用 HTML 5 的 history API 来控制路由跳转的:
<BrowserRouter
// 基准URL
basename={string}
// 如果为true 在导航过程中整个页面将会刷新 一般只在不支持HTML5 history的浏览器中使用
forceRefresh={bool}
// 用于确认导航的函数 默认window.confirm
getUserConfirmation={func}
// 用于设置location.key的长度
keyLength={number}
/>
8.2 HashRouter
使用 URL 的 hash 部分(即 window.location.hash)来保持 UI 和 URL 的同步。由此可以看出,HashRouter 是通过 URL 的 hash 属性来控制路由跳转的:
<HashRouter
// 同上
basename={string}
// 同上
getUserConfirmation={func}
// 使用的hash类型
hashType={string}
/>