React-Router4 嵌套路由实现的两种方式以及路由过渡动画的实现方式

刚学习react的路由 一个小的后台管理demo

目录结构

// 后台管理页面通常有两种布局方式 
// 一种是没有导航栏的 登录页面
// 一种是含有导航栏的 各功能性页面 
// 导航栏不需要每次都渲染  Layout中含有导航栏 和 传递进来的子组件即可
// 大致思路就是  登录页面和Layout页面同级 切换路由的时候 切换Layout中的子组件
src
	pages
		Layout // 这里有导航侧边栏
	App.js

方式1

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

Layout.js

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)

方式2

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 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

Layout.js

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)

layout.sytl 样式

// 父级相对定位
.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;
}

你可能感兴趣的:(前端,react,react-router,react-router4)