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