路由-过渡

路由

一、介绍

前端应用大多数是SPA(单页应用程序),也就是只有一个HTML页面的应用程序。因为它的用户体验更好、对服务器压力更小,所以更受欢迎。为了有效的使用单个页面来管理多页面的功能,前端路由应运而生。

前端路由模式

​ ①、hash模式

​ ②、history模式

​ ③、memeory模式

浏览器中前端路由模式

​ ①、hash模式

​ onhashchange

​ ②、history模式

​ pushState/replaceState/go/onpopstate

二、路由使用

2.1安装路由模块

https://serializedowen.github.io/docs/react-router-dom

yarn add react-router-dom@5   支持类组件和函数组件 react支持18及以下版本
react-router-dom@6 react16.8之后  推荐使用函数组件

2.2 相关组件

Ø 路由模式组件:包裹整个应用,一个React应用只需要使用一次

​ HashRouter: 使用URL的哈希值实现 (localhost:3000/#/first) 浏览器

BrowserRouter:使用H5的history API实现(localhost3000/first) 浏览器

MemoryRouter:此内存,浏览器中不建议使用

Ø 导航组件:用于指定导航链接

​ Link/NavLink:最终Link会编译成a标签,而to属性会被编译成 a标签的href属性

Ø 路由定义组件:指定路由规则和对应匹配成功后要渲染的组件

​ Route:

path属性:路由规则,这里需要跟Link组件里面to属性的值一致

component属性:展示的组件

import React, { Component } from 'react'
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'

// 引入路由渲染的组件
import Film from './views/Film'
import Detail from './views/Film/Detail'

class App extends Component {
  render() {
    return (
      <Router>
        <Switch>
          <Route path='/detail/:id(\d+)' component={Detail} />
          <Route path='/' component={Film} />
        </Switch>
      </Router>
    )
  }
}

export default App

2.3声明式导航

使用Link或NavLink组件完成声明式导航的定义

Link/NavLink区别

² Link组件不会根据路由的变化而添加或修改编译后html标签中的属性

² NavLink会根据路由的变化而自动修改编译后html标签中的属性

​ 如果当前的路由规则和Navlink中的To所写的规则一致则添加class样式,

​ 默认名称为 active,可以通过activeClassName来修改匹配成功后样式名称。

import React, { Component } from 'react'
import './style/app.css'
// 引入路由模块
// BrowserRouter
//  + basename: 设置根路径,默认为'/',自定义一访问url前经 basename='/app'
// Switch 只要子Route匹配成功,则停止匹配
//  + 使用Switch组件要把范围大的放在后面,把范围小的放在前面
// Redirect 重定向,一般写在规则的最后
//  + exact 严格匹配,一般多和404匹配在一起使用时才用
// Link, NavLink,NavLink它有激活样式状态,默认className='active'
import { BrowserRouter as Router, Route, Switch, Redirect, Link, NavLink } from 'react-router-dom'

// 引入路由渲染的组件
import Home from './views/Home'
import About from './views/About'
import Detail from './views/Detail'
import Notfound from './views/Notfound'

class App extends Component {
  render() {
    return (
      <Router>
        {/* 
          声明式导航
            Link / NavLink
              + to 跳转的路径 string|object 必填的
              + replace 是否替换当前的历史记录,默认false
            NavLink
              + activeClassName 激活的样式,默认'active' activeClassName='abc'
              + exact 严格匹配,默认false
        */}
        {/* <Link to='/home'>首页</Link> &nbsp;&nbsp;|&nbsp;&nbsp;
        <Link to='/about'>关于</Link> */}
        <NavLink to='/home'>首页</NavLink> &nbsp;&nbsp;|&nbsp;&nbsp;
        <NavLink to='/about'>关于</NavLink>
        <hr />
        {/* 
          定义路由规则
           路由匹配是从上向下依赖去匹配的,直接没有Route组件后停止匹配
           path 匹配的路径,它匹配是按包含去匹配的
           component 匹配成功后要渲染的组件
           exact 严格匹配,一般不用去设置
          <Route exact path='/' component={Home} />
        */}
        <Switch>
          {/* Route匹配渲染的组件才会注入到props对象中,把路由相关对象 */}
          <Route path='/about' component={About} />
          <Route path='/home' component={Home} />
          {/* 
            动态路由参数,它是要先定义后使用
              + :id 路由参数 this.props.match.params.id 获取路由参数  /detail/100
              + :id(\d+) 路由参数限制 正则表达式 \d+ 1到多个数字
              + :id? 可选路由参数 可以不传  /detail/100  /detail
          */}
          <Route path='/detail/:id' component={Detail} />
          {/* 重定向 */}
          <Redirect exact from='/' to='/home' />
          {/* 404页面 */}
          <Route path='*' component={Notfound} />
        </Switch>
      </Router>
    )
  }
}

export default App

2.4编程式导航

react-router-dom中通过history对象中的push/replace/goBack等方法实现编程式导航功能。

this.props.history.push("/home")

this.props.history.push/replace ({
  pathname: "/home",
  search: "from=404",	// 表示传递查询字符串
  state: {		// 隐式传参,地址栏不体现
    username: "admin",
  },
});

注:在react路由中this.props要想得到路由中的对象,则默认必须要通过路由规则匹配渲染的组件才能有此对象 - 必须是直接渲染的组件

2.5页面路由数据传递

路由参数:在Route定义渲染组件时给定动态绑定的参数。

React路由传参方式有三种:

Ø 动态路由参数(param)

​ 以“/detail/:id”形式传递的数据

​ 在落地组件中通过this.props.match.params得到

Ø 查询字符串(search)

​ 通过地址栏中的 ?key=value&key=value传递

​ 在落地组件中通过this.props.location.search得到

Ø 隐式传参(state),通过地址栏是观察不到的

​ 通过路由对象中的state属性进行数据传递

​ 在落地组件中通过this.props.location.state得到

2.6嵌套路由

在有一些功能中,往往请求地址的前缀是相同的,不同的只是后面一部份,此时就可以使用多级路由(路由嵌套)来实现此路由的定义实现。

import React, { Component } from 'react'
import { BrowserRouter, Route, Switch } from 'react-router-dom'

import Layout from './views/layout'

class App extends Component {
  render() {
    return (
      <BrowserRouter>
        <Switch>
          {/* 
            父路由 
            注:父路由一定不要去设置严格匹配,否则子路由永远不会被匹配到  exact
          */}
          <Route path='/admin' component={Layout} />
        </Switch>
      </BrowserRouter>
    )
  }
}

export default App

三、路由渲染

路由渲染分为component和render和children三种渲染模式

import React, { Component } from 'react'
import { BrowserRouter, Route, Switch } from 'react-router-dom'

import PageRender from './views/pageRender'

class App extends Component {
  state = {
    num: 100
  }
  add = () => {
    this.setState(state => ({
      num: state.num + 1
    }))
  }
  render() {
    return (
      <BrowserRouter>
        <h1>Num值:{this.state.num}</h1>
        <button onClick={this.add}>++++++++++++</button>
        <hr />
        <Switch>
          {/* component  */}
          {/* 
            类方式
              1.它不能进行路由匹配成功后,二次验证
              2.它会自动把路由信息通过props传递给子组件
              3.宿主组件更新,它不会销毁PageRender组件,只是重新渲染
          */}
          {/* <Route path='/' component={PageRender} /> */}
          {/* 
            函数方式 -- 相对类方式性能差一些
              1.它路由规则匹配成功后,还可以进行二次验证
              2.它不会自动把路由信息通过props传递给子组件,需要手动传递
              3.宿主组件更新,它会销毁PageRender组件,然后重新创建
          */}
          {/* <Route
            path='/'
            component={route => {
              if (route.location.search === '?name=admin') {
                return <PageRender {...route} />
              } else {
                return <div>你没有权限</div>
              }
            }}
          /> */}

          {/* render */}
          <Route
            path='/'
            render={route => {
              if (route.location.search === '?name=admin') {
                return <PageRender {...route} />
              } else {
                return <div>你没有权限</div>
              }
            }}
          />
        </Switch>
      </BrowserRouter>
    )
  }
}

export default App
import React, { Component } from 'react'
import { BrowserRouter, Route, Switch } from 'react-router-dom'

import PageRender from './views/pageRender'

class App extends Component {
  render() {
    // console.log('App', this.props)
    return (
      <BrowserRouter>
        {/* 
            children
              + 它一般不是用来定义路由规则的,它一般用于定义一些组件是否在特定的路由下显示
              + 它一般不会和Switch路由组件一起使用
          */}
        {/* 
            直接在children属性中使用自定义组件,它会进行路由规则匹配
            但是它不会把路由信息通过props传给它所调用的子组件中
            此方案,知道就可以,不推荐使用
          */}
        {/* <Route path='/aaa' children={<PageRender />} /> */}
        {/* 
          回调函数方式,它常用一些,主要用于给元素来进行判断是否显示
          回调函数方式,这不会去匹配路由规则,不管path和当前的路由是否匹配,都会执行回调函数
          如果当前地址和path匹配,那么route参数中的match属性就会有值,否则就为null
          */}
        <Route
          path='/user'
          children={route => {
            // console.log(route.match)
            // return route.match ? <h3 style={{ color: 'red' }}>你好child按钮</h3> : null
            return <h3 style={{ color: route.match ? 'red' : 'black' }}>你好child按钮</h3>
          }}
        />
      </BrowserRouter>
    )
  }
}

export default App

四、获取路由信息

让任意组件获取到路由信息

import React, { Component } from 'react'
import { Route, Switch, withRouter, Link } from 'react-router-dom'
// withRouter 高阶组件,它可以让任意的组件获取到路由信息
// 此高阶组件也不建议随意使用,尽可能少用,因为它会增加原有组件的层级

import PageRender from './views/pageRender'
import User from './views/user'

@withRouter
class App extends Component {
  checkLogin = location => {
    // 根据location.pathname来判断当前的路由是否需要登录,才能访问到
    console.log('route', location)
    // this.props.history.replace('/user')
  }

  componentDidMount() {
    // console.log(this.props.location)
    // 监听路由变化,初始化不会触发,只有路由变化才会触发
    // 初始化时我主动调用一次,来获取当前的路由信息
    this.checkLogin(this.props.location)
    // 可以用它来完成导航守卫功能
    this.props.history.listen(this.checkLogin)
  }

  render() {
    return (
      <div>
        <Link to='/user'>user</Link>&nbsp;&nbsp;|&nbsp;&nbsp;
        <Link to='/'>home</Link>
        <hr />
        <Switch>
          <Route path='/user' component={User} />
          <Route path='/' component={PageRender} />
        </Switch>
      </div>
    )
  }
}

// export default withRouter(App)
export default App

五、过渡

网址:https://reactcommunity.org/react-transition-group/css-transition

安装

npm install react-transition-group

import React, { Component } from 'react'
// CSSTransition 对元素进行过渡切换
//  + in 元素是否显示 true/false
//  + timeout 过渡样式切换时长 ms
//  + classNames 过渡样式类名 string|object
//     + string 过渡样式添加前缀  className='fade'  fade_enter
//     + object 过渡样式不添加前缀 className={ { enter: 'fade-enter' } } 自定义过渡样式名称
//  + unmountOnExit 元素隐藏时是否移除DOM true/false
//  + appear 元素第一次显示时是否添加过渡动画 true/false 默认false
//     + fade-appear, fade-appear-active, fade-appear-done
//  + 事件
//    + onEnter 元素显示前触发
//    + onEntering 元素显示时触发
//    + onEntered 元素显示后触发
//    + onExit 元素隐藏前触发
//    + onExiting 元素隐藏时触发
//    + onExited 元素隐藏后触发
// 注:CSSTransition它只能有一个子元素
import { CSSTransition } from 'react-transition-group'
import './style/transition.css'

class App extends Component {
  state = {
    isShow: true
  }
  setIsShow = () => {
    this.setState(state => ({
      isShow: !state.isShow
    }))
  }
  render() {
    return (
      <div>
        <CSSTransition
          // in属性不能少,true进场动画,false出场动画
          in={this.state.isShow}
          // 样式切换时长
          timeout={1000}
          // 过渡样式前缀名称
          classNames='fade'
          // 出场完毕后,删除dom
          unmountOnExit
          onEnter={el => {
            console.log('onEnter', el)
          }}
          onExited={el => {
            console.log('onExited', el)
          }}
        >
          <div className='box'>
            <h1>App过渡切换</h1>
            <h1>App过渡切换</h1>
          </div>
        </CSSTransition>
        <hr />
        <button onClick={this.setIsShow}>切换元素</button>
      </div>
    )
  }
}

export default App

六、补充

因为学习周期过长,我后面设置一个React学习的专栏,方便学习和管理笔记

  • 25
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值