react之路由导航

React-router

前言

React.js只提供 UI (view)层面的解决方案(MVC中的V)。在实际的项目当中,它并不能解决我们所有的问题,需要结合其它的库,例如 Redux、React-router 等来协助提供完整的解决方案。

介绍

React-router4(以下简称RR4)遵循React的设计万物皆组件的理念。所以只是一堆 提供了导航功能的组件,具有声明式(引入即用),可组合性的特点

  • 组成部分:
    • react-router 核心,是浏览器和原生应用的通用部分,不提供dom操作进行跳转的api。
    • react-router-dom 基于浏览器环境的开发。
    • react-router-native 基于react-native环境的开发。

安装

浏览器开发就只需要安装react-router-dom

  npm install react-router-dom --save

安装 react-router-domreact-router-native 时,都会自动将 react-router 作为依赖安装

常用组件

路由类型

1、BrowserRouter

<BrowserRouter/>是react-router-dom增加的组件,利用path来实现路由(类似与Vue中的history路由,使用pushState和replaceState事件构建路由)。使得页面和浏览器的history保持一致。如:http://localhost/home

需要服务器的配置

2、HashRouter

<HashRouter/>是react-router-dom增加的组件,利用hash来实现路由(使用window.location.hash和hashchange事件构建路由),如:http://localhost/#/home

路由渲染方式

Route

<Route/>是 React Router 中最重要的组件了,它最基本的职责就是:当页面的访问地址与 Route 上的 path 匹配时,就在<Route/>所在位置渲染出对应的组件

    <Route path="/home" component={Home} />
    <Route path="/list" component={List} />
    <Route path="/users" component={Users} />
  • path(String):当浏览器地址与path地址匹配时,则渲染component对应的组件

如果不给path,那么路由将总是匹配

  • component(Component):在path匹配成功之后会渲染这个组件

往组件props注入history,location,match参数

  • render(Function):在path匹配成功之后会渲染这个函数返回的内容
  • exact(Boolean):是否精确匹配
  <!-- 浏览器地址为'/home' 和'/home/a',都能匹配 -->
  <Route path="/home" component={Home}/>

  <!-- 浏览器地址为'/home'时能匹配 ,浏览器地址为'/home/a'时不能匹配 -->
  <Route path="/home" component={Home} exact /> 
Redirect

<Redirect/>用于重定向页面

  • from(String):浏览器地址为from的值时,重定向到to所在的地址
  • to(String|Object):跳转的地址(如值为Object,有以下参数)
    • pathname,跳转到的URL。
    • search,跳转后的url参数。此例中,跳转后的url是http://127.0.0.1:9090/p2?p1=1&p2=2
    • state,会保存在this.history.location.state中,可以用于传递数据
Switch

多个Route可能会被同时渲染(如下),用<Switch/>来包裹多个Route/Redirect组件,只渲染出第一个与浏览器访问地址匹配的 <Route><Redirect>(注意顺序问题)

  <!-- 以下可能会同时渲染两个组件 -->
  <Route path="/" component={Home} />
  <Route path="/home" component={Home} />
  <Route path="/list" component={List} />

  <!-- 以下只渲染一个组件 -->
  <Switch>
    <Route path="/" component={Home} />
    <Route path="/home" component={Home} />
    <Route path="/list" component={List} />
    <Redirecct to="/404"/>
  </Switch>
  • 用途:
    • 渲染单个组件
    • 重定向
    • 404页面

导航

声明式导航

利用组件(Link或NavLink)属性实现路由跳转

Link

<Link/>为你的应用提供声明式,无障碍导航,默认解析成a标签

  • to(String): 点击跳转到指定路径

如果只是单纯的跳转就直接用字符串形式的路径

  <Link to="/home" />
  • to(Object):携带参数跳转到指定路径(同Redirect)

跳转时携带详细信息(比如这是个支付跳转,需要把商品的价格等信息传递过去)

    <Link to={{
      pathname: '/pay',
      search: '?id=123456',
      state: { price: 998 }
    }} />
  • replace: bool

