routing.md 7.4 KB

SPA && ROUTING

Предыстория

Со времен зарождения современного фронтенда были попытки сделать продвинутый фронт с многостраничным интерфейсом. Технически это не представляет особой сложности, однако нет особого смысла в многостраничном интерфейсе без ссылок на эти страницы. С этим же были проблемы - при любой попытке смены адресной строки браузер упорно переходил на другую страницу, т.е. обращался к серверу и сбрасывал JS, подгружая новые скрипты. Это если страница реально существовала на сервере. Обычно же 404.

# -ссылки

При переходе по ссылкам внутри страницы (используя ) браузер, очевидно, страницу не перезагружает. Этим длительное время пользовались первые SPA типа gmail. На таких сервисах роутинг, аналогичный роутингу в express реализуется после символа # в адресе, таким образом с точки зрения браузера все эти "переходы" по ссылкам происходят внутри одной страницы, чем и пользуется JS, прослушивая события изменения адреса и перерисовывая содержимое страницы.

HTML5

HTML5 позволяет отлавливать любое изменение адреса, а так же управлять историей браузера. Таким образом можно полностью просимулировать работу страниц внутри SPA, как будто бы они загружаются с сервера.

Почему это называется SPA ?

Потому что физически это одна страница, которая просто показывает пользователю фейковые адреса и разный DOM.

Как это устроено

По образу и подобию роутинга в express, только адрес, который сопоставляется с шаблоном приходит не из браузера на бэк, а обрабатывается в самом браузере.

React

С точки зрения React каждая страница - это компонент. Роутинг прописывается в декларативной форме в JSX.

Установка

$ npm install --save react-router-dom

Подключение

import {Router, Route, Link} from 'react-router-dom';
import createHistory from "history/createBrowserHistory";

JSX

<Router history = {createHistory()}>
    <div>
        <Route path="/chat/:param1/:param2" component = {ChatPage} />
        <Route path="/" component = { MainPage } exact />
    </div>
</Router>
  • Router обрамляет все маршруты и обеспечивает интеграцию с Browser History API
  • Route принимает props:
    • path - шаблон адреса, как в express (обратите внимание на param1 и param2)
    • component - класс компонента.
    • exact - нужен что бы маршрут срабатывал только при полном совпадении.

Компоненты

Принимают параметры шаблона адреса в props

class ChatPage extends Component {
  render() {
    console.log(this.props)
    return (
        <div className="App">
            {this.props.match.params.param1} <br/>
            {this.props.match.params.param2}
        </div>
    )
  }
}

Link

Компонент Link предназначен для создания ссылок.

<Link to='/'>...go to main</Link>

Switch

Компонент Switch выбирает первый подходящий маршрут, остальные игнорирует:

<Router history = {createHistory()}>
    <div>
        <Switch>
            <Route path="/chat/" component = { ChatPage } />
            <Route path="/" component = { MainPage } exact />
            <Route component = { NotFound } />
        </Switch>
    </div>
</Router>

Redirect

Иногда нужно назначить перенаправление с одного адреса на другой. В таком случае поможет компонент Redirect:

<Router history = {createHistory()}>
    <div>
        <Switch>
            <Route path="/chat/" component = {ChatPage} />
            <Route path="/" component = { MainPage } exact />
            <Redirect from="/main" to="/" />
            <Route component = { NotFound } />
        </Switch>
    </div>
</Router>

При посещении адреса /main будет автоматически происходить переход на / и работать компонент MainPage

Переход на другую страницу из кода

Зачастую после сохранения тех или иных форм нужно перейти на другую страницу, т. е. сменить адрес в адресной строке таким образом, что бы React Router это обработал согласно конфигурации маршрутов.

Это можно сделать с помощью рендеринга компонента Redirect без from.

Вложенные маршруты

В относительно крупных проектах возникает надобность разделить разные части сайта на разные наборы маршрутов.

Например:

  • / Главная страница
  • /dashboard Личный кабинет
  • ...
  • /admin Админка
    • /admin/
    • /admin/dashboard Админский кабинет
    • ...

В такой структуре логично отделить маршруты общей и административной части сайта.

//App
<Router history = {createHistory()}>
    <div>
        <Switch>
            <Route path="/" component = { MainPage } exact />
            <Route path="/dashboard" component = { DashboardPage } />
            <Route path="/admin" component = { AdminPage } />
            <Route component = { NotFound } />
        </Switch>
    </div>
</Router>

...

class AdminPage extends Component {
    render(){
        let root = this.props.match.url
        return(
            <div>
                <Route path={`${root}/`} 
                        component={ AdminMainPage }
                    />
                <Route path={`${root}/dashboard`} 
                        component={ AdminDashboardPage }
                    />
            </div>
        )
    }
}