简易实现react-router v4(上)

栏目: IOS · Android · 发布时间: 6年前

内容简介:用react-router v4可以实现单页面应用,可以将组件映射到路由上,将对应的组件渲染到想要渲染的位置。 react路由有两种方式:一种是HashRouter,即利用hash实现路由切换。另一种是BrowserRouter,即利用html5 API实现路由的切换。本文是在阅读react-router v4源码之后简单的实现。本文将从以下几部分进行总结:以下是参照

用react-router v4可以实现单页面应用,可以将组件映射到路由上,将对应的组件渲染到想要渲染的位置。 react路由有两种方式:一种是HashRouter,即利用hash实现路由切换。另一种是BrowserRouter,即利用html5 API实现路由的切换。本文是在阅读react-router v4源码之后简单的实现。

本文将从以下几部分进行总结:

  1. 使用react-router的一个简单例子
  2. 代码结构
  3. Provider和Consumer
  4. 实现HashRouter
  5. 实现Route
  6. 实现Link
  7. 实现Redirect
  8. 实现Switch

使用react-router的一个简单例子

以下是参照 react-router 官方文档 实现的一个简单例子:

import React, { Component } from 'react';
import { render } from 'react-dom';
import { HashRouter as Router, Route, Link, Redirect, Switch } from 'react-router-dom';
import Home from './Home';
import Profile from './Profile';
import User from './User';

export default class App extends Component {
  constructor() {
    super();
  }
  
  render() {
    return (
      <Router>
        <div>
          <div>
            <Link to="/home">首页</Link>
            <Link to="/profile">个人中心</Link>
            <Link to="/user">用户</Link>
          </div>
          <div>
            <Switch>
              <Route path="/home" exact={true} component={Home}></Route>
              <Route path="/profile" component={Profile}></Route>
              <Route path="/user" component={User}></Route>
              <Redirect to="/home"></Redirect>
            </Switch>
          </div>
        </div>
      </Router>
    )
  }
}

render(<App></App>, document.querySelector('#root'));
复制代码

这样就能实现一个超级简单的单页面应用,根据路径的变化渲染相应的组件。点击首页会跳转到Home组件,点击个人中心会跳转到Profile组件,点击用户会跳转到User组件。在这个例子当中。看下这行代码

import { HashRouter as Router, Route, Link, Redirect, Switch } from 'react-router-dom';
复制代码

如果不引入'react-router-dom'这个包,而是自己实现一个my-react-router-dom,暴露出HashRouter,Route,Link,Redirect,Switch这几个组件,并且这几个组件和react-router-dom提供的功能基本一样,那究竟怎么实现呢?

代码结构

简易实现react-router v4(上)

如上图所示,在上面的那个例子路由引入'react-router-dom'改为'./my-react-router-dom', 并在同级新建一个my-react-router-dom文件夹,并在index.js中暴露出 HashRouter, Route, Link, Redirect, Switch这几个方法。

// 这是index.js文件
import HashRouter from './HashRouter';
import Route from './Route';
import Link from './Link';
import Redirect from './Redirect';
import Switch from './Switch';

export {
  HashRouter,
  Route,
  Link,
  Redirect,
  Switch
}
复制代码

Provider和Consumer

再看一下context.js文件,context可以跨组件传递数据:

// 这是context.js文件
import React, { Component } from 'react';
// 这个方法是16.3新增的
let { Provider, Consumer} = React.createContext();

export { Provider, Consumer};
复制代码

旧版context的致命缺陷

‘现有的原生 Context API 存在着一个致命的问题,那就是在 Context 值更新后,顶层组件向目标组件 props 透传的过程中,如果中间某个组件的 shouldComponentUpdate 函数返回了 false,因为无法再继续触发底层组件的 rerender,新的 Context 值将无法到达目标组件。这样的不确定性对于目标组件来说是完全不可控的,也就是说目标组件无法保证自己每一次都可以接收到更新后的 Context 值。’ 如何解读 react 16.3 引入的新 context api ---诚身的回答

新版 Context API 提供Provider和Consumer两个组件,顾名思义,provider(提供者)和Consumer(消费者):

  • Provider 组件:用在组件树中更外层的位置。它接受一个名为 value 的 prop,其值可以是任何 JavaScript 中的数据类型。
  • Consumer 组件:可以在 Provider 组件内部的任何一层使用。它接收一个名为 children 值为一个函数的 prop。这个函数的参数是 Provider 组件接收的那个 value prop 的值,返回值是一个 React 元素(一段 JSX 代码)。

实现HashRouter

分析上面的例子,HashRouter的别名设置为Router,传入的 Router 中的每一项即为一条路由配置,表示在匹配给定的地址时,应该使用什么组件渲染视图。HashRouter的简单实现如下:

// 这是hashRouter.js文件
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Provider } from './context';

export default class HashRouter extends Component {
  constructor() {
    super();
    this.state = {
      location: {
        // slice(1)将#截掉
        pathname: window.location.hash.slice(1) || ''
      }
    };
  }

