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 в реквизиты компонента. Это очень полезная функция, и ее использование проиллюстрировано ниже четырьмя небольшими примерами.
- Работает с подключением Redux
Ранее мы говорили, что Route — это компонент, а таблица маршрутизации постоянно меняется. Redux используется в проекте для управления данными, когда данные не изменяются, компонент не будет перерисовываться. Предполагая, что компонент маршрута в компоненте не был отрендерен и данные не изменились, даже если ссылка текущей страницы изменилась, ни один маршрут не будет соответствовать ссылке. Потому что компонент Route все еще не отображается в это время! Как узнать, изменилась ли ссылка? Вот где нужен withRouter.
import { withRouter } from 'react-router-dom'
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Component))
- получить текущий маршрут
Как показано на рисунке ниже, боковая панель слева должна решить, какую из них развернуть, а какую выделить в соответствии с изменением ссылки. Инкапсулируя левый компонент с помощью 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 только сравнивает, совпадают ли совпадение и местоположение дважды, и не сравнивает объект истории. Объект истории каждый разизменение, поэтому здесь он не используется для сравнения.
Таким же образом можно реализовать и хлебные крошки.
- прыжок страницы
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)
- Получить параметры в маршруте
В приведенном выше компоненте 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, исходя из моего собственного опыта разработки. Поскольку большая часть кода в этой статье представляет собой просто фрагменты, предназначенные только для иллюстрации концепции, подробности можно найти в документации этого проекта.исходный код.