Примечания по обновлению React-маршрутизатора

внешний интерфейс JavaScript React.js Redux

предисловие

Upgrade зависимости

react-router v4 реагирует с таким же разделением на две части: ядро ​​react-router и в зависимости от операционной среды, а также react-router-dom или react-router-native (например, react-dom и react-native). В этой статье будет сказано, что среда браузера, состоящая из react-router + react-router-dom

yarn add react-router react-router-dom history

react-router-reduxУ тебя естьмного причинИспользуйте его, но единственная причина или использование для нас - это навигация за пределами компонентов страницы, react-router-redux позволяет вам обрабатывать переходы по страницам через диспетчеризацию в любом месте, например: store.dispatch(push(' /')). Должны ли мы использовать react-router-redux из-за этого? Конечно нет, есть более простые способы выполнить это требование. Поэтому я удалил react-router-redux в этом обновлении.На момент написания этой статьи react-router-redux, поддерживающий react-router v4, все еще находился в версии v5.0.0-alpha.7, что также является одной из причин.

Запомнить предыдущую установкуhistory? history — это один из двух зависимых react-router, причина для явной установки, потому что мы хотим использовать его для реализации компонентов навигации по страницам снаружи. Следующий пример истории браузера (история хеширования и история памяти совпадают):

// history.js
import createHistory from 'history/createBrowserHistory';

const history = createHistory();

export default history;
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Router } from 'react-router';
import history from './history';
import App from './app';

ReactDOM.render(
  <Router history={history}>
    <App />
  </Router>,
  document.getElementById('app')
);

Получать!就这么简单,这样在任何地方只要引用 history 就可以使用它进行导航操作,如 history.push('/'),更多使用方式请参考 historyДокументация.其实 react-router-dom 的 BrowserRouter 跟我们做了同样的事,区别在于我们这么做能把 history 暴露出来。这个 history 就是页面组件 props 里面的 history 自然也就能做同样的事情。

react-router v3 ориентирован на конфигурацию, а написание компонентов — это просто синтаксический сахар. И react-router v4 полностью ориентирован на компоненты, а предоставленный Route Switch и т. д. — все это настоящие компоненты. Это также приводит к тому, что маршрутизация может быть записана только в виде компонентов, а конфигурация не может быть записана. Однако конфигурация, подобная v3, имеет некоторые удобства, такие как унифицированное управление и простота использования.

Благодаря гибкому синтаксису JSX у нас все еще есть возможность писать маршруты react-router v4 настраиваемым образом.

// routes.js
import Home from './home';
import About from './about';
import Help from './help';

export default [{
  path: '/',
  exact: true,
  component: Home
}, {
  path: '/about',
  component: About
}, {
  path: '/help',
  component: Help
}];
// app.js
import React from 'react';
import { Switch, Route } from 'react-router';
import routes from './routes';
import NotFound from './not-found';

class App extends React.Component {
  render() {
    return (
      <Switch>
        {routes.map((route, i) => <Route key={i} exact={!!route.exact} path={route.path} component={route.component} />)}
        <Route component={NotFound} />
      </Switch>
    );
  }
}

export default App;

Таким образом, мы пишем компонентно-ориентированную маршрутизацию способом конфигурации, учитывая преимущества обоих. Если у вас есть требования к вложенной маршрутизации, вы можете обратиться кОфициальный пример. Чиновник также предоставляетreact-router-config

Асинхронные компоненты и разделение кода

Одно из самых больших преимуществ веб-приложений заключается в том, что вам не нужно загружать все приложение целиком, а только те его части, которые вам нужны. Для достижения этой цели вам необходимо разбивать код и загружать компоненты асинхронно. К сожалению, react-router v4 не предоставляет интерфейс для загрузки асинхронных компонентов, таких как v3. Эту часть работы нужно взять на себя.

Мы можем создать Bundle компонентов более высокого порядка специально для загрузки асинхронных компонентов.

// bundle.js
import React from 'react';

