面试官,别再问我React-Router了!每一行源码我都看过了!React-Router 源码 阅读 react router

本文详述React-Router的核心原理,从简单示例入手,解析BrowserRouter、React-Router和history库。介绍如何监听URL变化,实现页面跳转,探讨Switch、Route等组件的工作方式,以及history对象的关键方法如go、push、replace等。
摘要由CSDN通过智能技术生成

前言

此处介绍一下React-Router的核心原理。特别细致的标点符号的不予讨论。

学前小知识

React-Router其实最核心的东西是Route组件和由统一作者开发的History库来建立的。接下来跟着镜头一起走进神秘的ßReact-Router世界吧。

已经知道怎么使用的直接跳过,到下面的源码分析去┗|`O′|┛ 嗷~~

简单示例

一起建一个简单的示例吧。先用react官网的create-react-app脚手架弄个react出来。

npx create-react-app my-app
cd my-app
npm start

安装react-router-dom

npm install react-router-dom

在大家安装之余,我简答的介绍一下react-routerreact-router-dom的区别。

react-router和react-router-dom的区别。

先看提供的API

import {
    Switch, Route, Router } from 'react-router';

import {
    Swtich, Route, BrowserRouter, HashHistory, Link } from 'react-router-dom';
React-router

提供了路由的核心api。如Router、Route、Switch等,但没有提供有关dom操作进行路由跳转的api;

React-router-dom

提供了BrowserRouterRouteLink等api,可以通过dom操作触发事件控制路由。

Link组件,会渲染一个a标签;BrowserRouterHashRouter组件,前者使用pushStatepopState事件构建路由,后者使用hashhashchange事件构建路由。

react-router-domreact-router的基础上扩展了可操作domapi

SwtichRoute 都是从react-router中导入了相应的组件并重新导出,没做什么特殊处理。

react-router-dompackage.json依赖中存在对react-router的依赖,故此,不需要npm安装react-router

  • 可直接 npm 安装 react-router-dom,使用其api。

简单修改

👌,大家🔥应该已经安装完了吧?接下来简单的修改一下脚手架里面的内容。主要是为了熟悉对手,知己知彼百战百胜。

在这里插入图片描述

修改一下src/app.js

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

function Home() {
   
  return (
    <>
      <h1>首页</h1>
      <Link to="/login">登录</Link>
    </>
  )
}

function Login() {
   
  return (
    <>
      <h1>登录页</h1>
      <Link to="/">回首页</Link>
    </>
  );
}

function App() {
   
  return (
    <Router>
      <Switch>
        <Route path="/login" component={
   Login}/>
        <Route path="/" component={
   Home}/>
      </Switch>
    </Router>
  );
}

export default App;

这样就完成了一个最简单的示例了。

SPA的核心思想

  • 监听URL的变化
  • 改变某些context的值
  • 获取对应的页面组件
  • render新的页面

源码攻读

BrowserRouter

从前面的简单示例中我们,发现有一个最外层的伙计,叫BrowserRouter。我们直接干到他的github源码去瞅瞅。

链接:https://github.com/ReactTraining/react-router/blob/master/packages/react-router-dom/modules/BrowserRouter.js

import React from "react";
import {
    Router } from "react-router";
import {
    createBrowserHistory as createHistory } from "history";
import PropTypes from "prop-types";
import warning from "tiny-warning";

/**
 * The public API for a <Router> that uses HTML5 history.
 */
class BrowserRouter extends React.Component {
   
  history = createHistory(this.props);

  render() {
   
    return <Router history={
   this.history} children={
   this.props.children} />;
  }
}

if (__DEV__) {
   
  BrowserRouter.propTypes = {
   
    basename: PropTypes.string,
    children: PropTypes.node,
    forceRefresh: PropTypes.bool,
    getUserConfirmation: PropTypes.func,
    keyLength: PropTypes.number
  };

  BrowserRouter.prototype.componentDidMount = function() {
   
    warning(
      !this.props.history,
      "<BrowserRouter> ignores the history prop. To use a custom history, " +
        "use `import { Router }` instead of `import { BrowserRouter as Router }`."
    );
  };
}

export default BrowserRouter;

抛开一些七七八八的判断。重新看。

import React from "react";
import {
    Router } from "react-router";
import {
    createBrowserHistory as createHistory } from "history";

class BrowserRouter extends React.Component {
   
  history = createHistory(this.props);

  render() {
   
    return <Router history={
   this.history} children={
   this.props.children} />;
  }
}

写着几个大字:《瓜子二手车》他只是一个中间商,他在赚差价。(打钱!

BrowserRouter是依赖于两个库:分别为historyreact-router。ok,我们一探究竟。

react-router

源码链接:https://github.com/ReactTraining/react-router/blob/master/packages/react-router/modules/Router.js

import HistoryContext from "./HistoryContext.js";
import RouterContext from "./RouterContext.js";

这两个东西其实很简单,都是引用了一个叫做createContext,目的也很简单,这里其实就是创建的普通context,只不过拥有特定的名称而已。源码如下。就几行。

// TODO: Replace with React.createContext once we can assume React 16+
import createContext from "mini-create-react-context";

const createNamedContext = name => {
   
  const context = createContext();
  context.displayName = name;

  return context;
};

export default createNamedContext;

OK,重点是接下来,抛开context之后,我们需要关注的东西。我整理一下。

先看构造函数

构造函数

constructor(props) {
   
  super(props);

  this.state = {
   
    location: props.history.location
  };

  // This is a bit of a hack. We have to start listening for location
  // changes here in the constructor in case there are any <Redirect>s
  // on the initial render. If there are, they will replace/push when
  // they mount and since cDM fires in children before parents, we may
  // get a new location before the <Router> is mounted.
  
  // _isMounted 表示组件是否加载完成
  this._isMounted = false;
  // 组件未加载完毕,但是 location 发生的变化,暂存在 _pendingLocation 字段中
  this._pendingLocation = null;

  // 没有 staticContext 属性,表示是 HashRouter 或是 BrowserRouter
  if (!props.staticContext) {
   
    this.unlisten = props.history.listen(location => {
   
      if (this._isMounted) {
   
        // 组件加载完毕,将变化的 location 方法 state 中
        this.setState({
    location });
      } else {
   
        this._pendingLocation = location;
      }
    });
  }
}

有两个值☞_isMounted_pendingLocation

分别是 是否挂载 待定

内部维护了一个location,默认值是由外面传入的history,我找到传入的地方。

其实就是之前看到的BrowserRouter

import React from "react"
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值