3. Демистификация исходного кода react-router-dom — BrowserRouter

внешний интерфейс исходный код JavaScript React.js
3. Демистификация исходного кода react-router-dom — BrowserRouter

С сегодняшнего дня мы начинаем распутывать загадочный череп react-router-dom, о нет, вуаль. Перед этим нам нужно понять некоторые предварительные знания: базовое использование контекста React и react-router-dom. Учащиеся, которым необходимо просмотреть, перейдите по адресу:

Ниже показан процесс чтения исходного кода с моим одноклассником Сяо С. Вы можете обратиться к этой идее, чтобы изучить исходный код других проектов с открытым исходным кодом.

Я:Сяо С, давайте сегодня изучим исходный код React-router-dom.

OK!

Я:Во-первых, на официальном сайте react-router есть основные способы использования.здесь (Китайский нажмите здесь) Перечисляет часто используемые компоненты и их использование

  1. Router (BrowserRouter, HashRouter)
  2. Route
  3. Switch
  4. Link
Хорошо, давай

Я:Начнем с исходников этих компонентов, первым должен быть BrowserRouter, или HashRouter

Итак, с чего начать?

Я:Во-первых, изgithub, чтобы получить код, соответствующий версии документа.
Я:Затем посмотрите на структуру пути. Такова, что:

Затем я обычно ищу учебник, чтобы вкратце пройтись по нему, загрузить код, а затем скопировать node__modules из отладчика. Тогда, если вы этого не понимаете, сдавайтесь

Я:Нет, вам нужно понять структуру кода, прежде чем углубляться в детали.

Ну а как иначе узнать код?

Я:После того, как вы увидите этот путь, первым делом посмотрите, для чего нужны эти папки и какая из них вам нужна.

скрипт построен, веб-сайт - документ, пакеты - функция
Это почти

Я:правильно. Откройте каждую папку, вы обнаружите, что вещи в пакетах — это исходный код, который нам нужен.

Я:Мы должны начать с исходного кода, потому что первое, что нужно усвоить при чтении исходного кода на этот раз, этоПринцип реализацииа не как построить
Я:Тогда мы начнемreact-router-domНачало
Я:Откройте react-router-dom и перейдите к модулям

Скачать основную ветку прямо с github?

Я:гм

Зачем смотреть на модули
Разве вы не должны сначала посмотреть на package.json и rollup?

Я:Основной код должен быть в модулях. Хочу сначала посмотреть всю конструкцию, чтобы составить общее впечатление

Энен

Я:Откройте модули, и вы увидите несколько компонентов, которые мы только что упомянули в документе.
Я:Давайте начнем сBrowserRouter.jsНачало

Ага

Я:Затем я открою этот файл и начну смотреть код
Я:Не буду обращать внимание на конфигурационные файлы package.json

жестокий

Я:Потому что на этот раз я смотрю на принцип, а не на то, как построен весь исходный код.
Я:Конфигурационный файл тоже вспомогательный

Хм.
Но иногда это важно

Я:Тогда используйте его

Вы бы хоть посмотрели, что используется и сколько входов

Я:Вам не нужно искать в package.json все, что вы используете, потому что компоненты, о которых я беспокоюсь, будут импортированы, когда они будут использоваться. Итак, глядя на исходный код, самое главное — сосредоточиться на нем. Вы должны обратить внимание, потому что некоторые исходные коды очень велики. Случайно попал в море деталей и не могу выбраться.

Имеет смысл
например реагировать

Я:Да, сразу все в нем не прочитаешь, поэтому приходится читать много раз
Я:Фокус может быть разным каждый раз

Да
Конечно

Я:Смешайте все это вместе, это будет очень грязно, и, наконец, сдайтесь.
Я:Более того, когда мы изучаем исходный код, нам не обязательно использовать каждую функцию в исходном коде в одном и том же проекте, или мы должны изучать ее отдельно и использовать отдельно.

Имеет смысл
Я не могу перестать оглядываться

Я:Тогда смотри сначала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__) {
  //此处省略若干行代码
}

export default BrowserRouter;
Тогда я был так сбит с толку, что не мог вспомнить, не мог понять

