React.js. Маршрутизация. Часть первая из трех
22.08.2021
Теги: Frontend • JavaScript • React.js • Web-разработка • Маршрутизация • Навигация
В React имеется своя система маршрутизации, которая позволяет сопоставлять запросы к приложению с определенными компонентами. Ключевым звеном в работе маршрутизации является модуль react-router
, который содержит основной функционал по работе с маршрутизацией. Для браузерных приложений предназначен модуль react-router-dom
, для мобильных приложений — react-router-native
.
Простое приложение
Давайте создадим простое приложение, у которого будет три страницы — Home
, About
и Contact
:
> npm install react-create-app
[src] App.js [components] Header.js Footer.js Content.js [pages] Home.js About.js Contact.js
import React from 'react'; import Header from './components/Header'; import Footer from './components/Footer'; import Content from './components/Content'; export default function App() { return ( <React.Fragment> <Header /> <Content /> <Footer /> </React.Fragment> ); }
export default function Content() { return ( <main className="container"> <h1>React Router</h1> </main> ); }
export default function Home() { return <h1>Home page</h1> }
export default function About() { return <h1>About page</h1> }
export default function Contsct() { return <h1>Contact page</h1> }
Компоненты Router и Route
Теперь установим модуль react-router-dom
:
> npm install react-router-dom
Нам потребуется импортировать BrowserRouter
и Route
:
import {BrowserRouter as Router, Route} from 'react-router-dom'; import Home from '../pages/Home'; import About from '../pages/About'; import Contact from '../pages/Contact'; export default function Content() { return ( <main className="container"> <Router> <Route path="/" component={Home} /> <Route path="/about" component={About} /> <Route path="/contact" component={Contact} /> </Router> </main> ); }
Здесь присходит следующее — перебираются все элементы Route
внутри Router
и если есть совпадение текущего URL с атрибутом path
— вызывается компонент, который указан в атрибуте component
. Запустим наше приложение:
> npm start
И увидим на главной странице результат работы компонента Home
:
<h1>Home page</h1>
Атрибуты exact и strict
Наберем в адресной строке /about
— и увидим сразу два заголовка:
<h1>Home page</h1> <h1>About page</h1>
Атрибут path
— это шаблон, который означает «URL страницы начинается с этой строки». То есть, если path="/about"
, то этому шаблону будут соответствовать URL-ы /about
, /about/one
, /about/one/two
— потому что все они начинаются на /about
.
В нашем случае произошло следующее. У главной страницы path
— это слэш, а наш URL в адресной строке браузера — это /about
. Поскольку URL начинается со слэша, то шаблон path="/"
срабатывает. Чтобы такого не происходило, можно использовать атрибуты exact
(точный) и strict
(строгий).
export default function Content() { return ( <main className="container"> <Router> <Route exact path="/" component={Home} /> <Route path="/about" component={About} /> <Route path="/contact" component={Contact} /> </Router> </main> ); }
Атрибут exact
требует точного совпадения текущего URL и шаблона path
. Теперь путь «слэш» будет соответствовать только URL главной страницы. Но у нас по прежнему работают URL /about/one
и /about/one/two
— давайте исправим это.
export default function Content() { return ( <main className="container"> <Router> <Route exact path="/" component={Home} /> <Route exact path="/about" component={About} /> <Route exact path="/contact" component={Contact} /> </Router> </main> ); }
Сейчас страница About
(и страница Contact
) доступна по двум адресам — это /about
и /about/
. Атрибут strict
позволяет избавиться от дублей.
export default function Content() { return ( <main className="container"> <Router> <Route exact path="/" component={Home} /> <Route exact strict path="/about" component={About} /> <Route exact strict path="/contact" component={Contact} /> </Router> </main> ); }
sensetive
, который делает шаблон чувствительным к регистру. То есть, если path="/about"
, то URL /About
уже не будет совпадать.
Компонент Switch
Мы уже знаем, что внутри Router
перебираются все элементы Route
— даже если совпадение уже найдено. Но мы можем изменить это поведение с помощью Switch
, который позволяет выйти из перебора при первом совпадении.
import {BrowserRouter as Router, Route, Switch} from 'react-router-dom'; import Home from '../pages/Home'; import About from '../pages/About'; import Contact from '../pages/Contact'; export default function Content() { return ( <main className="container"> <Router> <Switch> <Route exact path="/" component={Home} /> <Route exact strict path="/about" component={About} /> <Route exact strict path="/contact" component={Contact} /> </Switch> </Router> </main> ); }
Компонент NotFound
Осталась еще одна проблема. Если не было совпадений при переборе маршрутов — пользователь увидит пустую страницу. Так что давайте создадим еще один компонент NotFound
.
export default function NotFound() { return <h1>Page not found</h1> }
export default function Content() { return ( <main className="container"> <Router> <Switch> <Route exact path="/" component={Home} /> <Route exact strict path="/about" component={About} /> <Route exact strict path="/contact" component={Contact} /> <Route component={NotFound} /> </Switch> </Router> </main> ); }
Здесь присходит следующее — перебираются все элементы Route
внутри Router
до первого совпадения текущего URL с атрибутом path
. Если совпадений не было — отработает компонент NotFound
.
Компоненты Link и NavLink
Все это хорошо, но мы набираем адрес страницы вручную в адресной строке браузера, а хотелось бы кликать по ссылкам. Давайте создадим компонент NavBar
, в котором у нас будут ссылки, но не простые ссылки, а особенные.
import {Link} from 'react-router-dom'; export default function NavBar() { return ( <nav> <Link to="/">Home</Link> <Link to="/about">About</Link> <Link to="/contact">Contact</Link> </nav> ) }
export default function Content() { return ( <main className="container"> <Router> <NavBar /> <Switch> <Route exact path="/" component={Home} /> <Route exact strict path="/about" component={About} /> <Route exact strict path="/contact" component={Contact} /> <Route component={NotFound} /> </Switch> </Router> </main> ); }
Кроме компонента Link
для создания ссылок можно использовать компонент NavLink
. Главное отличние NavLink
от Link
— возможность задать css-класс и/или css-стиль активной ссылки. Как нетрудно догадаться из названия, это может быть полезно для главного меню, чтобы пользователю было удобнее.
import {NavLink} from 'react-router-dom'; export default function NavBar() { const activeStyle = {color: 'red'}; return ( <nav> <NavLink exact to="/" activeStyle={activeStyle}>Home</NavLink> <NavLink to="/about" activeStyle={activeStyle}>About</NavLink> <NavLink to="/contact" activeStyle={activeStyle}>Contact</NavLink> </nav> ) }
import {NavLink} from 'react-router-dom'; export default function NavBar() { return ( <nav> <NavLink exact to="/" activeClassName="active">Home</NavLink> <NavLink to="/about" activeClassName="active">About</NavLink> <NavLink to="/contact" activeClassName="active">Contact</NavLink> </nav> ) }
.active {
color: red;
}
Тут тоже не надо забывать об атрибуте exact
— иначе ссылка Home
будет красной всегда.
Главное отличие Link
и NavLink
от простых ссылок — это то, что каждая новая страница не загружается с сервера. Собственно, поэтому приложение и называется Single Page Application — потому что страница загружается один раз, при первом обращении. А дальше мы только точечно изменяем ее содержимое, показывая внутри Content
то одно содержимое, то другое — без обращения к серверу. Но для пользователя это выглядит, как будто мы каждый раз показываем новую страницу. В адресной строке браузера показывается соответствующий URL, правильно работают кнопки «Назад» и «Вперед» браузера.
Атрибут basename
Если приложение развернуто в подкаталоге на сервере, необходимо использовать атрибут basename
для Router
:
<Router basename="/calendar">
<Link to="/yesterday" />
<Link to="/today" />
<Link to="/tomorrow" />
</Router>
<Router basename="/calendar">
<Route path="/yesterday" component={...} />
<Route path="/today" component={...} />
<Route path="/tomorrow" component={...} />
</Router>
Поиск: JavaScript • React.js • Web-разработка • Frontend • Маршрутизация • Навигация • Теория • Route • Router • Switch • Link • NavLink • Redirect