class Bundle extends React.Component {
  constructor(props) {
    super(props);
    this.state = { Component: null };
    props.load().then(Component => this.setState({ Component: Component.default }));
  }
  render() {
    const { load, ...props } = this.props;
    const Component = this.state.Component;
    return Component ? <Component {...props} /> : null;
  }
}

export default Bundle;

Затем измените route.js

// routes.js
import React from 'react';
import Bundle from './bundle';

export default [{
  path: '/',
  exact: true,
  component(props) {
    // 这里的 component 函数也是一个高阶组件
    return <Bundle {...props} load={() => import('./home')} />;
  }
}, {
  path: '/about',
  component(props) {
    return <Bundle {...props} load={() => import('./about')} />;
  }
}, {
  path: '/help',
  component(props) {
    return <Bundle {...props} load={() => import('./help')} />;
  }
}];

Таким образом, каждая страница будет упакована в отдельный JS, и соответствующий компонент будет загружаться асинхронно при доступе к соответствующей странице. Это также позволяет осуществлять детальное управление кешем. Следует отметить, что на момент написания этой статьи синтаксис import() все еще находился на стадии 2 и должен быть добавлен в Babel.syntax-dynamic-importПлагин может работать правильно, и он должен поддерживаться webpack 2 и выше.

параметры запроса

потому чторазличные причиныreact-router v4 больше не анализирует параметры запроса URL-адресов, такие как ?key=value, а компонент страницы props.location содержит только строку поиска. Это несовместимо с v3 и очень неудобно. Есть ли способ сделать его совместимым? Конечно, в настоящее время написанный ранее histroy.js имеет новое применение.

// history.js
import qs from 'qs';
import createHistory from 'history/createBrowserHistory';

function addQuery(history) {
  const location = history.location;
  history.location = { ...location, query: qs.parse(location.search, { ignoreQueryPrefix: true }) };
}

const history = createHistory();

addQuery(history);

export const unlisten = history.listen(() => {
  // 每次页面跳转都会执行
  addQuery(history);
});

export default history;

Таким образом, мы можем получить проанализированные параметры URL-запроса в компоненте страницы props.location.query, который полностью совместим с v3. Дополнительным бонусом является то, что вы можете получить проанализированные параметры запроса URL, ссылаясь на историю в любом месте. Следует отметить, что вhistoryВ дизайне объект истории является изменяемым, поэтому мы можем напрямую изменять историю. Но history.location неизменяем, поэтому мы хотим убедиться, что каждый объект местоположения является совершенно новым.

с редуксом

React-Router V4 имеет большую яму с Redux (Mobx должен иметь такую ​​же проблему), см. Подробнееэта статья, Не повторяйте их здесь. Говоря простым языком, если компонент пакета через подключение использует редукс, а не подсборку рута, то история изменений не вызовет обновления компонентов, подкомпоненты естественно обновляться не будут. Приложения, такие как корневой компонент (выше App).

// app.js
import React from 'react';
import { connect } from 'react-redux';
import { Switch, Route } from 'react-router';
import routes from './routes';
import NotFound from './not-found';

class App extends React.Component {
  render() {
    return (
      <Switch>
        {routes.map((route, i) => <Route key={i} exact={!!route.exact} path={route.path} component={route.component} />)}
        <Route component={NotFound} />
      </Switch>
    );
  }
}

function mapStateToProps(state) {
  return {
    someState: state.someState
  };
}

export default connect(mapStateToProps)(App);
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { Router, Route } from 'react-router';
import store from './store';
import history from './history';
import App from './app';

ReactDOM.render(
  <Provider store={store}>
    <Router history={history}>
      <Route component={App} /> {/* 没有 path 就会匹配所有路由 */}
    </Router>
  </Provider>,
  document.getElementById('app')
);

наконец

不得不说升级 react-router 很困难,坑也很多。但是把坑一个个填完,最终完美升级也是一件很有意思,很有成就感的事。 Надеюсь, эта статья поможет вам.

GitHub