手写一个react-router-dom简易版(二)

本文只代表自己粗陋的观点,有很多错误,欢迎指出错误

接上面的一篇《手写一个react-router-dom简易版(一)》

HashRouter

基本思路

  • 同BrowserRouter思路一致,主要是监听路由变化不一致
  • 使用window.addEventListener(“hashchange”)监听
  • HashRouter.js添加HashRouter模式
import React from "react";
import { Provider } from "./context";
import History from "./history";
const url = require("url");
class HashRouter extends React.Component {
  constructor() {
    super();
    this.state = {
      pathname: window.location.hash.slice(1) || "/",
      count: 0,
    };
  }
  componentDidMount() {
    window.location.hash= window.location.hash || "/"
    window.addEventListener("hashchange", () => {
      this.setState({
        pathname: window.location.hash.slice(1) || "/",
      });
    });
  }
  render() {
    let value = {
      type: "HashRouter",
      //history添加HashRouter模式
      history: new History("HashRouter"),
      location: Object.assign(
        {
          pathname: "/",
        },
        url.parse(this.state.pathname, true)
      )
    };
    return <Provider value={value}>{this.props.children}</Provider>;
  }
}
export default HashRouter;

history添加HashRouter模式

完善代码

const url = require("url");
class History {
  constructor(type) {
    this.type = type || "BrowserRouter";
  }
  push(path) {
  //添加多种模式兼容
    switch (this.type) {
      case "BrowserRouter":
        if (typeof path === "string") {
          window.history.pushState(null, "", path);
        }
        if (typeof path === "object") {
          let obj = {
            pathname: path.path || "/",
            query: path.query || {},
          };
          const formatUrl = url.format(obj);
          window.history.pushState(null, "", formatUrl);
        }
        break;
      case "HashRouter":
        if (typeof path === "string") {
          window.location.hash = path.indexOf("/") === 0 ? path : "/" + path;
        }
        if (typeof path === "object") {
          let obj = {
            pathname: path.path || "/",
            query: path.query || {},
          };
          const formatUrl = url.format(obj);
          window.location.hash =
            formatUrl.indexOf("/") === 0 ? formatUrl : "/" + formatUrl;
        }
        break;
    }
  }
}
export default History;

BrowserRouter代码优化

完善代码

import React from "react";
import { Provider} from "./context";
import History from "./history";
const url = require("url");
class BrowserRouter extends React.Component {
  constructor() {
    super();
    this.state = {
      pathname: window.location.pathname || "/",
    };
  }
  componentDidMount() {
    window.addEventListener("pushState", () => {
      console.log("pushState");
      this.setPathname();
    });
  }
  setPathname = () => {
    this.setState(
      {
        pathname: window.location.pathname || "/",
      },(v) => {
        console.log(this.state);
      }
    );
  };
  render() {
    let value = {
      type: "BrowserRouter",
      //添加BrowserRouter模式
      history: new History("BrowserRouter"),
      location: Object.assign(
        {
          pathname: "/",
        },
        url.parse(this.state.pathname, true)
      )
    };
    return (
      <Provider value={value}>
       {this.props.children}
      </Provider>
    );
  }
}
export default BrowserRouter;

Switch

只会渲染一个路由。相反,仅仅定义一系列 时,每一个与路径匹配的 都将渲染出来

import React from 'react'
import { Consumer } from './context'
class Switch extends React.Component {
  render () {
    return (
      <Consumer>
        {
          (state) => {
            for (let i = 0; i < this.props.children.length; i++) {
              const path = (this.props.children[i].props && this.props.children[i].props.path) || '/'
              const reg = new RegExp('^' + path+'$')
              if (reg.test(state.location.pathname)) {
                return this.props.children[i]
              }else if(this.props.children[i].props.to){
                return this.props.children[i]
              }
            }
          }
        }
      </Consumer>
    )
  }
}
export default Switch

Redirect

需要与Switch组件配合使用,重定向到对应视图

import React from 'react'
import { Consumer } from './context'
class Switch extends React.Component {
  render () {
    return (
      <Consumer>
        {
          (state) => {
            for (let i = 0; i < this.props.children.length; i++) {
              const path = (this.props.children[i].props && this.props.children[i].props.path) || '/'
              const reg = new RegExp('^' + path+'$')
              if (reg.test(state.location.pathname)) {
                return this.props.children[i]
              }else if(this.props.children[i].props.to){
                return this.props.children[i]
              }
            }
          }
        }
      </Consumer>
    )
  }
}
export default Switch

总结

其实react-router-dom设计思路就是订阅-发布模式,通过包装组件(生产者),发布通知给消费组件(消费者)调用前端浏览器来改变url,再通过react数据变化,改变对应的视图变化,很多未实现的功能,大体思路都是一致的,其他组件就不一一实现了,有兴趣的读者可以按照这种思路,实现其他组件,其实vue-router设计思路与这种方式大体一致

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值