Принцип внешней маршрутизации, такой как react-router, примерно такой же, который может переключаться и отображать разные страницы без обновления. Суть маршрутизации заключается в том, что при изменении URL-адреса страницы результат отображения страницы может измениться в соответствии с изменением URL-адреса, но страница не будет обновляться. Одностраничные (SPA) приложения могут быть реализованы с помощью внешней маршрутизации.Эта статья начинается с принципа внешней маршрутизации и подробно описывает изменения в принципе внешней маршрутизации. Затем начните с исходного кода react-router4.0 и глубоко поймите, как react-router4.0 реализует интерфейсную маршрутизацию.
- Внешняя маршрутизация через Hash
- Внешняя маршрутизация через историю H5
- Использование React-router4.0
- Анализ исходного кода React-router4.0
Оригинальный адрес в моем блоге:GitHub.com/fort и все…
Если это поможет, твоя звезда станет для меня лучшей поддержкой~
1. Реализуйте интерфейсную маршрутизацию через Hash
1. Принцип хеша
Ранняя внешняя маршрутизация была реализована с помощью хеширования:
Изменение хеш-значения URL-адреса не приведет к обновлению страницы.
Таким образом, интерфейсную маршрутизацию можно реализовать с помощью хеширования, чтобы добиться эффекта отсутствия обновления. Свойство hash находится в объекте location.На текущей странице можно передать:
window.location.hash='edit'
Чтобы изменить хэш-значение текущего URL-адреса. После выполнения вышеуказанного назначения хэша URL-адрес страницы изменяется.
Перед назначением:http://localhost:3000После назначения:http://localhost:3000/#edit
Значений хеша, оканчивающихся на #, в url больше, но до и после присваивания, хотя хэш-значение страницы меняется, меняется полный url страницы, но страница не будет обновляться. Кроме того, существует событие, называемое hashchange, которое может отслеживать изменения хэша.Мы можем отслеживать изменения хэша следующими двумя способами:
window.onhashchange=function(event){
console.log(event);
}
window.addEventListener('hashchange',function(event){
console.log(event);
})
Когда значение хеша изменится, выведите HashChangeEvent. Конкретное значение HashChangeEvent:
{isTrusted: true, oldURL: "http://localhost:3000/", newURL: "http://localhost:3000/#teg", type: "hashchange".....}
С событием мониторинга и изменением хэш-страницы не обновляется, поэтому мы можем выполнять функцию отображения и скрытия различных дисплеев пользовательского интерфейса в функции обратного вызова события мониторинга, чтобы обеспечить маршрутизацию внешнего интерфейса.
Кроме того, помимо изменения значения хеш-функции текущей страницы через window.location.hash, это также может быть достигнуто через тег a html:
<a href="#edit">edit</a>
2. Недостатки хэша
Хэш имеет хорошую совместимость, поэтому он широко использовался на ранних этапах маршрутизации переднего плана, но использование хэша также имеет много недостатков.
- Поисковые системы не дружат со страницами с хешами
- Трудности с отслеживанием поведения пользователей на страницах с хэшем
2. Реализуйте интерфейсную маршрутизацию по истории
В интерфейсе History HTML5 объект History является низкоуровневым интерфейсом и не наследуется ни от какого интерфейса. Интерфейс History позволяет нам манипулировать историей сеансов браузера.
(1) Свойства и методы истории
История предоставляет некоторые свойства и методы.
Свойства истории:
- History.length: возвращает количество записей в истории сеанса, включая текущую страницу сеанса. Кроме того, если открывается новая вкладка, то значение этой длины равно 1.
- История.состояние: Сохраняет метод, который вызовет событие popState, и переданный объект атрибута (будет подробно описано в методах pushState и replaceState позже).
Метод истории:
-
History.back(): вернуться на предыдущую страницу в истории сеансов браузера, так же, как кнопка «Назад» в браузере.
-
History.forward(): указывает на следующую страницу в истории сеансов браузера, так же, как кнопка браузера вперед.
-
History.go(): вы можете перейти к указанной странице записи в истории сеансов браузера.
-
History.pushState(): pushState может поместить данные в стек истории сеансов браузера.Этот метод принимает 3 параметра, объект, заголовок и строку URL-адресов. После pushState URL-адрес текущей страницы будет изменен, но не будет обновлен
-
History.replaceState(): replaceState заменяет url страницы текущей сессии на указанные данные, после replaceState url текущей страницы тоже будет изменен, но страница не будет обновляться.
В приведенном выше методе одна и та же точка pushState и ответа:
То есть URL-адрес, отображаемый на текущей странице, будет изменен, но страница не будет обновлена.
разница:
pushState помещается в стек истории сеансов браузера, что увеличивает History.length на 1, а replaceState заменяет текущую историю сеансов, поэтому History.length не будет увеличиваться.
(2) История объекта спецификации
История является важным атрибутом в объектной модели спецификации браузера.История полностью наследует интерфейс истории, поэтому она имеет все атрибуты и методы истории.
Здесь мы в основном рассмотрим свойство history.length и методы history.pushState и history.replaceState.
- history.pushState(stateObj,title,url) or history.replaceState(stateObj,title,url)
pushState и replaceState принимают 3 параметра, а именно объект состояния, заголовок заголовка и измененный URL-адрес.
window.history.pushState({foo:'bar'}, "page 2", "bar.html");
В этот момент текущий URL становится:
Перед выполнением вышеуказанного метода:http://localhost:3000После выполнения вышеуказанного метода:http://localhost:3000/bar.html
Если мы выведем window.history.state:
console.log(window.history.state); // {foo:'bar'}
window.history.state — это первый объектный параметр нашего pushState.
-
Метод history.replaceState() не изменяет длину хитроя
console.log(window.history.length); window.history.replaceState({foo:'bar'}, "page 2", "bar.html"); console.log(window.history.length);
Длина окна.история.длина двух вышеуказанных выходных данных одинакова.
также.
Каждый раз, когда срабатывает history.back() или кнопка «Назад» браузера, срабатывает событие popstate, которое происходит при переходе назад или вперед:
window.onpopstate=function(event){
}
Уведомление: Методы history.pushState и history.replaceState не запускают события popstate.
Если вы используете историю как основу для маршрутизации, вам нужно использовать history.pushState и history.replaceState, вы можете изменить адрес url без обновления, а если страница откатится назад или вперед, будет запущено событие popstate.
Преимущества маршрутизации на основе истории:
- Поисковая система
- Легко подсчитать поведение пользователей
недостаток:
- Совместимость не так хороша, как хеш
- Бэкенд должен быть настроен соответствующим образом, иначе при прямом доступе к подстранице возникнет ошибка 404.
3. Использование React-router4.0
Разобравшись с принципом реализации фронтенд-маршрутизации, давайте представим React-router4.0. В кодовую базу React-router 4.0 включены следующие независимые пакеты согласно сценариям использования:
- react-router : основной код react-router4.0
- react-router-dom : создавать веб-приложения, основной пакет в сцене объектов DOM.
- react-router-native : подходит для создания реагирующих приложений.
- react-router-config : настроить статические маршруты
- react-router-redux : Настройте маршрутизацию в сочетании с избыточностью.Это устарело и устарело.
В react-router4.0 следуйте концепции дизайна Just Component:
Все предоставляемые API представлены в виде компонентов.
Например, такие API, как BrowserRouter, Router, Link и Switch, используются в виде компонентов.
1. API общих компонентов React-router-dom
Давайте возьмем пакет React-router-dom в React-router 4.0, чтобы представить часто используемые BrowserRouter, HashRouter, Link и Router.
(1) <BrowserRouter>
После обертывания всей системы приложений компонентом
Компонент
-
basename: string Этот атрибут представляет собой подкаталог, который добавляет значение с именем basename к текущему URL-адресу.
<BrowserRouter basename="test"/>
Если установлено свойство basename, то в это время:
http://localhost:3000а такжеhttp://localhost:3000/testУказывает тот же адрес и отображает тот же контент.
-
getUserConfirmation: func Это свойство используется для подтверждения функции навигации. Использовать window.confirm по умолчанию
-
forceRefresh: bool Значение по умолчанию — false, что означает, что страница не будет обновляться при смене маршрута.Если текущий браузер не поддерживает историю, то при установленном для forceRefresh значении true каждый раз при смене URL-адреса вся страница будет обновлен.
-
keyLength: число указывает длину ключевого атрибута местоположения. В react-router есть местоположение, соответствующее каждому URL-адресу, и значение ключа местоположения каждого URL-адреса отличается. Этот атрибут обычно использует значение по умолчанию. маленькое значение.
-
Children: node Свойство Children должно быть узлом ReactNode, представляющим единственный элемент, который нужно отобразить.
(2) <Route>
Компонент
Давайте сначала посмотрим, как выполнить сопоставление и определить атрибуты сопоставления адреса
-
путь: когда URL-адрес в местоположении изменяется, он будет соответствовать атрибуту пути в маршруте.Путь определяет эффект рендеринга, связанный с маршрутом или URL-адресом.
-
точно: Если точно, только URL-адрес точно совпадает с путем, он будет совпадать. Если нет точного атрибута, адреса URL-адресов не совсем совпадают и также будут совпадать.
Например, когда точное не установлено:
<Route path='/home' component={Home}/>
<Route path='/home/first' component={First}/>
На данный момент URL-адрес:http://localhost:3000/home/first, он будет соответствовать не только компоненту First, если path='/home/first', но и маршрутизатору, если path='home'.
Если установлено точное:
<Route path='/home' component={Home}/>
Только http://localhost:3000/home/first не будет соответствовать компоненту Home, только URL-адрес точно совпадает с путем, и только http://localhost:3000/home может успешно соответствовать компоненту Home.
- strict: В отличие от точного, строгий атрибут является только дополнением к точному атрибуту.После того, как строгий атрибут установлен, он строго ограничен, но косая черта "/".
Например, если параметр strict не установлен:
<Route path='/home/' component={Home}/>
На данный момент http://localhost:3000/home иhttp://localhost:3000/home/может соответствовать компоненту Home. Сопоставление более слабое для косой черты "/". Если установлен строгий атрибут:
<Route path='/home/' component={Home}/>
Затем, существует ли косая черта строгого соответствия в это время,http://localhost:3000/homeне будет соответствовать компоненту Home.
Когда компонент маршрута успешно соответствует URL-адресу, он продолжит рендеринг. Итак, какой атрибут определяет, какой компонент или стиль отображать, а компонент, визуализация и дочерние элементы Route определяют отображаемое содержимое.
- компонент: это свойство принимает компонент React, и когда URL-адрес успешно совпадает, компонент будет отображаться
- render: func Это свойство принимает функцию, которая возвращает элемент React. Когда URL-адрес успешно совпадает, возвращенный элемент визуализируется.
- дочерние элементы: аналогично рендерингу, принимает функцию, которая возвращает элемент React, но разница в том, что содержимое дочерних элементов всегда будет отображаться независимо от того, соответствует ли URL-адрес пути текущего маршрута или нет.
И методы или компоненты, принимаемые этими тремя свойствами, будут иметь три параметра местоположения, совпадения и истории. Если это компонент, местоположение, совпадение и история будут переданы по ссылке в свойствах компонента.
(3) <Link>
- нанизывать Значение атрибута to может быть строкой, которая совпадает с href тега a в html.Даже если значение атрибута to является строкой, щелкнув тег Link, чтобы перейти к маршруту соответствующего путь также изменит историю, местоположение, совпадение. Эти три объекта передаются в реквизиты компонента, соответствующего маршруту.
Например:
<Link to='/home'>Home</Link>
Как показано выше, когда to принимает строку, он переходит к маршруту, чей URL-адрес «/home», и отображает связанный с ним компонент, чтобы принять 3 объекта: историю, местоположение и совпадение. Эти три объекта будут подробно описаны в следующем подразделе.
- для объекта Значением атрибута to также может быть объект, который может содержать следующие атрибуты: pathname, searchh, hash и state.Первые три параметра относятся к тому, как изменить URL-адрес, а последний параметр состояния — к изменению URL-адреса. соответственно Передайте параметр объекта.
Например:
<Link to={{pathname:'/home',search:'?sort=name',hash:'#edit',state:{a:1}}}>Home</Link>
В предыдущем примере to — это объект. После нажатия тега Link для перехода измененный URL-адрес: '/home?sort=name#edit'. Но при сопоставлении с соответствующим маршрутом он соответствует только компонентам, путь которых «/home», «/home?sort=name#edit». Параметры после '/home' не используются в качестве критериев соответствия, а передаются только в качестве параметров совпавшему компоненту. Кроме того, state={a:1} также передается в качестве параметра вновь визуализируемому компоненту.
(4) Объект истории передается реквизитам компонента в React-router.
После введения
Как мы упоминали ранее, если маршрут соответствует соответствующему измененному URL-адресу, будет отображаться новый компонент. Реквизиты в новом компоненте включают три атрибута объекта: историю, местоположение и совпадение. Среди них наиболее важным является атрибут объекта истории. .
Также возьмите следующий пример для иллюстрации:
<Link to={{pathname:'/home',search:'?sort=name',hash:'#edit',state:{a:1}}}>Home</Link>
<Route exact path='/home' component={Home}/>
Мы использовали
// props中的history
action: "PUSH"
block: ƒ block()
createHref: ƒ createHref(location)
go: ƒ go(n)
goBack: ƒ goBack()
goForward: ƒ goForward()
length: 12
listen: ƒ listen(listener)
location: {pathname: "/home", search: "?sort=name", hash: "#edit", state: {…}, key: "uxs9r5"}
push: ƒ push(path, state)
replace: ƒ replace(path, state)
Из приведенной выше разбивки собственности:
-
push:f Этот метод используется для изменения URL-адреса в js. Ранее URL-адрес можно было изменить в виде HTML-тегов в компоненте Link. Метод push сопоставляется с методом pushState в window.history.
-
replace: f Этот метод также используется для изменения URL-адреса в js, а метод замены сопоставляется с методом replaceState в window.history.
-
block: f Этот метод также очень полезен, например, когда пользователь покидает текущую страницу, пользователю дается текстовое приглашение, а также приглашение вида history.block("Вы уверены, что хотите покинуть текущую страницу? ") может быть использован.
-
go / goBack / goForward
Методы истории go, goBack и goForward в реквизитах компонента соответствуют window.history.go, window.history.back и window.history.forward соответственно.
- действие: "НАЖАТЬ" || "ПОЛНЯТЬ" Атрибут действия очень большой, если текущий url изменить через тег Link или метод this.props.push в js, то действие в новом компоненте будет "PUSH", иначе - "POP".
Атрибут действия очень полезен. Например, когда мы делаем анимацию перелистывания страниц, анимация вперед — это SlideIn, а анимация назад — SlideOut. Мы можем судить, какую анимацию использовать, в соответствии с действием в компоненте:
function newComponent (props)=>{
return (
<ReactCSSTransitionGroup
transitionAppear={true}
transitionAppearTimeout={600}
transitionEnterTimeout={600}
transitionLeaveTimeout={200}
transitionName={props.history.action==='PUSH'?'SlideIn':'SlideOut'}
>
<Component {...props}/>
</ReactCSSTransitionGroup>
)
}
-
местоположение: объект В атрибуте местоположения нового компонента записываются параметры, переданные от предыдущего компонента.Из приведенного выше примера мы видим, что значение местоположения в это время равно:
hash: "#edit" key: "uxs9r5" pathname: "/home" search: "?sort=name" state: {a:1}
За исключением ключа, который используется как единственное представление, другие атрибуты — это параметры, которые мы передали из предыдущего тега Link.
Четыре, анализ исходного кода React-router4.0
В третьем разделе мы представили общее использование React-router и прочитали исходный код React-router 4.0.
Здесь мы в основном анализируем, как React-router 4.0 реализует интерфейсную маршрутизацию на основе window.history, поэтому спроектированными компонентами являются BrowserRouter, Router, Route и Link.
1. История в React-маршрутизаторе
Из введения в предыдущем разделе мы знаем, что в пропсах есть объект истории, который передается вновь отображаемому компоненту, щелкнув тег Link.Этот объект имеет богатый контент, такой как: действие, возврат, переход, местоположение, нажатие, и заменить методы.
React-router создает класс History для создания экземпляров с более богатыми свойствами на основе window.history. После создания экземпляра класса History у него есть такие методы, как action, goBack, location и так далее.
React-router разделяет метод построения этого нового класса History на пакет узлов с именем history.
npm install history -s
Его можно ввести с помощью вышеуказанного метода, давайте посмотрим на реализацию этого класса History.
const createBrowserHistory = (props = {}) => {
const globalHistory = window.history;
......
//默认props中属性的值
const {
forceRefresh = false,
getUserConfirmation = getConfirmation,
keyLength = 6,
basename = '',
} = props;
const history = {
length: globalHistory.length,
action: "POP",
location: initialLocation,
createHref,
push,
replace,
go,
goBack,
goForward,
block,
listen
}; ---- (1)
const basename = props.basename;
const canUseHistory = supportsHistory(); ----(2)
const createKey = () =>Math.random().toString(36).substr(2, keyLength); ----(3)
const transitionManager = createTransitionManager(); ----(4)
const setState = nextState => {
Object.assign(history, nextState);
history.length = globalHistory.length;
transitionManager.notifyListeners(history.location, history.action);
}; ----(5)
const handlePopState = event => {
handlePop(getDOMLocation(event.state));
};
const handlePop = location => {
if (forceNextPop) {
forceNextPop = false;
setState();
} else {
const action = "POP";
transitionManager.confirmTransitionTo(
location,
action,
getUserConfirmation,
ok => {
if (ok) {
setState({ action, location });
} else {
revertPop(location);
}
}
);
}
}; ------(6)
const initialLocation = getDOMLocation(getHistoryState());
let allKeys = [initialLocation.key]; ------(7)
// 与pop相对应,类似的push和replace方法
const push ... replace ... ------(8)
return history ------ (9)
}
-
(1) В объекте истории указывается, что новый метод построения История вернула как атрибут.
-
(2) Метод определения SupportHistory текущего браузера window.history для совместимости следующим образом:
export const supportsHistory = () => { const ua = window.navigator.userAgent; if ( (ua.indexOf("Android 2.") !== -1 || ua.indexOf("Android 4.0") !== -1) && ua.indexOf("Mobile Safari") !== -1 && ua.indexOf("Chrome") === -1 && ua.indexOf("Windows Phone") === -1 ) return false; return window.history && "pushState" in window.history; };
Из приведенного выше дискриминанта видно, что window.history абсолютно поддерживается в chrome, mobile safari и windows phone, но не поддерживает Android 2.x и Android 4.0.
-
(3) используется для создания уникального идентификационного ключа с указанным количеством цифр, связанных с каждой записью URL-адреса в истории, а длина ключа по умолчанию составляет 6 цифр.
-
(4) Метод createTransitionManager в (4) возвращает интегрированный объект, который содержит исторический адрес или функцию слушателя при изменении объекта Конкретный код выглядит следующим образом:
const createTransitionManager = () => { const setPrompt = nextPrompt => { }; const confirmTransitionTo = ( location, action, getUserConfirmation, callback ) => { if (typeof getUserConfirmation === "function") { getUserConfirmation(result, callback); } else { callback(true); } } }; let listeners = []; const appendListener = fn => { let isActive = true; const listener = (...args) => { if (isActive) fn(...args); }; listeners.push(listener); return () => { isActive = false; listeners = listeners.filter(item => item !== listener); }; }; const notifyListeners = (...args) => { listeners.forEach(listener => listener(...args)); }; return { setPrompt, confirmTransitionTo, appendListener, notifyListeners };
};
Функция setPrompt используется для установки текстовой подсказки, которая появляется при переходе по URL-адресу.Функция confirmTransaction будет генерировать местоположение, действие, обратный вызов и другие параметры нового объекта истории.Входящие объекты местоположения и действия.
Затем мы видим, что существует массив слушателей, в котором хранится серия массивов событий прослушивания, связанных с URL-адресами. С помощью следующего метода appendListener события могут быть добавлены в этот массив, а с помощью метода notifyListeners все события в массиве слушателей могут пройти и выполнить.
-
(5) Метод setState, который возникает, когда URL-адрес истории или действие истории изменяется, этот метод обновит атрибуты в объекте истории и вызовет метод notifyListeners, передав текущую историю. .действие. Пройдите и выполните все прослушиватели событий, которые прослушивают изменения URL-адреса.
-
(6) Этот метод getDOMLocation предназначен для создания объекта атрибута местоположения новой истории на основе текущего значения в window.state.AllKeys — это ключ, связанный с историческим URL-адресом, который всегда поддерживается при изменении URL-адреса и хранится глобально, и allKeys выполняется.При создании "POP" или "PUSH", "Repalce" и других методов, которые изменяют URL-адрес, он будет поддерживать обновление в реальном времени.
-
(7) Метод handlePop используется для обработки события "POP". Мы знаем, что щелчок назад в window.history вызовет событие "POP". То же самое и здесь. Действие выполнения - "POP", которое будет срабатывает при бэк.функции.
-
(8) содержит методы push и replace, аналогичные методу pop. То же самое, что делает метод push, заключается в выполнении действия как «PUSH» («REPLACE»), которое изменяет значение в массиве allKeys. Единственное отличие состоит в том, что действие заключается в том, что метод «PUSH» нажимает на добавление в массив allKeys, а метод «REPLACE» для замены действия — на замену текущего элемента.
-
(9) Верните вновь сгенерированный объект истории.
2. Компонент Link в React-маршрутизаторе
На самом деле, самое сложное для понимания — это как пересобрать функцию фабрики истории в React-router.В первом разделе мы подробно представили исходный код функции генерации истории createBrowserHistory, а дальше легко посмотреть компонент «Ссылка».
Во-первых, компонент Link похож на тег a в HTML, и его назначение очень простое: активировать метод изменения URL-адреса и метод активного изменения URL-адреса.Из приведенного выше введения в историю, известно, что используются методы push и replace, поэтому исходный код компонента Link для:
class Link extends React.Component {
handleClick = event => {
...
const { history } = this.context.router;
const { replace, to } = this.props;
if (replace) {
history.replace(replace);
} else {
history.push(to);
}
}
};
render(){
const { replace, to, innerRef, ...props } = this.props;
<a {...props} onClick={this.handleClick}/>
}
}
Приведенный выше код очень прост: получите историю из глобального объекта контекстного API React, а затем выполните history.replace(to), если replace имеет значение true в свойствах, переданных компоненту Link, to является объектом, содержащим путь, если он передан. свойство компонента Link имеет значение false, выполняется метод history.push(to).
3. Компонент роута в React-router
Компонент Route тоже очень простой, он принимает один из самых важных атрибутов path в своих реквизитах, Route делает только одно:
Когда URL-адрес изменяется, атрибут пути сравнивается с измененным URL-адресом, и, если совпадение успешно, отображается компонент, назначенный компонентом или дочерним атрибутом компонента.
Конкретный исходный код выглядит следующим образом:
class Route extends React.Component {
....
constructor(){
}
render() {
const { match } = this.state;
const { children, component, render } = this.props;
const { history, route, staticContext } = this.context.router;
const location = this.props.location || route.location;
const props = { match, location, history, staticContext };
if (component) return match ? React.createElement(component, props) : null;
if (render) return match ? render(props) : null;
if (typeof children === "function") return children(props);
if (children && !isEmptyChildren(children))
return React.Children.only(children);
return null;
}
}
Совпадение в состоянии является меткой того, соответствует ли оно.Если оно соответствует пути текущего маршрута, то компонент React, на который он указывает, отображается в соответствии с атрибутом компонента порядка приоритета, атрибутом рендеринга и атрибутом потомков.
4. Компонент Router в React-router
В компоненте Router это базовый компонент BrowserRouter, HashRouter и других компонентов. В этом компоненте определена функция сопоставления, содержащая правило сопоставления, а метод прослушивателя в новой истории используется для отслеживания изменения URL-адреса, так что при изменении URL-адреса результаты isMatch различных компонентов пути в маршрутизаторе измененный.
class Router extends React.Component {
componentWillMount() {
const { children, history } = this.props
//调用history.listen监听方法,该方法的返回函数是一个移除监听的函数
this.unlisten = history.listen(() => {
this.setState({
match: this.computeMatch(history.location.pathname)
});
});
}
componentWillUnmount() {
this.unlisten();
}
render() {
}
}
Вышеупомянутый сначала вызывает метод мониторинга прослушивателя, прежде чем компонент будет создан для отслеживания изменения URL-адреса и обновления результата isMatch в режиме реального времени.
5. Резюме
Начиная с принципа внешней маршрутизации, в этой статье представлены два часто используемых метода внешней маршрутизации, затем представлен API базового компонента и использование React-router, подробно представлен недавно созданный объект истории в компонентах React-router, и, наконец, объединяет React.-Router API Прочтите исходный код React-router.