升級到 React Router 4 並實現動態加載和模塊熱替換

這篇文章是升級到Webpack2坑——模塊熱替換失效頁面不自動刷新?的後續。那篇文章主要說明了,升級到 Webpack 2 以後,經過升級webpack-dev-serverreact-hot-loader插件,使模塊熱替換(HMR)再次生效。但後來發現,其實並無徹底生效。react

問題

項目中的 Route 使用了System.import(/* webpackChunkName: "example" */ './example.js)來實現模塊的動態加載,也就是根據組件打包成多個 bundle,只有在點擊到對應的 Route 時,這個 bundle 纔會被加載,而不是在打開首頁時,加載全部的 bundle。webpack

問題是,當咱們修改某個標籤頁對應的組件時,會發現 HMR 並非每次都生效。熱替換過程會報錯(相似[HMR] unexpected require(116) from disposed module 26),而且沒法切換標籤頁了。git

根據這篇討論webpack2 + hot-reload: Not works with System.import,我決定升級 React Router 到v4而且將 System.import() 替換成 import()github

升級到 React Router v4

版本變化:web

  • webpack:2.7.0
  • react-hot-loader: 4.1.2
  • react:0.14.7 >>> 15.6.2
  • react-dom:0.14.7 >>> 15.6.2
  • react-router: 2.5.2 卸載,安裝 react-router-dom 4.3.0-rc.3

reactreact-dom是順便升級的,並不強制。主要的升級是 react-router v2到v4。在v4版本中, Web開發只須要安裝react-router-dom,參考what's the diff between 'react-router-dom' & 'react-router'?中的回答。 React Router v4變化挺大的。所以看官方文檔(點這裏)是頗有必要的。bash

動態路由(Dynamic Routing)

React Router從v4開始,再也不是靜態路由(Static Routing),而變成了動態路由(Dynamic Routing)。前者是咱們習慣的用法,通常須要在應用初始化過程當中聲明路由,這時候頁面尚未任何渲染;後者則意味着,路由會隨着應用的渲染而發生,而不是一個在運行的應用以外的配置。React Router v4中的任何東西都是一個組件。babel

When we say dynamic routing, we mean routing that takes place as your app is rendering, not in a configuration or convention outside of a running app. That means almost everything is a component in React Router.react-router

一開始看到這部分我還沒太明白,直到我意識到,我再也不須要在一個Root組件中定義全部的 Route,而是能夠在任何組件內部添加一個 Route 時,我才弄懂這個「動態路由」的意思。就跟咱們平時使用自定義的組件是同樣的,想在哪裏渲染,就加到哪裏。參照示例Sidebarapp

You can render a in as many places as you want in your app. It will render along with any other s that also match the URL. So, a sidebar or breadcrumbs or anything else that requires you to render multiple things in multiple places at the same URL is nothing more than multiple s.dom

嵌套路由(Nested Routing)

v4版本之前,咱們可能會用到嵌套路由,尤爲是當頁面中有導航欄時。好比:

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

當嵌套的內存路由匹配時,外層路由和內層路由對應的組件都會被渲染出來,並且子組件會經過props.children傳遞給父組件,也就是子組件會渲染到父組件 render 方法中{this.props.children}所在的位置。

v4版本時,就不須要在 Route 配置的地方嵌套好幾個 Route,也不須要使用 this.props.children 指明子組件渲染的位置。只須要在父組件相應的位置添加 Route 便可。就跟嵌套一個 div 同樣。

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

const Parent = () => (
  <div>
    //......
    <Route path='child' component={Child} />
    <Route path='other' component={Other} />
  </div>
)
複製代碼

重定向路由

v4去掉 <IndexRedirect>了。要實現重定向可使用<Redirect>組件。

// v3
<Route path="/" component={App}>
  <IndexRedirect to="/welcome" />
</Route>
複製代碼
// v4
<Route exact path="/" render={() => <Redirect to="/welcome" component={App} />} />
//Switch限定只渲染第一個匹配的 Route
<Switch>
  <Route exact path="/" component={App} />
  <Route path="/login" component={Login} />
  <Redirect path="*" to="/" />  //當以上的path均不匹配時,重定向到'/'
</Switch>
複製代碼

局部刷新/總體刷新

局部刷新

<Link to='/home' />
複製代碼

整個頁面刷新:

window.location.href = '/home'
//or
<a href='/home' ></a>
複製代碼

動態加載

修改前:

<Route path="/settings"
  getComponent={(location, callback) => {
    import(/* webpackChunkName: "Settings" */ './Settings.js').then(component => {
      callback(null, component.default || component)
    })
  }}
  />
複製代碼

根據官方文檔中的 Code Splitting 部分,實現動態加載須要 webpackbabel-plugin-systax-dynamic-importreact-loadable。 修改後:

<Route exact path="/settings"
  component={Loadable({
    loader: () => import(/* webpackChunkName: "Settings" */ './Settings.js'),
    loading:Loading                            
  })}
/>
複製代碼

綜上就是升級過程的重點了。至此,動態加載和HMR都沒問題了。

相關文章
相關標籤/搜索