摘要:前言最近將公司項目的從版本升到了版本,跟完全不兼容,是一次徹底的重寫。升級過程中踩了不少的坑,也有一些值得分享的點。沒有就會匹配所有路由最后不得不說升級很困難,坑也很多。
前言
最近將公司項目的 react-router 從 v3 版本升到了 v4 版本,react-router v4 跟 v3 完全不兼容,是一次徹底的重寫。這也給升級造成了極大的困難,與其說升級不如說是對 router 層重寫。之前我也將項目的 react 從 v15 版本升級到了 v16 版本,相較而言升級 react-router 比升級 react 困難多了。升級過程中踩了不少的坑,也有一些值得分享的點。寫成一篇小文,供大家參考。
依賴升級react-router v4 跟 react 一樣拆成了兩部分,核心的 react-router 和依運行環境而定的 react-router-dom 或 react-router-native(跟 react-dom 和 react-native 一樣)。本文要說的是瀏覽器環境,也就是 react-router + react-router-dom
先安裝依賴(推薦使用 yarn)
yarn add react-router react-router-dom history
為什么要安裝 history 后面會解釋。
組件外導航與 react-router-redux之前我們項目中使用了 react-router-redux 你有很多理由使用它,但對于我們來說唯一的理由或者用處就是用于在頁面組件之外導航,react-router-redux 讓你可以在任何地方通過 dispatch 處理頁面跳轉,如:store.dispatch(push("/"))。因為這個我們就必須使用 react-router-redux 嗎?當然不需要,有更簡單的辦法實現這個需求。所以這次升級我移除了react-router-redux, 寫作此文時支持 react-router v4 的 react-router-redux 還處于 v5.0.0-alpha.7 也是原因之一。
還記得之前安裝的 history 嗎?history 是 react-router 唯二的主要依賴之一,之所以要顯式安裝,是因為我們要使用它來實現頁面組件外導航。以下以 browser history 為例(hash history 和 memory history 都是一樣的):
我們不使用 react-router-dom 提供的 BrowserRouter 而是自己實現一個
// 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(, 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 ({routes.map((route, i) => ); } } export default App;)}
這樣我們就用配置的方式寫出了面向組件的路由,兼顧兩者的優點。如果有嵌套路由需求,可以參考官方示例。官方也提供了一個 react-router-config, 不過我沒有使用,一來覺得沒必要,二來寫作此文時它還處于 v1.0.0-beta.4 版本。
異步組件與 Code SplittingWeb 應用最大的一個優勢就是不必下載整個應用,只用下載需要的部分就可以使用。要達到這樣的目標,就需要對代碼進行分片,異步加載組件。可惜 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 ?: null; } } export default Bundle;
然后修改一下 routes.js
// routes.js import React from "react"; import Bundle from "./bundle"; export default [{ path: "/", exact: true, component(props) { // 這里的 component 函數也是一個高階組件 returnimport("./home")} />; } }, { path: "/about", component(props) { return import("./about")} />; } }, { path: "/help", component(props) { return import("./help")} />; } }];
這樣每個頁面都會打包成多帶帶的 JS,訪問相應頁面才會去異步加載對應的組件。這樣也可以做精細化緩存控制。
需要注意的是 import() 語法在寫作本文時還處于 Stage 2 的狀態,需給 Babel 添加 syntax-dynamic-import 插件才能正常工作,另外需 webpack 2 及以上才支持。
因為各種原因 react-router v4 不再解析 ?key=value 這樣的 URL 的查詢參數,頁面組件 props.location 中只有 search 字符串。這跟 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;
這樣我們就能在頁面組件 props.location.query 拿到解析好的 URL 查詢參數了,跟 v3 完美兼容。還有個額外的好處是在任何地方引用 history 都可以拿到解析好的 URL 查詢參數。需要注意的是,在 history 的設計中,history 對象是 Mutable 的,所以我們可以直接修改 history。但是 history.location 是 Immutable 的,所以我們要確保每一個 location 對象都是全新的。
搭配 Reduxreact-router v4 跟 redux 搭配有一個大坑(mobx 應該也有同樣的問題),詳情請看這篇文章,這里就不再贅述。簡單來說,如果一個組件用 redux 的 connect 包裝過,又?不是 Route 的子組件,那么 history 的變更就不會觸發這個組件的更新,它的子組件自然也不會更新。比如應用的根組件(上文的 App)。
解決方案也很簡單,可以用 react-router v4 提供的 withRouter 再包裝一遍:withRouter(connect(...)(App)),或者讓 App 做為 Router 的子組件,原理都一樣。我采用的后者。
// 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 ({routes.map((route, i) => ); } } 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(最后, document.getElementById("app") ); {/* 沒有 path 就會匹配所有路由 */}
不得不說升級 react-router 很困難,坑也很多。但是把坑一個個填完,最終完美升級也是一件很有意思,很有成就感的事。希望這篇文章能對你有所幫助。
另外完整的 Demo 請戳我的 GitHub,喜歡的話點個 Star 吧 :P
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/90533.html
摘要:升級入坑小記場景描述引入的版本為,開啟調試工具默認升級后可以調試。遂升級,發現大量使用失效,報,的中文文檔,沒有及時更新。機票訂單和用戶信息。 Vuex 升級入坑小記 場景描述 引入Vuex的版本為0.3,開啟調試工具默認升級后可以調試Vuex。給作者一個大大的贊。為提高開發體驗也是操碎了心 (??????)?? (8。安利下(Vue Devtools)。 Vue Devtools ...
摘要:前言周日在公司的新電腦在以前配置的目錄按下時發現報了錯,百度了一下得知原來已經到了版本,就花了一點時間去升了個級,順便記下我個人使用到的配置文件新版本的不同點,文筆和水平有限,多多見諒新引入新引入的可替換老版的和,代碼更簡潔是任務監聽是任務 前言 周日在公司的新電腦在以前gulp3.9配置的目錄按下npm install時發現報了錯,百度了一下得知原來gulp已經到了4.0版本,就花了...
摘要:概述相對于幾乎是重寫了新版的更偏向于組件化。汲取了很多思想,路由即是組件,使路由更具聲明式,且方便組合。如果你習慣使用,那么一定會很快上手新版的。被一分為三。不止是否有意義參考資料遷移到關注點官方文檔 概述 react-router V4 相對于react-router V2 or V3 幾乎是重寫了, 新版的react-router更偏向于組件化(everything is comp...
摘要:普通的回調函數調用執行后續邏輯使用了以后的復雜邏輯獲取到正確的結果輸出兩個文件拼接后的內容雖說解決了的問題,不會出現一個函數前邊有二三十個空格的縮進。所以直接使用關鍵字替換原有的普通回調函數即可。 從今年過完年回來,三月份開始,就一直在做重構相關的事情。 就在今天剛剛上線了最新一次的重構代碼,希望高峰期安好,接近半年的Node.js代碼重構。 包含從callback+async.w...
摘要:調試集成環境選擇模塊,簡單分離開發,測試,線上環境。程序保護開機自啟托盤最小化崩潰監控升級一行代碼接入升級平臺,實現客戶端升級功能打包構建一個指令搞定打包項目地址 項目地址 : https://github.com/ConardLi/electron-react electron-react electron + react + react-router + mobx + webpac...
閱讀 3406·2021-11-25 09:43
閱讀 3464·2021-11-19 09:40
閱讀 2464·2021-10-14 09:48
閱讀 1283·2021-09-09 11:39
閱讀 1920·2019-08-30 15:54
閱讀 2821·2019-08-30 15:44
閱讀 1994·2019-08-29 13:12
閱讀 1543·2019-08-29 12:59