四、React全家桶(二)

react-router

react-router包含3个库,react-router、react-router-dom和react-router-native。react-router提供最基本的路由功能,实际使用的时候我们不会直接安装react-router,而是根据应用运行的环境选择安装react-router-dom(在浏览器中使用)或react-router-native(在rn中使用)。react-router-dom和react-router-native都依赖react-router,所以在安装时,react-router也会自动安装,创建web应用

安装:

npm install react-router-dom --save

基本使用:

react-router中奉行一切皆组件的思想,路由器-Router、链接-Link、路由-Route、独占-Switch、重定向-Redirect 都以组件形式存在

Route渲染优先级:children > component > render

创建RouterPage.js

import React, {Component} from 'react'
import {BrowserRouter, Link, Route} from 'react-router-dom';
import HomePage from './HomePage';
import UserPage from './UserPage';

export default class RouterPage extends Component {
  render() {
    return (
      <div>
        <h1>RouterPage</h1>
        <BrowserRouter>
          <nav>
            <Link to='/'>首页</Link>
            <Link to='/user'>用户中心</Link>
          </nav>
          {/* 跟路由需要添加 exact ,实现精确匹配 */}
          <Route exact path='/' component={HomePage}/>
          <Route path='/user' component={UserPage}/>
        </BrowserRouter>
      </div>
    )
  }
}

404页面---独占路由 Switch

设定一个没有path的路由在路由列表最后面,表示一定匹配

<BrowserRouter>
  <nav>
    <Link to='/'>首页</Link>
    <Link to='/user'>用户中心</Link>
  </nav>
  {/* 添加Switch表示仅匹配一个 */}
  <Switch>
  {/* 跟路由需要添加 exact ,实现精确匹配 */}
    <Route exact path='/' component={HomePage}/>
    <Route path='/user' component={UserPage}/>
    <Route component={()=><div>404</div>}/>
  </Switch>
</BrowserRouter>

动态路由

使用 :id 的形式定义动态路由

定义路由:

<Route path='/search/:id' component={Search}/>

添加导航链接:

<Link to={'/search/' + searchId}>搜索</Link>

创建Search并获取参数:

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

function Search(props){
  const {id} = props.match.params
  return <div>
    <h1>Search:{id}</h1>
  </div>
}

export default class RouterPage extends Component {
  render() {
    const searchId = 123
    return (
      <div>
        <h1>RouterPage</h1>
        <BrowserRouter>
          <nav>
            <Link to={'/search/' + searchId}>搜索</Link>
          </nav>
          <Switch>
            <Route path='/search/:id' component={Search}/>
          </Switch>
        </BrowserRouter>
      </div>
    )
  }
}

路由嵌套

Route组件嵌套在其他页面组件中就产生了嵌套关系

修改Search,添加新增和详情

function Search(props){
  const {id} = props.match.params
  return <div>
    <h1>Search:{id}</h1>
    <nav>
      <Link to="/search/add">新增</Link>
      <Link to={"/search/detail/" + id}>详情</Link>
    </nav>
    <Route path='/search/add' component={()=><div>add</div>}/>
    <Route path={"/search/detail/:" + id} component={Detail}/>
  </div>
}

function Detail(){
  return <div>
    <h1>Detail</h1>
  </div>
}

 

路由守卫

思路:创建高阶组件包装Route使其具有权限判断功能 

对 UserPage 做路由守卫,在RouterPage.js 中

<PrivateRoute path='/user' component={UserPage}/>
<Route path='/login' component={LoginPage}/>

PrivateRoute.js

import React, { Component } from 'react'
import {Route, Redirect} from 'react-router-dom';
import UserPage from './UserPage';

export default class PrivateRoute extends Component {
  render() {
    const {isLogin = false, location} = this.props
    const redirect = location.pathname
    console.log(this.props);
    return isLogin ? (
      <Route path='/user' component={UserPage}/>
    ) : (
      <Redirect to="/login"/>
    )
  }
}

与HashRouter对比:

1、HashRouter最简单,不需要服务器渲染,靠浏览器的#来区分path就可以,BrowserRouter需要服务器端对不同的URL返回不同的HTML

后端配置可参考  https://react-guide.github.io/react-router-cn/docs/guides/basics/Histories.html

2、BrowserRouter使用HTML5历史API(pushState,replaceState 和 propstate 事件),让页面的UI同步与URL

3、HashRouter 不支持 location.key 和location.state,动态路由跳转需要通过?传递参数

4、Hash不需要服务器任何配置就可以运行,如果你刚刚入门,那就使用它吧。但是我们不推荐在实际线上环境中用到它,因为每一个web应用都应该渴望使用 browserHistory

 

实现BrowserRouter

历史管理对象history初始化及向下传递,location变更监听

MyRouterPage.js

import React, { Component } from 'react'
// import { BrowserRouter, Link, Route, Switch } from 'react-router-dom';
import { BrowserRouter, Link, Route } from '../kReact-router-dom';
import HomePage from './HomePage';
import UserPage from './UserPage';

export default class MyRouterPage extends Component {
  render() {
    return (
      <div>
        <h1>MyRouterPage</h1>
        <BrowserRouter>
          <nav>
            <Link to="/">首页</Link>
            <Link to="/user">用户中心</Link>
          </nav>
            <Route exact path="/" component={HomePage}/>
            <Route path="/user" component={UserPage}/>
        </BrowserRouter>
      </div>
    )
  }
}

 

kReact-router-dom.js

import React, { Component, useContext } from 'react'
import { createBrowserHistory } from 'history';

const RouterContext = React.createContext()
const RouterProvider = RouterContext.Provider
const RouterConsumer = RouterContext.Consumer

export class BrowserRouter extends Component {
  constructor(props) {
    super(props)
    this.history = createBrowserHistory()
    this.state = {
      location: this.history.location
    }
    this.unlisten = this.history.listen(location => {
      this.setState({ location })
    })
    console.log(this.history);
  }
  componentWillUnmount() {
    if (this.unlisten) {
      this.unlisten()
    }
  }
  render() {
    const { children } = this.props
    return <RouterProvider value={{ history: this.history, location: this.state.location }}>
      {children}
    </RouterProvider>
  }
}

export function Route(props) {
  const ctx = useContext(RouterContext)
  const { location } = ctx
  const { path, component } = props
  const matchCurrent = path === location.pathname
  return matchCurrent ? React.createElement(component) : null
}

export class Link extends Component {
  handlerClick = (event, history) => {
    const { to } = this.props
    event.preventDefault()
    history.push(to)
  }
  render() {
    const { children, to } = this.props
    return <RouterConsumer>
      {
        ctx => <a href={to} onClick={event => this.handlerClick(event, ctx.history)}>
          {children}
        </a>
      }
    </RouterConsumer>
  }
}

 

posted @ 2019-10-19 15:50  落叶无痕~  阅读(410)  评论(0编辑  收藏  举报