react-router 入门笔记
2019-07-29 本文已影响0人
copyLeft
React-router 笔记 官方文档
基本思路
- react-router 通过react 组件的方式实现,
- 路由相关的数据,通过props传递给组件调用,
- 路由层级关系, 通过标签嵌套实现
基础标签
- BrowserRouter : 路由容器
- 该组件只能包含单个元素
- Route : 组件渲染出口
- 必须包含在 BrowserRouter 中
- exact 精确匹配
- Link : 跳转链接
- 必须包含在 BrowserRouter 中
基本使用
// react-router-demo
import React, { Component } from 'react'
import {
BrowserRouter as Router,
Route,
Link
} from 'react-router-dom'
// component
import Btn from'./btn'
// pages
function home(props){
return (
<div className="home">
home
</div>
)
}
function products(props){
return (
<div className="products">
products
</div>
)
}
function about(props){
return(
<div className="about">
about
</div>
)
}
function product(props){
return (
<div>
product
</div>
)
}
// router-config
const routerConfig = [
{
label: 'home',
path:'/',
component: home,
exact: true,
},
{
label: 'products',
path: '/products',
component: products,
exact: true,
},
{
label: 'about',
path: '/about',
component: about
},
{
label: 'procut',
path: '/products/:id',
component: product
}
]
// container
export default class pages extends Component{
render(){
const buildLabel = () =>{
let tempalte = [];
for(let item of routerConfig){
tempalte.push(
<Btn key={item.label}>
<Link to={item.path}>
{item.label}
</Link>
</Btn>
)
}
return tempalte
}
return (
<Router>
<div className='pages-container'>
<div className="tab">
{buildLabel()}
</div>
{
routerConfig.map((item) =>{
return (
<Route exact={item.exact} key={item.label} path={item.path} component={item.component}/>
)
})
}
</div>
</Router>
)
}
}
路由传参
- 配置参数路径: path = '/:params'
- 函数组件, 通过组件参数中的 match.params[paramName] 获取路由参数
// pages
function home(props){
return (
<div className="home">
in home page
<br/>
router parmas:
<br/>
{ props.match.params.userId }
</div>
)
}
render(){
return (
<Router>
<div className='pages-container'>
<Link to='/userId'>
to user page
</Link>
<Route exact path='/' component={home}></Route>
<Route path='/:userId' component={home}></Route>
</div>
</Router>
)
}
重定向 Redirect
通过返回组件 Redirect 实现重定向
function subPage(props){
return (
<Redirect to={{
pathname: '/in_show_page'
}}></Redirect>
)
}
return (
<Router>
<div className='pages-container'>
<Link to='/subPage'>
to sub page
</Link>
{/* 参数路由 */}
<Route path='/:userId' component={home}></Route>
{/* 重定向路由 */}
<Route path='/subPage' component={subPage}></Route>
</div>
</Router>
)
命令式导航(history)
- 命令式导航,通过history上的方法实现
- 为props 添加 history 参数, 在组件内部获取路由相关的参数,及控制路由动作
withRouter
- 对于 Route 绑定的组件,组要是页面,本身已经将 路由接口包裹在props中, 而其他组件想获取路由接口需要通过 withRouter(compoent) 处理.
- withRouter 处理的组件必须包裹在 <Router> 标签中s, 也就是说, 子组件中路由参数等,来自于包裹的 Router 对象
// 使用 withRouter 处理组件,组件props中将包含 路由相关对象, { match, location, history }
// 定义组件
function jump (props){
const { match, location, history } = props;
console.log(props)
return (
<div>
<button onClick={ history.goBack }> back </button>
<button onClick={ () =>{history.push('/home')} }> jump to home </button>
<button onClick={ () =>{ history.replace('/subPage') } }> replace path </button>
</div>
)
}
// 在父组件中构件
render(){
return (
<Router>
<div className="route-render">
<Link to='/history'> history </Link>
<Route path='/history' component={withRouter(jump)} ></Route>
</div>
</Router>
)
}
跳转拦截 (Prompt)
- 使用Prompt, 可在路由跳转前,执行相关操作
//跳转提示, 每次路由跳转,提示信息
<Prompt message="路由将跳转"/>
//message 为函数
<prompt message={ lcoation => (`跳转地址 ${location.pathname}`) } />
// 带触发条件 when = Boolean
<Propmt when={ location.pathname === '/home' } message='你将进入home页面' />
//
标签API
<Router>
- history: 绑定history对象, 方便外部调用
<Route>
- component: 渲染组件, 组件props将包含, { match, location, history } 路由参数
- render: 通过函数渲染组件, 通过渲染简单组件的方式, 及通过该方式,为子组件配置参数
- children: 构建自定义链接标签,
- path: 路由匹配地址
- exac: 是否精确匹配
- stric: 使用严格模式
<Switch>
- 多路径匹配时,只渲染就近配置路径下的组件
/**
* 路径为 '/' 只会渲染 home 组件
*/
<Switch>
<Route path='/' componet={home}/>
<Route path='/about' componet={about}/>
<Route path='/product' componet={product}/>
</Switch>
路由参数
-
match
- params :查询参数
- isExac : 是否精确匹配
- path : 包含 basename 路径
- url: Link 地址
-
location
- key: 'ac3df4', // 标识符
- pathname: '/somewhere' //路由地址
- search: '?some=search-string',
- hash: '#howdy',
- state: { [userDefined]: true }
-
history
- length : 记录条数
- action : 当前记录
- location : location
- push: 添加纪录,(跳转页面)
- replace: 替换当前记录 (跳转页面)
- go: 跳转到某条记录
- goBack: 回退
- goForward: 前进
路由嵌套
- 我们知道路由组件都包含在 <BrowserRouter> 中, 且该标签只能包含单一子元素,我们可以认为该标签创建一个路由环境, 包含在该标签内的 路由组件无论层级数,都归属于该路由环境.
//父组件
<App>
<BrowserRouter>
<div>
<Route path='/' component={Home} />
<Route path='/about' component={About} />
<Sub/>
</div>
</BrowserRouter>
</App>
//子组件
<div className='sub'>
<Link to='/about'> to about </Link>
<Route path='/about' rennder={ () =>( <div> in sub about </div> ) } />
</div>
/**
* Sub中的路由组件 与App中的路由组件处于同一层级, 当点击 Link标签时, 将进入 About 而不是Sub的自定义组件
*/
- 创建属于当前页的子路由需要,需要创建新的 '<BrowserRouter>' 标签, 在没有配置basename的情况下,子路由的路径将以上级路由路径为基础, 且优先匹配当前路由环境下的组件, 例如: 父组件路径: '/home' 子组件下有 <Route path='/sub'>, 实际路径为: '/home/sub' 所以在划分路径时, 需要注意路径嵌套的问题,如对根路径 '/' 的处理, 很可能出现,路由配置冲突。
//父路由
let AppRouter = [
{
label: 'home',
path: '/',
component: Home,
exact: true
},
{
label: 'user-center',
path: 'user-center',
component: UserCenter
},
{
label: 'books',
path: '/books',
component: Books
},
{
label: 'book',
path: '/books:id',
component: Books
}
]
return (
<Router>
<div className="App">
<Switch>
{routes}
</Switch>
</div>
</Router>
);
//子路由
<Router>
<div>
<h4>路由</h4>
<Link to='/books'> to sub </Link>
<Route path='/' render={ () =>( <div> 路由嵌套 path='/' </div> ) }></Route>
<Route path='/books' render={ () =>( <div> 路由嵌套 path='/sub' </div> ) }></Route>
</div>
</Router>
/*
** 这是个路由冲突的例子, 可以看到,在父组件和子组件中,都配置了路径 '/books',
** 当触发 Link 跳转时,将显示自组件内的组件, 即显示: '路由嵌套,path=/sub'
** 看起来一切正常,但当我们刷新页面, 将进入主路由的 Books 组件, 所以对于这样的路由冲突,编写时不易发现
*/
component, rander, children 的区别
- component 是应用最多的渲染接口,一般组件使用该接口就可以了, 该接口在渲染是将调用creatElement 构建组件
- rander 接受一个渲染函数, 构建时直接调用函数返回的模板, 不会调用creatElement, 这里是与component不同的地方, rander主要用在需要为组件传递一些 props参数时使用, 如果我们在component 中传入匿名函数包裹的组件, 该组件将被反复调用, 应该creatELement函数无法对匿名函数做比较。参考: React router的Route中component和render属性的使用
- children 无论路径是否匹配都将被渲染, 不同的是, 对于已匹配的路径,children 组件内将获取到 match 参数
自定义history
- 一般在浏览器使用的路由为 BrowserRouter,该路由是封装后的Router,提供了默认的history,所以该路由没有history 接口, 我们可以使用Router 自定特定类型的Router
import { Router} from 'react-router-dom'
import { createBrowserHistory } from 'history'
// 创建history
const history = createBrowserHistory()
// 包装原方法, 添加日志功能
const go = history.go
history.go = n => {
console.log(`go to --> ${n}`)
go(n)
}
// 监听路由变化, 重定向
history.listen(( location, action ) => {
const isLogin = false
if( isLogin ) {
setTimeout(() => {
history.push("/login")
})
}
})