這篇文章是升級到Webpack2坑——模塊熱替換失效頁面不自動刷新?的後續。那篇文章主要說明了,升級到 Webpack 2 以後,經過升級
webpack-dev-server
和react-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
版本變化:web
react
和react-dom
是順便升級的,並不強制。主要的升級是 react-router
v2到v4。在v4版本中, Web開發只須要安裝react-router-dom
,參考what's the diff between 'react-router-dom' & 'react-router'?中的回答。 React Router v4變化挺大的。所以看官方文檔(點這裏)是頗有必要的。bash
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 時,我才弄懂這個「動態路由」的意思。就跟咱們平時使用自定義的組件是同樣的,想在哪裏渲染,就加到哪裏。參照示例Sidebar。app
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
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 部分,實現動態加載須要 webpack
,babel-plugin-systax-dynamic-import
和react-loadable
。 修改後:
<Route exact path="/settings"
component={Loadable({
loader: () => import(/* webpackChunkName: "Settings" */ './Settings.js'),
loading:Loading
})}
/>
複製代碼
綜上就是升級過程的重點了。至此,動態加載和HMR都沒問題了。