С сегодняшнего дня мы начинаем распутывать загадочный череп react-router-dom, о нет, вуаль. Перед этим нам нужно понять некоторые предварительные знания: базовое использование контекста React и react-router-dom. Учащиеся, которым необходимо просмотреть, перейдите по адресу:
- первый разContext — инструмент React для доступа к данным между компонентами.
- Статья 2Базовое использование React-Router
Ниже показан процесс чтения исходного кода с моим одноклассником Сяо С. Вы можете обратиться к этой идее, чтобы изучить исходный код других проектов с открытым исходным кодом.
Я:Сяо С, давайте сегодня изучим исходный код React-router-dom.
OK!
Я:Во-первых, на официальном сайте react-router есть основные способы использования.здесь (Китайский нажмите здесь) Перечисляет часто используемые компоненты и их использование
- Router (BrowserRouter, HashRouter)
- Route
- Switch
- 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 состоит в том, чтобы создать объект истории и передать его компоненту
В настоящее время
Я:ты сказал
Что вы выберете: посмотреть на реактивный маршрутизатор или на историю?
Я:Ха-ха, в это время я действительно хочу взглянуть на 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";
Это действительно так
Я:Затем я сосредоточусь на реакции-маршрутизаторе
Я:потому что
- история, я могу догадаться, что он делает, это как-то связано с путем браузера
- Если в роутере используется история, я подожду, пока не столкнусь с препятствиями при чтении кода, а потом зайду в историю, это сработает?
Да
Я:вернуться на этот путь
Я:пойти посмотреть реагировать-маршрутизатор
почему он
Я:Потому что, когда он импортировал пакет, он не добавил относительный путь.
Я:Указывает, что это выпущенный пакет узлов, который необходимо найти в пути node_modules при импорте.
import { Router } from "react-router";
Я:Я подниму его.Конечно, предполагается, что в файле конфигурации должны быть соответствующие конфигурации.
Энен
Я:Введите этот путь, файлы действительно tmd, mmp
У меня есть эта привычка, сначала посмотрите, импортирует ли индекс только
Я:Но на самом деле, когда мы используем 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-маршрутизатора предполагается, что каждый
Я:Далее мы можем продолжить рассмотрение этого компонента. Давай сначала поедим, прочитает, а следующий развал послушаем.
хорошо. Пока-пока.