Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[译]React Router:从V2/V3迁移到V4 #4

Open
YutHelloWorld opened this issue Aug 1, 2017 · 13 comments
Open

[译]React Router:从V2/V3迁移到V4 #4

YutHelloWorld opened this issue Aug 1, 2017 · 13 comments

Comments

@YutHelloWorld
Copy link
Owner

YutHelloWorld commented Aug 1, 2017

官方文档:Migrating from v2/v3 to v4

React Router V4相对V2/V3几乎完全重写了,遵循 Just Component 的 API 设计理念。

react-router V4 分成了三个包:react-router-dom(for web)、react-router-native(for #native)、react-router(core)。在浏览器中,你只需使用react-router-dom。react-router-dom和react-router详见: remix-run/react-router#4648

The Router

在 React Router v3中,有 一个单独的 <Router> 组件. 它会提供一个 history 对象作为prop属性。

同时,可以使用routes prop或者children<Router>配置route

// v3
import routes from './routes'
<Router history={browserHistory} routes={routes} />
// or
<Router history={browserHistory}>
  <Route path='/' component={App}>
    // ...
  </Route>
</Router>

而在V4中,一个重大变化就是提供了数种不同的router组件。每种都会创建一个history对象。 <BrowserRouter> 创建 browser history, <HashRouter> 创建 hash history, and<MemoryRouter>创建memory history

V4取消了集中的路由配置。

//v4
<BrowserRouter>
  <div>
    <Route path='/about' component={About} />
    <Route path='/contact' component={Contact} />
  </div>
</BrowserRouter>

这里要注意的是,router component 只能包裹一个元素。

// yes
<BrowserRouter>
  <div>
    <Route path='/about' component={About} />
    <Route path='/contact' component={Contact} />
  </div>
</BrowserRouter>

// no
<BrowserRouter>
  <Route path='/about' component={About} />
  <Route path='/contact' component={Contact} />
</BrowserRouter>

Routes

在V3中,<Route>不是一个真正的组件,而是作为一个标签用来创建route配置对象。

/// in v3 the element
<Route path='contact' component={Contact} />
// 等同于
{
  path: 'contact',
  component: Contact
}

使用 v4,您可以像常规的 React 程序一样布局您应用中的组件,您要根据位置(特别是其 pathname )呈现内容的任何位置,您将呈现 <Route>

在 v4,<Route> 其实是一个组件,所以无论你在哪里渲染 <Route>,它里面的内容都会被渲染。当 <Route> 的 path 与当前的路径匹配时,它将会渲染 component, render, or children 属性中的内容,当 <Route>path 与当前的路径不匹配时,将会渲染 null

路由嵌套

在 v3 中,<Route> 组件是作为其父 <Route> 组件的 children 嵌套其中的。

<Route path='parent' component={Parent}>
  <Route path='child' component={Child} />
  <Route path='other' component={Other} />
</Route>

当嵌套的 <Route> 匹配时,react 元素将会使用子 <Route> 和 父 <Route>component 属性去构建,子元素将作为父元素的 children 属性。

<Parent {...routeProps}>
  <Child {...routeProps} />
</Parent>

使用 v4,子 <Route> 会由父 <Route> 呈现。

<Route path='parent' component={Parent} />

const Parent = () => (
  <div>
    <Route path='child' component={Child} />
    <Route path='other' component={Other} />
  </div>
)

on* properties

react-router v3 提供 onEnter, onUpdate, and onLeave 方法。这些方法本质上是重写(覆盖)了 react 生命周期方法。

使用 v4,你将会使用生命周期方法 通过 <Route> 渲染的组件,你可以使用 componentDidMountcomponentWillMount 代替 onEnter,你可以使用 componentDidUpdate 或者 componentWillUpdate (更或者 componentWillReceiveProps) 代替 onUpdate,你可以使用 componentWillUnmount 代替 onLeave

<Switch>

在v3中,您可以指定一些子路由,并且只会渲染匹配到的第一个。

// v3
<Route path='/' component={App}>
  <IndexRoute component={Home} />
  <Route path='about' component={About} />
  <Route path='contact' component={Contact} />
</Route>

v4 通过 <Switch> 组件提供了相似的功能,当 <Switch> 被渲染时,它仅会渲染与当前路径匹配的第一个子 <Route>

// v4
const App = () => (
  <Switch>
    <Route exact path='/' component={Home} />
    <Route path='/about' component={About} />
    <Route path='/contact' component={Contact} />
  </Switch>
)

<Redirect>

在V3, 如果你想从一个路径重定向到另一个, 例如从 //welcome, 你可以使用 <IndexRedirect >.

// v3
<Route path="/" component={App}>
  <IndexRedirect to="/welcome" />
</Route>

在V4,你可以使用<Redirect>达到相同的效果。

// v4
<Route exact path="/" render={() => <Redirect to="/welcome" component={App} />} />

<Switch  >
  <Route exact path="/" component={App} />
  <Route path="/login" component={Login} />
  <Redirect path="*" to="/" />
</Switch>

PatternUtils

matchPattern(pattern, pathname)

在v3中,您可以使用与内部相同的匹配代码来检查路径是否匹配模式。在v4中,已被由path-to-regexp 库驱动的matchPath替代。

formatPattern(pattern, params)

在v3中,您可以使用Pattern Utils.format Pattern从路径模式(可能在常量或中央路由配置中)生成有效的路径以及包含名称参数的对象:

// v3
const THING_PATH = '/thing/:id';

<Link to={PatternUtils.formatPattern(THING_PATH, {id: 1})}>A thing</Link>

Link

在V3中,您可以省略to属性或将其设置为null以创建没有href属性的锚标签。

// v3
<Link to={disabled ? null : `/item/${id}`} className="item">
  // item content
</Link>

在v4中,您应该总是提供to.如果你要设置to,你可以做对其做一层封装。

// v4
import { Link } from 'react-router-dom'

const LinkWrapper = (props) => {
  const Component = props.to ? Link : 'a'
  return (
    <Component {...props}>
      { props.children }
    </Component>
  )
)

<LinkWrapper to={disabled ? null : `/item/${id}`} className="item">
  // item content
</LinkWrapper>
@WisestCoder
Copy link

很棒

@zdJOJO
Copy link

zdJOJO commented Aug 24, 2017

写的不错!
我可以问一个问题吗?
V4 取消了 路由集中配置的概念,写法上,Route是写在了对应的组件中。
但是,一旦需要组件按需加载的话,这样就又回到了V3时代的集中配置路由了。我的理解是,如果按需加载的话,只能最外层的父组件可以实现按需加载, 嵌套组件中子组件要如何实现按需加载呢?

@YutHelloWorld
Copy link
Owner Author

v4 按需加载不需要集中配置路由。具体实现代码可以看 #5 代码分割。每个路由组件只有当path匹配时,通过webpack2的impost()异步加载。 @zdJOJO

@zdJOJO
Copy link

zdJOJO commented Aug 24, 2017

我看了下, 还是有些问题。
第一, 博主使用的是 webpack2的impost()异步加载 和 V4 官方提供的 webpack的 bundle-loader 有何却别?
第二, 我发现 博主项目的路由不存在 嵌套路由,都是一级父路由组件,不存在二级或者三级子路由组件,所以这边的按需加载都是父组件的按需加载,假如Zen组件中有嵌套了子路由, 那这个自路由需不需要单独打包进行按需加载?还是说和父组件Zen 打包在一起,随Zen的加载一起被加载?

@YutHelloWorld
Copy link
Owner Author

YutHelloWorld commented Aug 24, 2017

这个问题很好 @zdJOJO

  1. 看了下官方说明:https://reacttraining.com/react-router/web/guides/code-splitting

为什么使用bundle, 而不是imort()?
我们已经使用它多年并且仍然使用,同时TC39提出官方的动态进口。最新的提案是import(),我们可以将Bundle组件调整为使用import():
捆绑装载程序的另一个巨大优点是它第二次同步回调,这防止每次访问代码分割屏幕时闪烁加载屏幕。不管您导入的方式如何,这个想法是一样的:一个在渲染时处理代码加载的组件。现在你所做的就是渲染一个,无论你想要动态地加载代码。

具体差别,还有待研究。
2. 子路由是否需要按需加载是根据你的业务需求来的。如果不做异步加载,你可以参照Home组件的方式同步加载。如果仍然异步,还是相同的方式使用异步高阶组件AsyncComponent。

@zdJOJO
Copy link

zdJOJO commented Aug 24, 2017

thx 我再研究研究

@YutHelloWorld
Copy link
Owner Author

看了下bundler-loader

var load = require("bundle-loader?lazy!./file.js");

// The chunk is not requested until you call the load function
load(function(file) {

});

效果和import()类似

@Chickenbrother
Copy link

Chickenbrother commented Mar 13, 2018

  import React from 'react';
import { BrowserRouter, Route, Switch } from 'dva/router';
import dynamic from 'dva/dynamic';
import MiddleSelectPage from "./routes/MiddleSelectPage";

function RouterConfig({ history, app }) {
  const IndexPage = dynamic({
    app,
    component: () => import('./routes/IndexPage'),
  });
  const HomePage = dynamic({
    app,
    component: () => import('./routes/Home/HomePage'),
  }) ;
  return (
    <BrowserRouter>
      <Switch>
        <Route path="/" exact component={MiddleSelectPage} />
        <Route path="/Home" exact component={HomePage} />
        <Route path="/Login" exact component={IndexPage} />
     </Switch>
    </BrowserRouter>
  );
}

为什么我用了BrowserRouter 浏览器路径中带有#符号,如何设置可以去掉

@YutHelloWorld
Copy link
Owner Author

#是hashHistory 或者HashRouter 引起的, 从你的代码看不出问题~ @Chickenbrother

@ganyanchuan1989
Copy link

按需加载不需要集中路由的说话不太正确。按需加载是解决文件大的问题,集中路由是解决架构层级问题。特别是大项目协同开发,没有集中路由还是不太好管理,代码的后期维护也会比较麻烦。

@hkjpotato
Copy link

@ganxunzou gan那么对于react router v4为了维护架构是否还是要写成集中路由?我觉得如果我们一直写出集中路由的话是不是还是没有真正理由v4里route也是component的概念。

@fi3ework
Copy link

fi3ework commented Jun 1, 2018

@hkjpotato 理论上,v4也是可以写成集中配置的路由的,像以前都写在一起就好了。应该说,更灵活了。

@ganyanchuan1989
Copy link

ganyanchuan1989 commented Jun 4, 2018

@fi3ework @hkjpotato 是可以写集中路由。现在也有 react-router-config 。在没有这个的时候,我借鉴ant-design-pro的方式,自定义router配置,导出所有的Component,然后在使用的地方用Switch控制只能显示同时显示一个。

另外项目如果路由都写到页面里面,后续维护是很麻烦的,结构也不清晰。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants