升級 React Router 與 Code Splitting 實踐

背景

公司某 SPA 項目的代碼越來越重,功能模塊也越來多;而且裡面的一個關鍵依賴react router的版本也有年頭沒有更新了。處於對欠技術債的恐懼以及小目標的召喚,就領了這個活:也就是標題說的兩件事,升級 react router 到最新版(v4)和使用 code splitting 拆分模塊。

Breaking Changes

凡是這種大的重構工程首先要保證的是不影響重構前的所有功能,為了避免產生副作用,好好過一遍官方文檔尤其是Change Log是非常必要的。具體來說就是 react-router-v2 到目標版本 v4 的所有大改動, 點這裡查看詳情,為了節省時間,我把主要的更新日誌列在這了:

[v3.0.0-alpha.2]

Jul 19, 2016

  • Breaking: Remove all deprecated functionality as of v2.6.0 ([#3603], [#3646])
  • Breaking: Support history v3 instead of history v2 ([#3647])
  • Feature: Add router to props for route components ([#3486])

--

[v3.0.0-alpha.1]

May 19, 2016

  • Breaking: Remove all deprecated functionality as of v2.3.0 ([#3340], [#3435])
  • Breaking/Feature: Make <Link> and withRouter update inside static containers ([#3430], [#3443])
  • Feature: Add params, location, and routes to props injected by withRouter and to properties on context.router ([#3444], [#3446])

官網非常貼心,給出了詳細的遷移指導,為了節省時間,這裡總結一下:

  • 依賴變化, v2.8.1 => v4.1.2(這不是廢話嗎)
  • 不再支持中心化的全局路由配置
  • 抽象出 react router core 包,單獨的瀏覽器應用可以安裝 react router dom 這個依賴
  • 移除了和 history 有關的介面,可以使用新的組件實現舊有功能

如果要集成 redux 使用,有幾點要注意,

安裝對應版本的 react-router-redux

npm install --save react-router-redux@nextn

使用提供的路由 hoc 關聯路由信息到 react component - withRouter

// beforenexport default connect(mapStateToProps)(Something)nn// afternimport { withRouter } from react-router-domnexport default withRouter(connect(mapStateToProps)(Something)n

安裝獨立的history依賴, 並且配置 redux store

npm install --save historynnimport React from react;nimport ReactDOM from react-dom;nnimport { createStore, combineReducers, applyMiddleware } from redux;nimport { Provider } from react-redux;nnimport createHistory from history/createBrowserHistory;nimport { Route } from react-router;nnimport { ConnectedRouter, routerReducer, routerMiddleware, push } from react-router-redux;nnimport reducers from ./reducers;nnconst middleware = routerMiddleware(history);nnconst store = createStore(n combineReducers({n ...reducers,n router: routerReducer,n }),n applyMiddleware(middleware)n);nnReactDOM.render(n <Provider store={store}>n <ConnectedRouter history={history}>n <div>n <Route exact path="/" component={Home} />n <Route path="/about" component={About} />n <Route path="/topics" component={Topics} />n </div>n </ConnectedRouter>n </Provider>,n document.getElementById(root)n);n

要更深度整合的話,可以參考這個Deep Intergration?(嘴上說不推薦要深度整合,anyway 還是支持了)

代碼分割(code splitting)

分割這事主要分兩個方面,一個是怎麼才能提取公共的依賴,另一個是使用啥方法可以申明某個組件是要被拆出來做獨立 chunk 的;實踐下來,發現這兩方面的事情都和 webpack 的配置密不可分。使用 webpack2+的版本就可以同時解決這兩個問題了。

主流的提取公共依賴的途徑有 2 種,Dll 和 CommonsChunk 插件。 Dll+Dll Reference 插件需要配合使用,優點是獨立於 webpack 運行時打包,可以減少整體打包所花費的時間。CommonsChunk 配置優化的方法也有許多,在實踐過程中,可以靈活配置 minChunks 屬性按引用次數和引用位置打包可以得到比較好的效果。

minChunks: number|Infinity|function(module, count) -> boolean,n // The minimum number of chunks which need to contain a module before its moved into the commons chunk.n // The number must be greater than or equal 2 and lower than or equal to the number of chunks.n // Passing `Infinity` just creates the commons chunk, but moves no modules into it.n // By providing a `function` you can add custom logic. (Defaults to the number of chunks)n

還有一種方法就是手動配置 verdor 的 entry,適用於項目不大,開發者對項目依賴關係又非常清楚的場景。總的來說,具體使用哪種方法還是要按照實際項目的情況作選擇。

談到代碼分割的另一個方面,如何拆分獨立 chunk 也有下面幾個可選項。

  • dynamic import,使用直觀,可以一定程度的做封裝
  • bundle loader,這個是 react router 官方例子使用的方法,可配置能力強
  • proise loader, 和 bundle loader 類似但是是用 Promise 的方式調用

可能遇到的問題

  • 很多項目是使用 html plugin 使用模版 html 插入生成的 bundle 文件,使用 dll 插件的時候,html plugin 沒法取得對應的 chunk name, 這種情況可以使用add-asset-html-webpack-plugin,估計以後會有新的插件或者介面支持吧
  • 查看打包和拆分情況, 推薦使用 webpack bundle analyzer, 用 treemap 的形式直觀展示 chunk 的分布和佔比情況,還可以設置 gzip 或者原始輸入大小 (parse gzip stat)

參考文章

  • 非常推薦 reacttraining.com
  • 使用 DDL 優化編譯,附其他 code splitting 方法比較
  • react router with webpack
  • webpack and code splitting

原文鏈接 @建躍

本文版權屬於再惠研發團隊,歡迎轉載,轉載請保留出處。

推薦閱讀:

TAG:前端开发 | react-router | webpack |