为 true 时,点击链接后将使用新地址替换掉上一次访问的地址

NavLink

<NavLink/><Link/>的特殊版,顾名思义这就是为页面导航准备的。因为导航需要有 “激活状态”

  • activeClassName(String) 写法:activeClassName=""

导航选中激活时候应用的样式名,默认样式名为 active

  • activeStyle(Object)

如果不想使用样式名就直接写style

  • to: string|object

<Link/>

  • isActive: func
    通过返回值(boolean)决定导航是否激活,或者在导航激活时候做点别的事情。不管怎样,它不能决定对应页面是否可以渲染。

编程式导航

利用路由提供的history对象实现路由跳转

利用js实现跳转,编程式导航的三大对象:history,location,match

  • history.push(path|Object)
  • history.replace(path|Object)
获取history对象
  • 1、利用<Route />渲染的组件

直接通过props.history/ this.props.history获取

组件只要是通过<Route component={组件}/>方式渲染的组件,history对象自动传入组件的props(this.props.history访问)

  • 2、withRouter高阶组件(推荐)

高阶组件不是一个React组件,而是一个函数,包装函数

利用withRouter高阶组件包装后,直接通过组件的props.history获取,就可以使用编程式导航进行点击跳转

高阶组件:一个包装函数

  • 3、Context(了解,不推荐)

RR4 在 Router 组件中通过Contex暴露了一个router对象,router对象下包含history(即:this.context.router.history)

路由的Enter与Leave

相比之前的版本,RR4有了很大的改变,废除了之前版本onEnter、onLeave等路由钩子函数,利用组件生命周期函数来替代

  • 使用componentDidMount或componentWillMount来代替onEnter
  • 使用componentDidUpdate 或 componentWillUpdate来代替onUpdate
  • 使用componentWillUnmount来代替onLeave

动态路由

在匹配路径path 的后面加上冒号 + 参数, 如path ="goods/:id"

  • 获取动态id方式
    当通过<Route/>渲染组件时,路由会给我们组件注入3个参数(history,location,match),通过match.params获取动态路由参数
    在这里插入图片描述

在这里插入图片描述

嵌套路由

props.match是实现嵌套路由的对象,当我们在某个页面跳转到它的下一级子页面时,我们不会显显性地写出当前页面的路由,而是用match对象的path和url属性。

  • match.path:是指写在 <Route/> 中的 path 参数;
  • match.url:是指在浏览器中显示的真实 URL。

match.path 可用于嵌套组件中的 <Route/>,而 match.url 可用于嵌套组件中的 <NavLink/>

  <div className="subnav">
    <NavLink to={props.match.url + "/computer"} activeClassName="active">电脑</NavLink>
    <NavLink to={props.match.url + "/pad"} activeClassName="active">平板</NavLink>
    <NavLink to={props.match.url + "/acc"} activeClassName="active">配件</NavLink>
  </div>

  <Switch>
    <Route path={props.match.path + "/phone"} component={Phone}/>
    <Route path={props.match.path + "/computer"} component={Computer}/>
    <Redirect from={props.match.path} to={props.match.path + "/computer"} exact />
    <Route path={props.match.path + "/pad"} component={Pad}/>
    <Route path={props.match.path + "/acc"} component={Acc}/>
  </Switch>
import { Layout, Menu } from 'antd'
import React from 'react'
import { Route } from 'react-router-dom'

import IQReact from './IQReact.jsx'
import IQVue from './IQVue.jsx'
import IQNode from './IQNode.jsx'
import IQJquery from './IQJquery.jsx'

const { Header, Footer, Sider, Content } = Layout;

