В React имеется своя система маршрутизации, которая позволяет сопоставлять запросы к приложению с определенными компонентами. Ключевым звеном в работе маршрутизации является модуль react-router, который содержит основной функционал по работе с маршрутизацией. Однако если мы собираемся работать в браузере, то нам также надо использовать модуль react-router-dom, а также history
Итак, определим в проекте следующий файл index.html:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Маршруты в React</title> </head> <body> <div id="app"></div> <script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script> <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script> <script src="https://unpkg.com/history@5/umd/history.production.min.js" crossorigin></script> <script src="https://unpkg.com/react-router@6.3.0/umd/react-router.production.min.js" crossorigin></script> <script src="https://unpkg.com/react-router-dom@6.3.0/umd/react-router-dom.production.min.js" crossorigin></script> <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script> <script type="text/babel"> const Router = ReactRouterDOM.BrowserRouter; const Route = ReactRouterDOM.Route; const Routes = ReactRouterDOM.Routes; function About(){ return <h2>О сайте</h2>; } function NotFound(){ return <h2>Ресурс не найден</h2>; } function Main(){ return <h2>Главная</h2>; } ReactDOM.createRoot( document.getElementById("app") ) .render( <Router> <div> <Routes> <Route path="/" element={<Main />} /> <Route path="/about" element={<About />} /> <Route path="*" element={<NotFound />} /> </Routes> </div> </Router> ); </script> </body> </html>
Аналогичный пример с использованием классов-компонентов:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Маршруты в React</title> </head> <body> <div id="app"></div> <script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script> <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script> <script src="https://unpkg.com/history@5/umd/history.production.min.js" crossorigin></script> <script src="https://unpkg.com/react-router@6.3.0/umd/react-router.production.min.js" crossorigin></script> <script src="https://unpkg.com/react-router-dom@6.3.0/umd/react-router-dom.production.min.js" crossorigin></script> <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script> <script type="text/babel"> const Router = ReactRouterDOM.BrowserRouter; const Route = ReactRouterDOM.Route; const Routes = ReactRouterDOM.Routes; class About extends React.Component{ render(){ return <h2>О сайте</h2>; } } class NotFound extends React.Component{ render(){ return <h2>Ресурс не найден</h2>; } } class Main extends React.Component{ render(){ return <h2>Главная</h2>; } } ReactDOM.createRoot( document.getElementById("app") ) .render( <Router> <div> <Routes> <Route path="/" element={<Main />} /> <Route path="/about" element={<About />} /> <Route path="*" element={<NotFound />} /> </Routes> </div> </Router> ); </script> </body> </html>
Прежде всего для работы с маршрутами необходимо добавить ссылки на модули react-router-dom, react-router и history:
<script src="https://unpkg.com/history@5/umd/history.production.min.js" crossorigin></script> <script src="https://unpkg.com/react-router@6.3.0/umd/react-router.production.min.js" crossorigin></script> <script src="https://unpkg.com/react-router-dom@6.3.0/umd/react-router-dom.production.min.js" crossorigin></script>
В системе маршрутизации каждый маршрут сопоставляется с определенным компонентом, поэтому для примера я определил три однотипных компонента: Main, About и NotFound.
В начале для работы с маршрутами также получаем ряд объектов, которые потребуются для определения маршрутов:
const Router = ReactRouterDOM.BrowserRouter; const Route = ReactRouterDOM.Route; const Routes = ReactRouterDOM.Routes;
Здесь определены три объекта из модуля react-router-dom.
Router определяет набор маршрутов и, когда к приложению, приходит запрос, то Router выполняет сопоставление запроса с маршрутами. И если какой-то маршрут совпадает с URL запроса, то этот маршрут выбирается для обработки запроса.
И также для выбора маршрута определен объект Routes. Он содержит набор маршрутов и позволяет выбрать первый попавшийся маршрут и его использовать для обработки.
Каждый маршрут представляет объект Route. Он имеет ряд атрибутов. В частности, здесь для маршрута устанавливаются два атрибута:
path: шаблон адреса, с которым будет сопоставляться запрошенный адрес URL
element - тот компонент, который отвечает за обработку запроса по этому маршруту
Например, первый маршрут выступает в качестве корневого. Он сопоставляется с адресом "/" и обрабатывается компонентом Main:
<Route path="/" element={<Main />} />
Второй маршрут будет сопоставляться с адресом "/about", а обрабатываться он будет компонентом About.
<Route path="/about" element={<About />} />
Особо следует выделить третий маршрут:
<Route path="*" element={<NotFound />} />
Путь в виде звездочки - "*" указывает, что этот маршрут будет сопоставляться со всеми адресами URL, которые не соответствуют предыдущим маршрутам. И он будет обрабатываться компонентом NotFound. Таким образом мы можем задать обработку при обращении к несуществующим ресурсам в приложении.
При работе с маршрутами следует учитывать, что мы не сможем просто кинуть страницу index.html в браузер, и у нас все заработает, как в прошлых статьях. Чтобы система маршрутизации заработала, нам надо разместить файл index.html на веб-сервере. В качестве веб-сервера можно использовать любой понравивший веб-сервер (Apache, IIS, Ngnix и т.д.) или обращаться к данной html-странице в рамках веб-приложения. В данном же случае я буду использовать Node.js как самый демократичный и распространенный вариант.
Итак, напишем небольшое приложение Node.js для запуска html-страницы index.html. Для этого добавим в тут же папку, где находится файл index.html, новый файл app.js со следующим кодом:
const http = require("http"); const fs = require("fs"); http.createServer(function(request, response){ fs.readFile("index.html", function(error, data){ response.end(data); }); }).listen(3000, function(){ console.log("Server started at 3000"); });
В начале файла подключаются модули http (для запуска веб-сервера и обработки входящих запросов) и fs (для считывания файла index.html из файловой системы).
Далее создаем собственно веб-сервер с помощью функции http.createServer()
. В качестве параметра она принимает функцию, которая будет
запускаться для обработки каждого приходящего на сервер запроса. И эта функция имеет два параметра: request
- объект, который хранит все
данные запроса, и response
- объект, который позволяет определить нам ответ на запрос.
В данном случае у нас предельно простая ситуация - нам надо просто отправить статический файл index.html. Для этого
с помощью функции fs.readFile()
считываем из файловой системы запрошенный файл и с помощью метода response.end()
отправляем считанный данные пользователю.
И в конце функция listen()
запускает созданный веб-сервер на 3000-м порту, и после этого мы сможем обращаться к приложению по адресу
"http://localhost:3000".
Теперь запустим приложение. Для этого откроем командную строку/терминал и перейдем с помощью команды cd к папке проекта. Затем запустим приложение с помощью команды
node app
После запуска сервера обратимся в браузере по адресу http://localhost:3000/, и по умолчанию сработает маршрут "/", который обрабатывается компонентом Main:
Обратимся теперь по адресу http://localhost:3000/about:
Этот запрос будет обрабатываться компонентом About, так как он отвечает за обработку маршрута, который совпадает с запросом "/about".
Запросы по всем другим адресам, которые не соответствуют предыдущим маршрутам, будут обрабатываться компонентом NotFound.
С помощью атрибута element объекта Route также можно без применения классов динамически определить содержимое компонента, который будет обрабатывать маршрут:
<Router> <Routes> <Route path="/" element={<Main />} /> <Route path="/about" element={<h2>About</h2>} /> <Route path="/contact" element={(<h2>Contacts</h2>)} /> <Route path="*" element={<NotFound />} /> </Routes> </Router>
При определении маршрутов следует учитывать, что действует точное сопоставление с шаблоном, который указан в свойстве path элемента Route. Например, возьмем второй маршрут:
<Route path="/about" element={<h2>About</h2>} />
Данный маршрут соответствует запросам:
http://localhost:3000/about
http://localhost:3000/about/
Однако запросу http://localhost:3000/about/25
этот маршрут уже НЕ соответствует.