Я:Ха-ха, с таким небольшим количеством кода должны быть зависимые компоненты
Я:Сначала посмотрите, какие компоненты являются зависимыми
Я:Меня больше всего интересует история и реакция-маршрутизатор. следующим образом:

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";
история это библиотека
так далее
я немного не в курсе

Я:Подождал 30 секунд...
Почему меня интересуют эти двое?

ваша точка интереса
Я видел учебники, связанные с исходным кодом, и немного понимаю историю.

Я:Эм. Об этом сообщил официальный сайт.

Routers

At the core of every React Router application should be a router component. For web projects, react-router-dom provides and routers. Both of these will create a specialized history object for you.

Я:При реализации маршрутизации необходимо использовать историю
Я:Следовательно, это может быть использовано в качестве подготовительного знания для чтения исходного кода. (Если у партнеров есть потребности, объясните в комментариях, мы можем добавить еще одну статью об истории)
Я:Но я оставлю его в покое и посмотрю, повлияет ли это на чтение react-router.
Я:Кроме того, как я уже говорил, количество исходных строк в этом файле очень мало, и оно должно зависеть от других компонентов. Кажется, что этот реактивный маршрутизатор играет важную роль.
Я:Итак, теперь есть два Todos:historyа такжеreact-router

гм

Я:В этот момент нам нужно обратить внимание на пакет react-router.
Я:Два todos я сейчас пропущу, я поставил этот компонент(BrowserRouter) на первый взгляд все равно кода не много

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

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

if (__DEV__) {
  //此处省略若干行代码
}

Я:Я пропущу ветку if(__DEV__), потому что то, на что я сейчас смотрю, — самое основное.
Я:Не забудьте ввести __DEV__ слишком рано, что удобно для разработки и обычно имеет мало общего с основными понятиями.
Я:Осталось только две вещи

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

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

Я:Итак, теперь задача BrowserRouter состоит в том, чтобы создать объект истории и передать его компоненту react-router.

В настоящее время

Я:ты сказал

Что вы выберете: посмотреть на реактивный маршрутизатор или на историю?

Я:Ха-ха, в это время я действительно хочу взглянуть на HashRouter

Я тоже

Я:Из-за предложения импорта

import { createBrowserHistory as createHistory } from "history";

Так что у меня есть основания подозревать, что код для HashRouter похож, просто импортируются разные функции из пакета истории.

HashRouter.js

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

Это действительно так

Я:Затем я сосредоточусь на реакции-маршрутизаторе
Я:потому что

  1. история, я могу догадаться, что он делает, это как-то связано с путем браузера
  2. Если в роутере используется история, я подожду, пока не столкнусь с препятствиями при чтении кода, а потом зайду в историю, это сработает?
Да

Я:вернуться на этот путь

Я:пойти посмотреть реагировать-маршрутизатор

почему он

Я:Потому что, когда он импортировал пакет, он не добавил относительный путь.
Я:Указывает, что это выпущенный пакет узлов, который необходимо найти в пути node_modules при импорте.

import { Router } from "react-router";

Я:Я подниму его.Конечно, предполагается, что в файле конфигурации должны быть соответствующие конфигурации.

Энен

Я:Введите этот путь, файлы действительно tmd, mmp

Я:Импорт представляет собой пакет, под пакетом есть index.js, я должен сначала посмотреть на этот js

У меня есть эта привычка, сначала посмотрите, импортирует ли индекс только

Я:Но на самом деле, когда мы используем react-router-dom, в Интернете будут обсуждения, сравнивающие его с react-router.

не обратил особого внимания
смущенный

Я:Итак, react-router — это выпущенный пакет узла. Однако я не уверен, где находится его код.Если я не смогу его найти, я могу найти его в других местах на github или найти ссылку на официальном сайте npm.

Да

Я:Введите index.js

"use strict";

if (process.env.NODE_ENV === "production") {
  module.exports = require("./cjs/react-router.min.js");
} else {
  module.exports = require("./cjs/react-router.js");
}

Я:Кода не так много, он разделен на продакшн и другие ветки.
Я:Я бы выбрал другую ветку
Я:Но я нашел проблему, бля
Я:В текущем пути нет папки cjs
Я:Поскольку BrowserRouter импортирует пакет
Я:Таким образом, этот пакет должен быть собран после

В это время мы должны посмотреть на скрипт package.