// 类组件
class Category extends React.PureComponent {
  state = {
    current: '/react',
    menu: [
      {
        name: 'React',
        path: '/react',
        component: IQReact
      },
      {
        name: 'Vue',
        path: '/vue',
        component: IQVue
      },
      {
        name: 'NodeJS',
        path: '/node',
        component: IQNode
      },
      {
        name: 'Jquery',
        path: '/jquery',
        component: IQJquery
      },
    ]
  }
  goto = ({key}) => {
    this.setState({
      current: key
    })
    this.props.history.push('/category' + key)
  }
  componentWillMount(){
    const {pathname} = this.props.location
    console.log(pathname)
    this.setState({
      current:pathname.replace(this.props.match.url,'')
    })
  }
  render() {
    const { menu, current } = this.state
    const { match } = this.props
    return <div>
      <Layout>
        <Sider>
          <Menu onClick={this.goto} defaultSelectedKeys={[current]} mode="inline">
            {
              menu.map(item => <Menu.Item key={item.path} >
                {item.icon}
                {item.name}
              </Menu.Item>)
            }
          </Menu>
        </Sider>
        <Content>  
          {
            menu.map(item => <Route key={item.name} path={match.path + item.path} component={item.component}></Route>)
          }
        </Content>
      </Layout>
    </div>
  }
}

export default Category

补充:

声明式导航

利用组件(Link或NavLink)属性实现路由跳转

在这里插入图片描述
main.js中的写法:这里是 < App/>

import React from 'react'
import ReactDOM from 'react-dom'

// 安装路由 react-router 和 react-router-dom
import {
  HashRouter,
  BrowserRouter,
  Route
} from 'react-router-dom'

import App from './App'

const Router = process.env.NODE_ENV === 'product' ? BrowserRouter : HashRouter
ReactDOM.render( 
  <Router>
    <App/> 
  </Router>,
  document.querySelector('#app')
)

编程式导航

利用路由提供的history对象实现路由跳转
1、利用 < Route/> 渲染的组件 ,获取history对象

注意:设置方法goto(), 事件中传入路径参数 onClick={this.goto.bind(this,item.path)}

goto=(path)=>{
    console.log(this.props) // 这里的打印的是跳转路由后, 渲染的组件的 props
    // 跳转路由 有浏览器记录
    // this.props.history.push(path)

    // 跳转路由 无浏览器记录
    this.props.history.replace(path)
  }
  
  render(){
    const {menu}=this.state
    console.log('app.props=',this.props) // 这里的打印的是加载页面时,当前渲染的组件的 props
    // 编程式导航时 利用 < Route/> 渲染的组件 ,获取history对象时:
    // 1、当main.js中为<App /> 时打印里面为空的
    ///2、当main.js中为<Route component={App} /> 时打印 location、match、history
  
    return (
      <div>
        <Switch>
          <Route path="/home" component={Home}></Route>
          <Route path="/good" component={Good}></Route>
          <Route path="/search" component={Search}></Route>
          <Route path="/profile" component={Profile}></Route>
          <Route path="/login" component={Login}></Route>
          <Route path="/reg" component={Reg}></Route>
          <Route path="/notfound" render={()=> <div>404</div>}></Route>
          {/* 重定向可以不加to */}
          <Redirect from="/" to="/home" exact></Redirect>

          {/* 404 */}
          <Redirect to="/notfound"></Redirect>
        </Switch>
        <nav>
          <ul>
            {
              // 声明式导航
              // menu.map(item=><li key={item.name}>
              //   <NavLink to={item.path} activeClassName="active" activeStyle={{fontWeight:"bold"}}>{item.text}</NavLink>
              // </li>)

              // 编程式导航
               menu.map(item=><li key={item.name} onClick={this.goto.bind(this,item.path)}>
                 {item.text}
              </li>)
            }
          </ul>
        </nav>
      </div>
    )
  }

注意:main.js中需要把< App/> 修改为 < Route component={App} />

import React from 'react'
import ReactDOM from 'react-dom'

// 安装路由 react-router 和 react-router-dom
import {
  HashRouter,
  BrowserRouter,
  Route
} from 'react-router-dom'

import App from './App'

