刚学习react的路由 一个小的后台管理demo
// 后台管理页面通常有两种布局方式
// 一种是没有导航栏的 登录页面
// 一种是含有导航栏的 各功能性页面
// 导航栏不需要每次都渲染 Layout中含有导航栏 和 传递进来的子组件即可
// 大致思路就是 登录页面和Layout页面同级 切换路由的时候 切换Layout中的子组件
src
pages
Layout // 这里有导航侧边栏
App.js
import React, {
Component } from 'react'
// router
import {
HashRouter as Router, Route, Switch, Redirect } from 'react-router-dom'
// redux
import {
Provider } from 'react-redux'
import {
createStore } from 'redux'
import reducer from '@store'
// 消息提醒
import {
message } from 'antd'
// 页面组件
import Login from '@pages/Login/'
import Layout from '@pages/Layout/'
import Index from '@pages/Index/'
import Table from '@pages/Table/'
import Ani from '@pages/Animation/'
//创建store
const store = createStore(reducer)
const requireAuth = (Child, props) => {
// 一个简单的验证 demo 不要在意这些细节
let userData = window.localStorage.getItem('userData')
try {
userData = JSON.parse(userData)
} catch (error) {
return <Redirect to="/login" />
}
if (userData && userData.name) {
return <Child {
...props} />
} else {
message.error('登录已过期,请重新登录')
return <Redirect to="/login" />
}
}
class App extends Component {
render() {
return (
<Provider store={
store}>
<Router activeClassName="active">
<div>
<Switch>
<Route path="/login" component={
Login} />
{
/* 写法1 将路由集中在一起 路由可以通过循环的方式添加 我这里写的demo就没有循环 */}
{
/* 写法1 集中式的写法 注意 path = "/" 是父路由 所以不要写exact去完全匹配它 别的路由exact根据场景加或者不加 */}
<Layout path="/" component={
Layout}>
<Route
exact
path="/index"
component={
props =>
requireAuth(Index, props)
}
/>
<Route
exact
path="/table"
component={
props =>
requireAuth(Table, props)
}
/>
<Route
exact
path="/ani"
component={
props => requireAuth(Ani, props)}
/>
</Layout>
</Switch>
</div>
</Router>
</Provider>
)
}
}
export default App
import React, {
Component } from 'react'
// 通过withRouter来获取router的对象进行路由操作
import {
withRouter } from 'react-router-dom'
import {
Layout, Menu } from 'antd'
// 路由列表 通常是从后端获取的json数据 这里模拟的 [{path:'/', name:''} ...之类]
import routes from './routes'
const {
Header, Content, Sider } = Layout
class Lay extends Component {
constructor(props) {
super(props)
this.state = {
defaultMenuKeys: [this.props.location.pathname]
}
}
// 路由重定向
componentWillMount() {
const pathname = this.props.location.pathname
if (pathname === '/') {
this.props.history.replace('/index')
}
}
// 点击导航进行跳转
handleClick(data) {
this.setState({
defaultMenuKeys: [data.key]
})
this.props.history.push(data.path)
}
// 将Menu列表填充到组件中
getMenu = arr => {
return arr.map(v => {
return (
<Menu.Item
key={
v.path}
onClick={
this.handleClick.bind(this, v)}
>
{
v.name}
</Menu.Item>
)
})
}
render() {
return (
<Layout>
{
/* 脑阔*/}
<Header className="header">
<h1 style={
{
color: '#fff', float: 'left' }}>
xxxx管理系统
</h1>
</Header>
<Layout>
{
/* 左侧导航部分 */}
<Sider width={
200} style={
{
background: '#fff' }}>
<Menu
mode="inline"
defaultSelectedKeys={
this.state.defaultMenuKeys}
defaultOpenKeys={
['sub1']}
style={
{
height: '100%', borderRight: 0 }}
>
{
this.getMenu(routes)}
</Menu>
</Sider>
<Layout style={
{
padding: '0 24px 24px' }}>
<Content
style={
{
background: '#fff',
padding: 24,
margin: 0,
minHeight: 280
}}
>
{
/* 写法1 这里写传递进来的字组件 */}
<>{
this.props.children}</>
</Content>
</Layout>
</Layout>
</Layout>
)
}
}
export default withRouter(Lay)
import React, {
Component } from 'react'
// router
import {
HashRouter as Router, Route, Switch, Redirect } from 'react-router-dom'
// redux
import {
Provider } from 'react-redux'
import {
createStore } from 'redux'
import reducer from '@store'
// 页面组件
import Login from '@pages/Login/'
import Layout from '@pages/Layout/'
//创建store
const store = createStore(reducer)
class App extends Component {
render() {
return (
<Provider store={
store}>
<Router activeClassName="active">
<div>
<Switch>
<Route path="/login" component={
Login} />
{
/* 方式二中 我们将路由与组件结合 不再集中在一起 */}
<Layout path="/" component={
Layout}>
</Switch>
</div>
</Router>
</Provider>
)
}
}
export default App
import React, {
Component } from 'react'
// 写法二的话 不能用 HashRouter as 一开始因为这个我怎么写都不对
// import { HashRouter as Route, Switch, withRouter } from 'react-router-dom'
import {
Route, Switch, withRouter } from 'react-router-dom'
// 页面组件
import Index from '@pages/Index/'
import Table from '@pages/Table/'
import Ani from '@pages/Animation/'
// antd布局组件
import {
Layout, Menu } from 'antd'
// 路由
import routes from './routes'
const {
Header, Content, Sider } = Layout
class Lay extends Component {
constructor(props) {
super(props)
this.state = {
defaultMenuKeys: [this.props.location.pathname]
}
}
// 路由重定向
componentWillMount() {
const pathname = this.props.location.pathname
if (pathname === '/') {
this.props.history.replace('/index')
}
}
// 点击导航进行跳转
handleClick(data) {
this.setState({
defaultMenuKeys: [data.key]
})
this.props.history.push(data.path)
}
// 将Menu列表填充到组件中
getMenu = arr => {
return arr.map(v => {
return (
<Menu.Item
key={
v.path}
onClick={
this.handleClick.bind(this, v)}
>
{
v.name}
</Menu.Item>
)
})
}
render() {
return (
<Layout>
<Header className="header">
<h1 style={
{
color: '#fff', float: 'left' }}>
xxx管理系统
</h1>
</Header>
<Layout>
<Sider width={
200} style={
{
background: '#fff' }}>
<Menu
mode="inline"
defaultSelectedKeys={
this.state.defaultMenuKeys}
defaultOpenKeys={
['sub1']}
style={
{
height: '100%', borderRight: 0 }}
>
{
this.getMenu(routes)}
</Menu>
</Sider>
<Layout style={
{
padding: '0 24px 24px' }}>
<Content
style={
{
background: '#fff',
padding: 24,
margin: 0,
minHeight: 280
}}
>
{
/* <>{this.props.children}> 这是写法1的子 */}
<Switch>
<Route
path="/index"
component={
Index}
/>
<Route
path="/table"
component={
Table}
/>
<Route path="/ani" component={
Ani} />
{
/*
exact根据场景加或者不加
这里也可以使用这种方式去验证路由权限
requireAuth(Ani, props)}
/>
*/ }
</Switch>
</Content>
</Layout>
</Layout>
</Layout>
)
}
}
export default withRouter(Lay)
一种就是集中管理, 一种就是根据页面去管理
各有各的好处
现在在捯饬路由过渡动画
等捯饬出来了再加进来~嘿嘿
这个demo为了演示有很多地方都没拆出来
实际项目尽量拆出来
比较清晰
祝女孩子们节日快乐!
import React, {
Component } from 'react'
// 写法二的话 不能用 HashRouter as 一开始因为这个我怎么写都不对
// import { HashRouter as Route, Switch, withRouter } from 'react-router-dom'
import {
Route, Switch, withRouter } from 'react-router-dom'
// 页面组件
import Index from '@pages/Index/'
import Table from '@pages/Table/'
import Ani from '@pages/Animation/'
// antd布局组件
import {
Layout, Menu } from 'antd'
// 路由
import routes from './routes'
// 引入路由动画组件 !!!!!!!
import {
TransitionGroup, CSSTransition } from 'react-transition-group'
// 样式
import './layout.styl'
const {
Header, Content, Sider } = Layout
class Lay extends Component {
constructor(props) {
super(props)
this.state = {
defaultMenuKeys: [this.props.location.pathname]
}
}
componentWillMount() {
// 路由重定向
this.props.path === '/' && this.props.history.replace('/index')
}
// 点击导航进行跳转
handleClick(data) {
this.setState({
defaultMenuKeys: [data.key]
})
this.props.history.push(data.path)
}
// 将Menu列表填充到组件中
getMenu = arr => {
return arr.map(v => {
return (
<Menu.Item
key={
v.path}
onClick={
this.handleClick.bind(this, v)}
>
{
v.name}
</Menu.Item>
)
})
}
render() {
// 获取location对象
const location = this.props.location
return (
<Layout>
<Header className="header">
<h1 style={
{
color: '#fff', float: 'left' }}>
xxx管理系统
</h1>
</Header>
<Layout>
<Sider width={
200} style={
{
background: '#fff' }}>
<Menu
mode="inline"
defaultSelectedKeys={
this.state.defaultMenuKeys}
defaultOpenKeys={
['sub1']}
style={
{
height: '100%', borderRight: 0 }}
>
{
this.getMenu(routes)}
</Menu>
</Sider>
<Layout style={
{
padding: '0 24px 24px' }}>
<Content
style={
{
background: '#fff',
padding: 24,
margin: 0,
minHeight: 280
}}
>
<TransitionGroup>
<CSSTransition
// 需要加一个key属性,让react认识每个组件,并进行正确的加载。
// 这里我改了官方demo的代码, 原来是设置成location.key, 这样的话每次点击同一个路由链接的时候都会渲染。
key={
this.props.location.pathname}
// classNames 就是设置给css动画的标示,记得'classNames'带's'的。
classNames="slide"
// 动画时间设置为800ms,和css中的需要一致。
timeout={
300}
>
{
/* 这个很重要必须要传入loaction对象 不然不显示前一个节点 */}
<Switch location={
location}>
<Route
path="/index"
component={
Index}
/>
<Route
path="/table"
component={
Table}
/>
<Route path="/ani" component={
Ani} />
{
/*
exact根据场景加或者不加
这里也可以使用这种方式去验证路由权限
requireAuth(Ani, props)}
/>
*/ }
</Switch>
</CSSTransition>
</TransitionGroup>
</Content>
</Layout>
</Layout>
</Layout>
)
}
}
export default withRouter(Lay)
// 父级相对定位
.ant-layout-content {
position: relative;
}
// 过渡过程中避免因为绝对定位而撑不开 绝对定位是为了避免前后组件互相挤
setTransitionStyle() {
position: absolute;
// 因为父级有padding在 撑满会抖动 就 直接宽度100%即可
// left: 0;
// right: 0;
width: 100%;
}
.slide-enter {
setTransitionStyle();
opacity: 0.01;
transform: translateX(100%);
}
.slide-enter-active {
setTransitionStyle();
opacity: 1;
transform: translateX(0%);
transition: all 300ms ease-out;
}
.slide-exit {
setTransitionStyle();
opacity: 1;
transform: translateX(0%);
}
.slide-exit-active {
setTransitionStyle();
opacity: 0.01;
transform: translateX(100%);
transition: all 300ms ease-out;
}