目录
React 快速上手 - 07 前端路由 react-router
目标
- 基础使用
- 参数传递
- 路由匹配
- 转换动画
- 跳转路由
环境
- react 16
- react-router 4
- react-router-dom 4
- react-transition-group
0. 安装
通过官网我们可以发现 react-router
可以用在 web 网站端
native 设备端
我们这里针对 web 网站端
安装
yarn add react-router-dom
react-router
会包自动依赖安装
1. 先跑一个简单例子
- 代码示范
import React,{Component} from 'react' import {HashRouter as Router,Route,Link,Switch} from 'react-router-dom' const Home = () => ( <div> <h2>首页</h2> </div> ) const About = () => ( <div> <h2>关于</h2> </div> ) class RouterView extends Component { render() { return ( <Router> <div> <ul> <li> <Link to="/">首页</Link> </li> <li> <Link to="/about">关于</Link> </li> </ul> <Switch> <Route exact path="/" component={Home} /> <Route path="/about" component={About} /> </Switch> </div> </Router> ) } } export default RouterView
- 代码结构
最外层需要包裹组件<Router>
组件<Route>
就是你看到的组件内容,放在哪里就哪里显示
组件<Link>
是页面链接
- 动图说明
- codepen
https://codepen.io/ducafecat/...
2. 基础使用
2.1 BrowserRouter
还是 HashRouter
-
BrowserRouter
是需要服务端配合,是基于html5的pushState和replaceState的,很多浏览器不支持,存在兼容性问题。
链接地址长这样
http://localhost:3000/about
-
HashRouter
是浏览器端解析路由
链接地址长这样
http://localhost:3000#/about
- 灵活切换
import {HashRouter as Router,Switch} from 'react-router-dom' //import {BrowserRouter as Router,Switch} from 'react-router-dom' <Router> ... </Router>
我用
as Router
做了别名,方便切换
2.2 组件 <Route>
属性 exact
完全匹配
<Route path="/about" component={About} />
exact=false
的时候 path
等于 /about
/about/me
都能匹配
但是 exact=true
的时候 只匹配 path
等于 /about
2.3 组件 <Route>
属性 strict
末尾斜杠的匹配
<Route strict path="/about/" component={About} />
当 strict=true
路由请求末尾必须带 /
2.4 组件 <Link>
- 属性
to: string
路由地址字符串
<Link to="/about?me=haha">关于</Link>
- 属性
to: object
路由对象
<Link to={{ pathname: '/courses',search: '?sort=name',hash: '#the-hash',state: { fromDashboard: true }>关于</Link>
- 属性
replace: bool
设置 true
替换浏览器对象 history
为当前路由,按回退按钮时会发现之前的路由被替换了
2.5 组件 <NavLink>
- 属性
activeClassName: string
样式名称
<NavLink to="/about" activeClassName="selected">关于</NavLink>
- 属性
activeStyle: object
样式对象
<NavLink to="/about" activeStyle={{ fontWeight: 'bold',color: 'red' }}>关于</NavLink>
- 属性
isActive: func
判断函数
const checkIsActive = (match,location) => { if (!match) { return false } ... return true } <NavLink to="/about" isActive={checkIsActive}>关于</NavLink>
2.6 组件 <Switch>
只渲染出第一个与当前访问地址匹配的 <Route>
或 <Redirect>
否则你有几个 <Route>
都会显示
2.7 组件 <Redirect>
路由重定向
<Switch> <Redirect from='/users/:id' to='/users/profile/:id'/> <Route path='/users/profile/:id' component={Profile}/> </Switch>
当请求 /users/:id
被重定向去 '/users/profile/:id'
- 属性
from: string
需要匹配的将要被重定向路径。
- 属性
to: string
重定向的 URL 字符串
- 属性
to: object
重定向的 location 对象
- 属性
push: bool
若为真,重定向操作将会把新地址加入到访问历史记录里面,并且无法回退到前面的页面。
2.8 组件 <Prompt>
- 属性
message: string
<Prompt message="确定要离开?" />
- 属性
message: func
<Prompt message={location => `确定要去 ${location.pathname} ?`} />
- 属性
when: bool
决定是否启用 Prompt
3. 参数传递
3.1 编写路由定义
<Route path="/topics/:topicId" component={Topic} />
:topicId
定义参数
3.2 编写接收组件
const Topic = ({match}) => ( <div> <h3>参数: {match.params.topicId}</h3> </div> )
match.params
就是传递的参数
3.3 完整例子
import React,{Component} from 'react' import {BrowserRouter as Router,Switch} from 'react-router-dom' const Topic = ({match}) => ( <div> <h3>参数: {match.params.topicId}</h3> </div> ) class RouterView extends Component { render() { return ( <Router> <div> <ul> <li> <Link to="/topics/rendering">Rendering with React</Link> </li> <li> <Link to="/topics/components">Components</Link> </li> <li> <Link to="/topics/props-v-state">Props v. State</Link> </li> </ul> <Switch> <Route path="/topics/:topicId" component={Topic} /> </Switch> </div> </Router> ) } } export default RouterView
动图效果
4. 没有匹配
import React,Switch} from 'react-router-dom' const Back = () => ( <div> <h2>首页</h2> </div> ) const NoMatch = () => ( <div> <h2>没有匹配</h2> </div> ) class RouterView extends Component { render() { return ( <Router> <div> <ul> <li> <Link to="/back">返回</Link> </li> <li> <Link to="/also/will/not/match">路由请求</Link> </li> </ul> <Switch> <Route path="/back" component={Back} /> <Route component={NoMatch} /> </Switch> </div> </Router> ) } } export default RouterView
在最下面写个默认组件,都没命中,就是这个了
动图效果
5. 嵌套路由
在 react-route4
中嵌套要这样写
<Switch> <Route path="/article" component={ArticleList} /> <Route path="/article/:id" component={Article} /> <Route path="/article/:id/recommend" component={ArticleRecommend} /> </Switch>
写成一排,业务如下
path | 组件 | 说明 |
---|---|---|
/article | ArticleList | 文章列表 |
/article/:id | Article | 文章 |
/article/:id/recommend | ArticleRecommend | 文章推荐 |
6. 自定义路由
适合用来做权限检查
6.1 创建自定义 Route
const isAuthenticated = true const PrivateRoute = ({ component: Component,...rest }) => ( <Route {...rest} render={props => isAuthenticated ? ( <Component {...props} /> ) : ( <Redirect to={{ pathname: "/login",state: { from: props.location } }} /> ) } /> );
6.2 使用自定义路由
... <Route path="/public" component={Public} /> <Route path="/login" component={Login} /> <PrivateRoute path="/protected" component={Protected} />
7. 转换动画
这里用到了 react-transition-group
库
动图效果
7.1 安装
yarn add react-transition-group
7.2 编写动画 css
文件 fade.css
.fade-enter { opacity: 0; z-index: 1; } .fade-enter-active { opacity: 1; transition: opacity 250ms ease-in; } .fade-exit { opacity: 0; }
名称 | 说明 |
---|---|
fade-enter | 动画启动 |
fade-enter-active | 动画激活 |
fade-exit | 动画离开 |
其它动画样式名字可以参考 css-transition
7.3 导入包和动画样式
... import {TransitionGroup,CSSTransition} from 'react-transition-group' import './fade.css'
TransitionGroup,CSSTransition
必须导入
7.4 编写样式
const styles = {} styles.fill = { position: 'relative',height: '200px',width: '500px' } styles.content = { ...styles.fill,top: '40px',textAlign: 'center',height: '120px' } styles.nav = { padding: 0,margin: 0,position: 'absolute',top: 0,height: '40px',width: '100%',display: 'flex' } styles.navItem = { textAlign: 'center',flex: 1,listStyleType: 'none',padding: '10px' } styles.hsl = { color: 'white',paddingTop: '20px',fontSize: '30px',height: '120px' } styles.rgb = { color: 'white',height: '120px' }
这是 react
的样式对象,当然你也可以写成 .css
文件
7.5 编写导航
const NavLink = props => ( <li style={styles.navItem}> <Link {...props} style={{color: 'inherit'}} /> </li> )
7.6 编写展示组件
// 切换区域 A const HSL = ({match: {params}}) => ( <div style={{ ...styles.fill,...styles.hsl,background: `hsl(${params.h},${params.s}%,${params.l}%)` }} > hsl({params.h},{params.s}%,{params.l}%) </div> ) // 切换区域 B const RGB = ({match: {params}}) => ( <div style={{ ...styles.fill,...styles.rgb,background: `rgb(${params.r},${params.g},${params.b})` }} > rgb({params.r},{params.g},{params.b}) </div> )
7.7 编写容器组件
const RouterView = () => ( <Router> <Route render={({location}) => ( <div style={styles.fill}> <Route exact path="/" render={() => <Redirect to="/hsl/10/90/50" />} /> <ul style={styles.nav}> <NavLink to="/hsl/10/90/50">Red</NavLink> <NavLink to="/hsl/120/100/40">Green</NavLink> <NavLink to="/rgb/33/150/243">Blue</NavLink> <NavLink to="/rgb/240/98/146">Pink</NavLink> </ul> <div style={styles.content}> <TransitionGroup> <CSSTransition key={location.key} classNames="fade" timeout={300}> <Switch location={location}> <Route exact path="/hsl/:h/:s/:l" component={HSL} /> <Route exact path="/rgb/:r/:g/:b" component={RGB} /> <Route render={() => <div>Not Found</div>} /> </Switch> </CSSTransition> </TransitionGroup> </div> </div> )} /> </Router> )
- codepen
https://codepen.io/ducafecat/...
代码
- reactjs-example / 5-1-routerBase
- reactjs-example / 5-2-routerParams
- reactjs-example / 5-3-routerNoMatch
- reactjs-example / 5-4-routerTransitions
- reactjs-example / 5-5-routerRedirect