const Router = process.env.NODE_ENV === 'product' ? BrowserRouter : HashRouter
ReactDOM.render( 
  <Router>
    <Route component={App} />
    {/* 这里不加 path 因为所有路径都要渲染这个App组件 */}
  </Router>,
  document.querySelector('#app')
)

在这里插入图片描述

2、利用 withRouter 高阶组件,获取history对象

利用withRouter高阶组件包装后,直接通过组件的props.history获取,就可以使用编程式导航进行点击跳转

1、首先引入withRouterimport {Route,Redirect,Switch,NavLink, withRouter} from 'react-router-dom'
2、使用高阶组件App = withRouter(App)

import React from 'react'
// 安装react-router 和 react-router-dom
import {Route,Redirect,Switch,NavLink, withRouter} from 'react-router-dom'

// 导入路由
import Home from './pages/Home.jsx'
import Good from './pages/Good.jsx'
import Search from './pages/Search.jsx'
import Profile from './pages/Profile.jsx'
import Login from './pages/Login.jsx'
import Reg from './pages/Reg.jsx'

// 导入样式
import './App.css'

// 有状态改变用class组件
class App extends React.PureComponent {
  state = {
    menu:[{
      text:'首页',
      name:'home',
      path:'/home'
    },{
      text:'商品',
      name:'good',
      path:'/good'
    },{
      text:'搜索',
      name:'search',
      path:'/search'
    },{
      text:'我的',
      name:'profile',
      path:'/profile'
    }]
  }
  
  // 传入路径参数 path
  goto = (path) => {
    console.log(this.props) // 这里的打印的是跳转路由后, 渲染的组件的 props
    // 跳转路由 有浏览器记录
    // this.props.history.push(path)

    // 跳转路由 无浏览器记录
    this.props.history.replace(path)
  }
  
  render(){
    const {menu}=this.state
    console.log('app.props=',this.props) // 这里的打印的是当前渲染的组件的 props
    // 这时当main.js中为 <App /> 时能打印出history对象,因为已经使用高阶组件进行了包装
  
    return (
      <div>
        <Switch>
          <Route path="/home" component={Home}></Route>
          <Route path="/good" component={Good}></Route>
          <Route path="/search" component={Search}></Route>
          <Route path="/profile" component={Profile}></Route>
          <Route path="/login" component={Login}></Route>
          <Route path="/reg" component={Reg}></Route>
          <Route path="/notfound" render={()=> <div>404</div>}></Route>
          {/* 重定向可以不加to */}
          <Redirect from="/" to="/home" exact></Redirect>

          {/* 404 */}
          <Redirect to="/notfound"></Redirect>
        </Switch>
        <nav>
          <ul>
            {
              // 声明式导航
              // menu.map(item=><li key={item.name}>
              //   <NavLink to={item.path} activeClassName="active" activeStyle={{fontWeight:"bold"}}>{item.text}</NavLink>
              // </li>)

              // 编程式导航
               menu.map(item=><li key={item.name} onClick={this.goto.bind(this,item.path)}>
                 {item.text}
              </li>)
            }
          </ul>
        </nav>
      </div>
    )
  }
}

// 高阶组件(包装函数)
App = withRouter(App)

export default App

注意main.js中的改变:

import React from 'react'
import ReactDOM from 'react-dom'

// 安装路由 react-router 和 react-router-dom
import {
  HashRouter,
  BrowserRouter,
  Route
} from 'react-router-dom'

import App from './App'

const Router = process.env.NODE_ENV === 'product' ? BrowserRouter : HashRouter
ReactDOM.render( 
  <Router>
    <App/>
    {/* <Route component={App} /> */}
    {/* 这里不加path 因为所有路径都要渲染这个App组件 */}
  </Router>,
  document.querySelector('#app')
)

使用高阶组件后,就不用把组件修改为 <Route component={App}还是之前的写法 < App />

在这里插入图片描述

3、利用 Context 获取history对象

RR4 在 Router 组件中通过Contex暴露了一个router对象,router对象下包含history(即:this.context.router.history)

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值