Резюме использования React Router

внешний интерфейс JavaScript React.js внешний фреймворк Ant Design

React Router необходим при разработке одностраничных приложений с помощью React. Когда я впервые столкнулся с React Router, я шаг за шагом следил за документацией, и хотя некоторые концепции были недостаточно понятны, проект наконец был завершен. позже прочиталReact Router 4, о котором вы не зналиЭтот пост понял, что предыдущее использование в некоторых случаях в проекте было не совсем правильным. В процессе обучения я пошел на некоторые обходные пути. Эта статья представляет собой небольшую сортировку и обобщение моего собственного опыта в надежде вдохновить читателей. Нажмите, чтобы увидеть полный адрес проектаздесь, в проекте используетсяant.design.

1. Динамическая маршрутизация

React Router v4 представил концепцию динамической маршрутизации. Динамической маршрутизации соответствует статическая маршрутизация. Если вы использовали Express или koa, вы слишком хорошо знакомы со статической маршрутизацией. В следующем примере мы используемkoa-route, привяжите маршрут к соответствующему контроллеру.

const route = require('koa-route')
 
app.use(route.get('/article', Controller1))
app.use(route.get('/article/:id', Controller2))
app.use(route.get('/article/:id/edit', Controller3))

Форма, подобная приведенной выше, является статическим маршрутом. Наиболее очевидной особенностью статической маршрутизации является то, что в коде перечислены все маршруты, которые необходимо обработать. Когда проект запускается, мы знаем соответствие между всеми маршрутами и контроллером. Проще говоря, представление маршрутизации не меняется.

React Router рассматривает все как компонент, и Route также является компонентом. Предположим, что существует маршрут, подобный следующему.

<Route path="/article" component={ArticleList} />

Если Маршрут не был отрендерен, когда мы открываем ссылку /article, компонент ArticleList вообще не отрисовывается. Маршрут вступит в силу только тогда, когда маршрут будет отрендерен. По сравнению с предыдущим статическим маршрутом текущая таблица маршрутизации изменена. Во время выполнения маршруты могут добавляться динамически. Когда страница открыта, может быть невозможно узнать соответствие между всеми маршрутами и компонентами.

Поскольку маршруты постоянно меняются, компоненты, которые мы пишем, сильно отличаются от того, что было раньше. Рассмотрим следующее одностраничное приложение.

登录页面

其他页面

Мы заметили, что страницы всего веб-сайта можно разделить на две категории: страницы входа и другие страницы. В App.js вы можете сначала добавить два маршрута.

// App.js
<Switch>
  <Route path="/login" exact component={Login} />
  <Route path="/" component={PrimaryLayout} />
</Switch>

Маршруты можно добавлять постоянно, поэтому теперь нам не нужно перечислять все маршруты в App.js. В PrimaryLayout.js добавьте необходимые маршруты.

// PrimaryLayout.js
<Switch>
  <Route path="/article" exact component={ArticleList} />
  <Route path="/article/:id" exact component={ArticleDetail} />
  <Route path="/article/:id/edit" exact component={ArticleEdit} />
</Switch>

2. Компонент маршрута

Функция компонента Route относительно проста: когда ссылка соответствует правилу сопоставления, компонент отображается. Обратите внимание, что в приведенном выше коде компонент Route вложен в компонент Switch. Когда ссылка соответствует правилам соответствия нескольких маршрутов, будут отображаться несколько компонентов. Если маршрут вложен в коммутатор, будет отображаться только первый маршрут, соответствующий правилам.

У Route есть реквизит под названием render . Установите эту функцию рендеринга, тогда вы сможете выполнять сложную логическую обработку в роутинге.