Я:Ну да
Я:Но я чувствую себя немного не в своей тарелке
Я:возвращаюсь к самому роутеру

хорошо
Продолжать
Как вернуться к самому роутеру

Я:В /react-router есть файл router.js
Я:Открой его и посмотри, там только эти две строчки кода, а не то, что мне нужно.
Я:Экспортируется или после компиляции index.js

см. модули

Я:Да, посмотрите на модули
Я:Откройте Router.js в модулях

Если бы это был я, я бы сбился с пути на этот раз.
Перейти прямо к сводке
Затем, наконец, найти маршрутизатор
router.js

Я:я тоже могу заблудиться
Я:Я бегал к истории раньше
Я:Но, подумав об этом, это нехорошо
Я:С точки зрения исходного кода, Router.js легко найти непосредственно в модулях.
Я:Из-за других файлов это не реализация исходного кода с первого взгляда.

Хм

Я:Теперь открой его, и он выглядит так, посмотрим, сколько там строк
Я:После сотни или около того поездок у меня появилась уверенность, ха-ха

import React from "react";
import PropTypes from "prop-types";
import warning from "tiny-warning";

import RouterContext from "./RouterContext";
import warnAboutGettingProperty from "./utils/warnAboutGettingProperty";

function getContext(props, state) {
  return {
    history: props.history,
    location: state.location,
    match: Router.computeRootMatch(state.location.pathname),
    staticContext: props.staticContext
  };
}

/**
 * The public API for putting history on context.
 */
class Router extends React.Component {
  static computeRootMatch(pathname) {
    return { path: "/", url: "/", params: {}, isExact: pathname === "/" };
  }

  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.
    this._isMounted = false;
    this._pendingLocation = null;

    if (!props.staticContext) {
      this.unlisten = props.history.listen(location => {
        if (this._isMounted) {
          this.setState({ location });
        } else {
          this._pendingLocation = location;
        }
      });
    }
  }

  componentDidMount() {
    this._isMounted = true;

    if (this._pendingLocation) {
      this.setState({ location: this._pendingLocation });
    }
  }

  componentWillUnmount() {
    if (this.unlisten) this.unlisten();
  }

  render() {
    const context = getContext(this.props, this.state);

    return (
      <RouterContext.Provider
        children={this.props.children || null}
        value={context}
      />
    );
  }
}

// TODO: Remove this in v5
if (!React.createContext) {
  Router.childContextTypes = {
    router: PropTypes.object.isRequired
  };

  Router.prototype.getChildContext = function() {
    const context = getContext(this.props, this.state);

    if (__DEV__) {
      const contextWithoutWarnings = { ...context };

      Object.keys(context).forEach(key => {
        warnAboutGettingProperty(
          context,
          key,
          `You should not be using this.context.router.${key} directly. It is private API ` +
            "for internal use only and is subject to change at any time. Instead, use " +
            "a <Route> or withRouter() to access the current location, match, etc."
        );
      });

      context._withoutWarnings = contextWithoutWarnings;
    }

    return {
      router: context
    };
  };
}

if (__DEV__) {
  Router.propTypes = {
    children: PropTypes.node,
    history: PropTypes.object.isRequired,
    staticContext: PropTypes.object
  };

  Router.prototype.componentDidUpdate = function(prevProps) {
    warning(
      prevProps.history === this.props.history,
      "You cannot change <Router history>"
    );
  };
}

export default Router;
тогда так мало кода
Первая реакция - посмотреть на вступление

Я:правильно
Я:Но вы видите, есть пять

import React from "react";
import PropTypes from "prop-types";
import warning from "tiny-warning";

import RouterContext from "./RouterContext";
import warnAboutGettingProperty from "./utils/warnAboutGettingProperty";
Не обращайте внимания на первые три, на первый взгляд они бесполезны.

Я:да
Я:На самом деле я немного беспокоюсь о пятом сейчас.

буду смотреть рендер

Я:не волнуйся
Я:Потому что если пятое имя будет warnXXXX
Я:означает предупреждение

Энен
поиск

Я:Предупреждения обычно есть в разрабатываемой версии, если их можно исключить, останется четвертая зависимость

наверное бесполезно
Посмотрите еще раз, это в __DEV__