  componentDidMount() {
    // 首次进入页面url会显示#/
    // 如localhost:3000会显示为localhost:3000/#/
    window.location.hash = window.location.hash || '/';
    // 监听hash值变化,重新设置location状态
    window.addEventListener('hashchange', () => {
      this.setState({
        location: {
          ...this.state.location,
          pathname: window.location.hash.slice(1) || '/'
        }
      })
    })
  }

  render() {
    // 每个子route对象都会包含location
    let value = {
      location: this.state.location,
      history: {
        push(to) {
          window.location.hash = to;
        }
      }
    }
    return (
      <Provider value={value}>
        {this.props.children}
      </Provider>
    )
  }
}

复制代码

这样一来,使用hashRouter的方式第一次进入页面时,将显示#/,如localhost:3000首次进入页面将会显示localhost:3000/#/,通过Provider组件来将location和history对象传递给子route,通过hashchange方法来监听hash值的变化,进而重新设置location的状态。

实现Route

通过分析上面的例子,看这一行代码:

<Route path="/home" exact={true} component={Home}></Route>
复制代码

发现Route组件包含有path,exact,component这些属性,那如何实现Route组件呢?看Route.js文件:

// 这是route.js文件
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Consumer } from './context';
import pathToReg from 'path-to-regexp';

export default class Router extends Component {
  constructor() {
    super();
  }

  render() {
    return (
      <Consumer>
        {state => {
          // <route path="xx" component="xx" exact={true}></route>
          // path是route传递的
          let { path, component: Component, exact = false } = this.props;
          // pathname是location中的
          let pathname = state.location.pathname;
          // 根据path实现一个正则,通过正则匹配
          // location中的/home/123是能匹配到Home组件的
          let keys = [];
          let reg = pathToReg(path, keys, { end: exact});
          keys = keys.map(item => item.name);
          let result = pathname.match(reg);
          let [url, ...values] = result || [];
          // 实现路由跳转
          let props = {
            location: state.location,
            history: state.history,
            match: {
              params: keys.reduce((obj, current, index) => {
                obj[current] = values[index];
                return obj;
              }, {})
            }
          }
          if (result) {
            return <Component {...props}></Component>
          }
          return null
        }}
      </Consumer>
    )
  }
}
复制代码

利用path-to-regexp这个库来进行是否严格匹配路径,利用新版context的Consumer组件和props来取出path,component,axact这几个参数。如果匹配到path,则通过<Component {...props}>返回相应匹配到的组件。

实现Link

分析上面的例子,Link的组件实现稍微简单一些,点击内容跳转到to属性对应的路径即可:

// 这是Link组件
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Consumer } from './context';

export default class Link extends Component {
  constructor() {
    super();
  }

  render() {
    return (
      <Consumer>
        {state => {
          return <a onClick={() => {
            state.history.push(this.props.to);
          }}>{this.props.children}</a>
        }}
      </Consumer>
    )
  }
}
复制代码

通过history.push即可实现点击this.props.children的内容跳转到Link组件的to属性对应的路径。

实现Redirect

重定向就是匹配不到后直接跳转到Redirect中的to路径:

// 这是Redirect.js文件
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Consumer } from './context';

export default class Redirect extends Component {
  constructor() {
    super();
  }

  render() {
    return (
      <Consumer>
        {state => {
          // 重定向就是匹配不到后直接跳转到redirect中的to路径
          state.history.push(this.props.to);
          return null;
        }}
      </Consumer>
    )
  }
}
复制代码

实现Switch

Switch组件的作用就是只匹配第一个匹配到的组件:

// 这是Switch.js的文件
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Consumer } from './context';
import pathToRegExp from 'path-to-regexp';

// Switch的作用就是匹配一个组件
export default class Switch extends Component {
  constructor() {
    super();
  }

  render() {
    return (
      <Consumer>
        {state => {
          {
            let pathname = state.location.pathname;
            // 取出Switch包含的组件
            let children = this.props.children;
            for ( var i = 0; i < children.length; i++) {
              let child = children[i];
              // Redirect组件可能没有path属性
              let path = child.props.path || '';
              pathToRegExp(path, [], {end: false});
              // switch匹配成功了
              if (reg.test(pathname)) {
                // 将匹配到的组件返回即可
                return child;
              }
            }
            return null;
          }
        }}
      </Consumer>
    )
  }
}
复制代码

通过遍历this.props.children来进行逐一匹配,如果匹配到相应的路径,立即返回对应的组件,如果匹配不到,则返回空。


以上所述就是小编给大家介绍的《简易实现react-router v4(上)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

ANSI Common Lisp

ANSI Common Lisp

Paul Graham / Prentice Hall / 1995-11-12 / USD 116.40

For use as a core text supplement in any course covering common LISP such as Artificial Intelligence or Concepts of Programming Languages. Teaching students new and more powerful ways of thinking abo......一起来看看 《ANSI Common Lisp》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换