<Switch>
  <Route path="/login" exact
    render={() => auth ? <Redirect to="/product" /> : <Login  />
  <Route path="/" 
    render={() => auth ? <PrimaryLayout/> : <Redirect to="/login"/>} />
</Switch>

Переменная auth — это статус входа пользователя.Когда пользователь вошел в систему, он не может напрямую получить доступ к странице входа, а когда он не вошел в систему, он не может получить доступ к страницам, которые требуют разрешения позже. Для более сложного управления разрешениями просто напишите функцию рендеринга таким же образом.

3. Компонент маршрутизатора и история

Компонент Router является компонентом относительно низкого уровня. В реальной разработке мы обычно выбираем BrowserRouter или HashRouter.

// index.js
import { BrowserRouter } from 'react-router-dom'
ReactDOM.render(
  <Provider store={store}>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </Provider>,
  document.getElementById('root'))

И BrowserRouter, и HashRouter инкапсулируют Router и поставляются с объектом истории. Самая большая разница между ними - это разница в их собственных объектах истории.

import { createBrowserHistory, createHashHistory } from 'history'

const history = createBrowserHistory()
// 或者下面这样
// const history = createHashHistory()

<Router history={history}>
  <App/>
</Router>

Реквизиты BrowserRouter и HashRouter, такие как базовое имя, getUserConfirmation и т. д., могут быть установлены при создании объекта истории.

const history = createBrowserHistory({
  basename: '/admin'
})

4. withRouter

withRouter — этокомпоненты более высокого порядка, добавьте объекты match, location и history в реквизиты компонента. Это очень полезная функция, и ее использование проиллюстрировано ниже четырьмя небольшими примерами.

  1. Работает с подключением Redux

Ранее мы говорили, что Route — это компонент, а таблица маршрутизации постоянно меняется. Redux используется в проекте для управления данными, когда данные не изменяются, компонент не будет перерисовываться. Предполагая, что компонент маршрута в компоненте не был отрендерен и данные не изменились, даже если ссылка текущей страницы изменилась, ни один маршрут не будет соответствовать ссылке. Потому что компонент Route все еще не отображается в это время! Как узнать, изменилась ли ссылка? Вот где нужен withRouter.

import { withRouter } from 'react-router-dom'
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Component))
  1. получить текущий маршрут

Как показано на рисунке ниже, боковая панель слева должна решить, какую из них развернуть, а какую выделить в соответствии с изменением ссылки. Инкапсулируя левый компонент с помощью withRouter, компонент может реагировать на изменения в ссылке.

侧边栏

class LeftSider extends React.Component {
  componentDidMount() {
    this.setHighLightKeys(this.props)
  }

  componentWillReceiveProps(nextProps) {
    this.setHighLightKeys(nextProps)
  }

  shouldComponentUpdate(nextProps, nextState) {
    const { match, location } = this.props
    if (!(match === nextProps.match && location === nextProps.location)) {
      return true
    }
    return nextState !== this.state
  }
}

export default withRouter(LeftSider)

Обратите внимание, что функция shouldComponentUpdate только сравнивает, совпадают ли совпадение и местоположение дважды, и не сравнивает объект истории. Объект истории каждый разизменение, поэтому здесь он не используется для сравнения.

Таким же образом можно реализовать и хлебные крошки.

  1. прыжок страницы

React Router предоставляет компоненты Link, NavLink и Redirect для управления переходами между страницами. Но я управляю переходом на страницу в событии нажатия кнопки, и эти компоненты нельзя использовать. Здесь вы можете подумать об использовании объекта местоположения.

// 错误的方式!!!
location.href = '/article'

Этот способ работает, но это неправильно. Если ранее использовавшийся BrowserRouter становится HashRouter, этот метод не будет работать. Реквизиты в компоненте, инкапсулированном withRouter, включают историю, а переход на страницу управляется объектом истории. У объекта истории есть такие методы, как push, replace и go, и эти методы вызываются для перехода на другую страницу.

class Comoponent extends React.Component {
  handleClick () {
    this.props.history.push('/article')
  }
}
export default withRouter(Component)
  1. Получить параметры в маршруте

В приведенном выше компоненте ArticleDetail нам нужно знать идентификатор текущего маршрута. Объект match реквизитов компонента содержит параметры маршрута.

class ArticleDetail extends React.Component {
  state = {
    id: null
  }
  componentDidMount () {
    const { id } = this.props.match
    this.setState({ id })
  }
}

5. Разделение кода

используй сейчасreact-loadableДля достижения асинхронной загрузки компонентов все становится намного проще. В предыдущей документации React Router асинхронная загрузка компонентов реализована следующим образом.

// 一种比较繁琐的方式
import Component from 'bundle-loader!./Component'
// 为此还要编写一个组件
class Bundle extends React.Component {
  state = {
    // short for "module" but that's a keyword in js, so "mod"
    mod: null
  }

  componentWillMount () {
    this.load(this.props)
  }

  componentWillReceiveProps (nextProps) {
    if (nextProps.load !== this.props.load) {
      this.load(nextProps)
    }
  }

  load (props) {
    this.setState({
      mod: null
    })
    props.load((mod) => {
      this.setState({
        // handle both es imports and cjs
        mod: mod.default ? mod.default : mod
      })
    })
  }

  render () {
    return this.state.mod ? this.props.children(this.state.mod) : null
  }
}
// 加载异步组件
<Bundle load={Component}>
    {(Container) => <Container {...props}/>}
</Bundle>

С помощью react-loadable это делается всего несколькими строками кода.

import Loadable from 'react-loadable'

const Loading = () => <Spin />

const LogIn = Loadable({
  loader: () => import('../components/Login'),
  loading: Loading
})

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

const LogIn = Loadable({
  loader: () => import(/* webpackChunkName: "Login" */'../components/Login'),
  loading: Loading
})

6. Резюме

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

использованная литература