Я:Да, искал текущий файл, под веткой __DEV__, не читал, ха-ха
Я:Тогда останется только один context.js.

излишний

Я:Я думаю, что хочу взглянуть на этот файл сейчас. Если контента не так много, я сделаю это первым. Если будет больше, то я поставлю это первым.

Энен

Я:тогда посмотрю, хаха
Я:Введите файл RouterContext.js

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

const context = createContext();

context.Provider.displayName = "Router.Provider";
context.Consumer.displayName = "Router.Consumer";

export default context;

Я:у меня второй раз

собака

Я:Я понимаю, менее десяти строк, я могу сосредоточиться на файле Router.js. Содержимое этого файла является ядром всех маршрутизаторов.
Я:Вот стандартное использование контекста, рекомендованное менеджером магазина, см.это
Я:Вернемся к Router.js, ха-ха

а потом
см. createContext

Я:createContext — это последнее использование контекста, см.это
Я:Итак, вам нужно иметь знания, чтобы подготовиться, ха-ха
Я:Проще говоря, это поставщик и потребитель.
Я:На этот раз я смотрел реакцию-маршрутизатор
Я:не сбивайтесь с пути
Я:Вернитесь к router.js
Я:Теперь давайте немного углубимся в детали.
Я:Начните с первого определения функции

function getContext(props, state) {
  return {
    history: props.history,
    location: state.location,
    match: Router.computeRootMatch(state.location.pathname),
    staticContext: props.staticContext
  };
}

Я:Из имени нужно получить контекст, и каждый вызов возвращаетВновь созданныйПредмет, лишнего не знаю, положи сначала, оглянись

гм

Я:Я начну с краткого обзора того, какие методы доступны в компонентах. Также обнаружил, что помимо комплектующих есть и другие коды
Я:В дополнение к содержимому компонента есть решение по компоненту, которое, похоже, касается проблем совместимости старой версии реакции. тогда проигнорирую

// TODO: Remove this in v5
if (!React.createContext) {
  Router.childContextTypes = {
    router: PropTypes.object.isRequired
  };

  Router.prototype.getChildContext = function() {
    const context = getContext(this.props, this.state);

    if (__DEV__) {
      const contextWithoutWarnings = { ...context };

      Object.keys(context).forEach(key => {
        warnAboutGettingProperty(
          context,
          key,
          `You should not be using this.context.router.${key} directly. It is private API ` +
            "for internal use only and is subject to change at any time. Instead, use " +
            "a <Route> or withRouter() to access the current location, match, etc."
        );
      });

      context._withoutWarnings = contextWithoutWarnings;
    }

    return {
      router: context
    };
  };
}

Я:Таким образом, основное внимание уделяется этому компоненту. В компоненте есть некоторые функции жизненного цикла
Я:конструктор, компонентDidMount
Я:Эти два места для инициализации

Ага

Я:по одному
Я:Дело в том, что суд

if (!props.staticContext) {
  this.unlisten = props.history.listen(location => {
    if (this._isMounted) {
      this.setState({ location });
    } else {
      this._pendingLocation = location;
    }
  });
}

Я:Функция if (!props.staticContext) {} заключается в том, чтобы обеспечить использование одной и той же истории, когда маршрутизатор вложен в маршрутизатор.
Я:Внутри находится монитор, который отслеживает изменение местоположения в истории, то есть, когда путь изменяется по этой истории, он будет отслеживаться и обрабатываться единообразно.

Ага

Я:Там вызывается setState, а затем выполняется рендер.

гм

Я:Рендеринг очень прост, просто измените значение контекста

Хм

Я:Мы знаем, что пока значение контекста изменяется, будет вызываться соответствующая потребительская функция, верно?

Ага

Я:Итак, теперь маршрутизатор закончился
Я:Далее нам интересно, какие компоненты используют Consumer

найти маршрут

Я:правильно. В соответствии с использованием React-маршрутизатора предполагается, что каждый будет прослушивать этот контекст, а затем выполнять сопоставление путей, чтобы решить, следует ли отображать содержимое, указанное его собственным атрибутом компонента.
Я:Далее мы можем продолжить рассмотрение этого компонента. Давай сначала поедим, прочитает, а следующий развал послушаем.

хорошо. Пока-пока.