React Router 從v3升級到v4的踩坑之旅
React 應用很少不用react-router這個包的。marknoteapp.com之前一直用v3,看到v4出來後一直心癢。最近,抱著 用新不用舊 的理念,手賤升了一下級。這一升級,差不多2天功夫花掉啦。
概述
和 Angular 那改朝換代般的升級相比,React技術棧一直以其穩定的 API 而備受好評。不過,這次 react-router 從v3到v4的升級,簡直是砸 React 的好名聲的。
如果你以為這個升級只是在 package.json 中簡單的改一下版本號就好了,那你就大錯特錯了。我相信,你改完版本號之後,會像我當初一樣面對成堆的問題而驚奇不已。
事實上,不少開發者對v4的的變化感到驚詫,比如下面的這位就抱怨v3升v4花了10天。
總之,v3到v4不是一件幾分鐘就可以搞定的事。一箇中型的專案,花2-3天在升級上很正常。
重要的事情說三遍:升級之前先備份!升級之前先備份!!升級之前先備份!!!
備份完之後,就和我一起來踩坑吧!
坑一: 包的選擇
react router v4 是對v3的重寫。現在分為三個包:
- react-router:只提供核心的路由和函式。一般的應用不會直接使用;
- react-router-dom:供瀏覽器/Web應用使用的API。依賴於react-router, 同時將react-router的API重新暴露(export)出來;
- react-router-native:供 React Native 應用使用的API。同時將react-router的API重新暴露(export)出來;
對於一般的web應用,直接引入第二個包即可,安裝之前先刪除老版本react-router:
npm uninstall react-router --save
npm install --save react-router-dom
或者你像我一樣,更親睞yarn的話:
yarn remove react-router
yarn add react-router-dom
引入完之後,要做的第一件事就是將所有原先引入react-router的地方都換成react-router-dom。
// 原先
import { Route, Redirect } from 'react-router';
// 現在
import { Route, Redirect } from 'react-router-dom';
坑二:不再用,要用具體的實現
在V3中,一般使用,只是在其history屬性中指定是哪種實現: hashHistory或者browserHistory之類的。
// v3
import { Router, hashHistory } from 'react-router';
const MyApp = () => (
<Router history={hashHistory}>
...
</Router>
);
而在v4中,你需要直接用具體的實現,比如HashRouter或者BrowserRouter:
// v4
import { HashRouter } from 'react-router-dom';
const MyApp = () => (
<HashRouter>
...
</HashRouter>
);
坑三: 對於redux應用,你需要用react-router-redux
如果你像我一樣,也用了redux的話,那麼恭喜你,你還得用react-router-redux。
而且,這個包的正式版4.x不支援react-router v4。你需要用 alpha 版 的react-router-redux。在package.json 里加入react-router-redux~5.0.0或者用yarn:
yarn add react-router-redux@5.0.0
雖然我用的是alpha 6,貌似也沒啥毛病。
而且,對於redux應用,你不可以用BrowserRouter,或者HashRouter,而應該使用ConnectedRouter。
比如,我的程式碼:
import { Route } from 'react-router-dom';
import history from 'history/createBrowserHistory';
import { ConnectedRouter, routerReducer, routerMiddleware } from 'react-router-redux';
import App from './components/App';
ReactDOM.render(
<Provider store={store}>
<ConnectedRouter history={history()}>
<Route path="/" component={App} />
</ConnectedRouter>
</Provider>,
document.getElementById('root')
);
坑四:巢狀 Route和Index Route 全失效啦
在V3中,巢狀Route和Index Route的使用是很常見的。比如marknoteapp.com中,部落格部分的路由定義如下:
<Route path="/blog/:bloggerId" component={BlogLayout}>
<IndexRoute component={BlogList}/>
<Route path="/blog/:bloggerId/settings" component={BlogConfig}/>
<Route path="/blog/:bloggerId/about" component={BlogAbout}/>
<Route path="/blog/:bloggerId/dashboard" component={BlogDashboard}/>
<Route path="/blog/:bloggerId/:postId" component={BlogPost}/>
</Route>
這樣/blog/123 會觸發BlogLayout,和BlogList,而/blog/123/456則會觸發BlogLayout和BlogPos。很方便。
在 v4 中 IndexRoute沒了,然後不允許Route巢狀。
要實現類似的功能,需要兩步:
- 在父元件中將路由給”/blog/:blggerId”給”BlogLayout”,程式碼如下:
<Switch>
<Route path="/blog/:bloggerId" component={BlogLayout} />
</Switch>
- 然後在BlogLayout中定義Blog這部分的路由:
<div className='content' id="blog-content">
<Switch>
<Route exact path="/blog/:bloggerId/settings" component={BlogConfig} />
<Route exact path="/blog/:bloggerId/about" component={BlogAbout} />
<Route exact path="/blog/:bloggerId/dashboard" component={BlogDashboard} />
<Route exact path="/blog/:bloggerId/:postId" component={BlogPost} />
<Route exact path="/blog/:bloggerId" component={BlogList} />
</Switch>
</div>
這裡有幾點需要注意:
- 在 v3 中,Router 的定義一般是全域性的,所有的路由都在一個檔案中定義;而在 v4 中Router可以出現在任何元件中。
- 在 v4 中,Router 在UI元件中載入對應的子 UI 元件。比如,我的上面的程式碼中,路由會根據URL 匹配來在BlogConfig/BlogAbout/BlogDashboard/BlogPost/BlogList選擇一個,載入進來,並顯示在id為 blog-content 的 div 中。
- Switch 用來從多個Route中選出一個。否則會觸發多個元件。
- exact 這個屬性來表示精確匹配,否則,URL /blog/123/456 也可以觸發BlogList。
- 由於v4中沒有了IndexRoute,將BlogList 的 path宣告得和它的父元件一樣就可以了。
- 在v4中,path屬性永遠是絕對路徑。如果你不想重複當前級別的路徑,可以使用match這個屬性。
比如在我的程式碼中, 在BlogLayout中,我可以 match.url 來代替“/blog/:bloggerId”,程式碼如下:
<div className='content' id="blog-content">
<Switch>
<Route exact path={"${match.url}/settings"}} component={BlogConfig} />
<Route exact path={"${match.url}/about"} component={BlogAbout} />
<Route exact path={"${match.url}/dashboard"} component={BlogDashboard} />
<Route exact path={"${match.url}/:postId"} component={BlogPost} />
<Route exact path={"${match.url}}component={BlogList} />
</Switch>
</div>
坑五:不再可以從params中取URL引數了
在 v3 中,你可以從params這個屬性中取到URL 中傳遞過來的引數。比如我在 BlogLayout中獲取bloggerID,v3的程式碼如下:
//v3
const bloggerId = this.props.params.bloggerId;
這個屬性在v4中不再被自動注入了,需要從match屬性中獲取。程式碼如下:
//v4
const bloggerId = this.props.match.params.bloggerId;
這裡還有一個小小的差異。在v3中,引數會自動解碼,而在v4中不會。所以如果你的URL引數中有特殊字元的話,你可能需要自己調decodeURI之類的方法解碼。
坑六:Route的onEnter, onUpdate和onLeave之類的事件沒有了
在v3中,我可以使用使用 Route的 onEnter, onUpdate和 onLeave事件來做一些事情。
比如,我可以在onUpdate中觸發google analytics來跟蹤使用者行為:
//v3
<Router history={browserHistory} onUpdate={doLogPageView} >
這裡doLogPageView實現如下:
function doLogPageView(){
const sUrl = window.location.pathname + window.location.search;
ReactGA.set({ page: sUrl });
ReactGA.pageview(sUrl);
}
在v4中,Route的事件沒了。我查了很多資料,貌似有兩種解決辦法:
- 將程式碼移到每個元件的componentWillMount 方法中去。這意味著每個元件都要做這樣的事情,程式碼冗餘;
- 使用withRouter 這個HOC (高階元件);
我用的後一種方法,程式碼如下:
import { withRouter } from 'react-router-dom';
import ReactGA from 'react-ga';
class App extends Component {
componentWillMount() {
this.setState(
{
height: window.innerHeight,
sidebarOpen: false,
docked: true,
}
);
this.onSetSidebarOpen = this.onSetSidebarOpen.bind(this);
this.props.history.listen((location, action) => {
const url = location.pathname + location.search + location.hash;
ReactGA.set({ page: url });
ReactGA.pageview(url);
console.log(`current URL:${url}`);
});
}
}
export default withRouter(App);
總結
簡單的說,react-router v4 簡直不像是v3的升級,而更像一次天馬行空的重寫。其理念是完全不一樣的。v3 更像是AOP (面向切面程式設計)的樣子:路由全域性配置,可以通過 事件進行一些共通的處理;v4 用react-router 團隊的話說是 real component,可以嵌入任何元件中。
據說 facebook 的react 團隊對 v3之前的react-router 一直沒有好感,而v4之後則頗多溢美之詞。
但是客觀的講,從v3升級到 v4遇到的問題還是比較多的。尤其是官方居然沒有一個全面的指南。雖然在 這裡 有一個文件,但是很多實際升級時會遇到的問題都沒有涉及。
你的專案用的v3 還是 v4?需要升級嗎?升級遇到什麼問題,可以在下面留言,一起探討。
參考
感謝作者marknote授權釋出。
作者簡介: marknote,個人開發者,專注iOS和web相關技術,喜好嘗試新技術,熱愛分享。 個人郵箱:[email protected],點選檢視: 部落格地址,簡書個人主頁。
相關推薦
React Router 從v3升級到v4的踩坑之旅
React 應用很少不用react-router這個包的。marknoteapp.com之前一直用v3,看到v4出來後一直心癢。最近,抱著 用新不用舊 的理念,手賤升了一下級。這一升級,差不多2天功夫花掉啦。概述和 Angular 那改朝換代般的升級相比,Rea
react-router 從 v3 版本升到 v4 版本,升級小記
必須 寫作 his 瀏覽器 down red isp 跳轉 mark react-router v4 跟 react 一樣拆成了兩部分,核心的 react-router 和依運行環境而定的 react-router-dom 或 react-router-native(跟
Vue-router踩坑之旅(2)
報錯:vue.esm.js?efeb:591 [Vue warn]: Do not use built-in or reserved HTML elements as component id: head原因:元件內部設定的name值是vue內的保留值。解決:修改元件內nam
webpack踩坑之旅
image cnp conf 項目 style win src 丟失 文件 1、安裝webpack失敗問題 錯誤原因: 這主要是我以普通用戶的身份進行webpack的全局安裝,權限不夠。 【普通用戶】 說白了就是通過運行window+r+cmd進入的命令行 解決方式:
Ubuntu搭建Hadoop的踩坑之旅(三)
namenode 結束 ctu mapreduce 分布 使用 framework 2.6 start 之前的兩篇文章介紹了如何從0開始到搭建好帶有JDK的Ubuntu的過程,本來這篇文章是打算介紹搭建偽分布式集群的。但是後來想想反正偽分布式和完全分布式差不多,所幸直接介紹
一次痛苦又甜蜜的微信支付踩坑之旅
call utf-8 客戶 打開 區分 AD times jpg 運算 凡是和錢打交道的事,沒有一樣是容易的。這是我第一次接觸微信支付,發現網上還是有很多同學在求助,XXX了怎麽辦?XXX是什麽情況?為了幫助更多的小夥伴脫離“苦海”,我決定寫下這次的踩坑之旅,給更多的人幫助
python 3.6.1 安裝scrapy踩坑之旅
ext href sta 版本 deb targe IE src pyw 系統環境:win10 64位系統安裝 python基礎環境配置不做過多的介紹 window環境安裝scrapy需要依賴pywin32,下載對應python版本的exe文件執行安裝,下載的pywin
快應用開發的踩坑之旅
校驗 未來 bug 失敗 作用 定義 無需 功能 com 前言 嘗試一款新的開發框架的時候勢必會遇見各種各樣的問題。可能因為一開始不熟悉文檔,導致配置錯誤,或是api使用錯誤。當然開發的時候我們也不能確認框架沒有問題,是否存在bug。所以在某些出錯的情況下,我們也許會不斷懷
記一次修改php.ini不生效的踩坑之旅
前言 想給公司的測試環境裝一個xdebug,按照以往的方式(之前已經裝過很多次了),編譯安裝了xdebug,然後修改php.ini,將xdebug擴充套件加進去,可是,不論怎麼改,都不生效,xdebug就是沒有。 首先,我想到的是xdebug版本不對,由於之前有過這種經驗,xdebug安裝了
小程式wepy踩坑之旅(五)----- 購物車的實現
首先大家可以看下演示效果 我先把封裝的幾個元件程式碼放到前面。 1.購物車數量加減cart-count.wpy元件 <template> <view class="cart-count"> <vi
小程式wepy踩坑之旅(四)----- 簡單的動畫
大家可以先看下官網小程式apianimation:https://developers.weixin.qq.com/miniprogram/dev/api/api-animation.html,看完之後推薦看一下http://www.jb51.net/article/102263
小程式wepy踩坑之旅(三)----- 微信小程式wepy左滑刪除特效原始碼
我寫在了shop_cart.wepy裡,原始碼就在下面註釋很詳細,直接拷貝到新建的.wpy就可以使用 <template> <view class="item-box"> <view class="items">
小程式wepy踩坑之旅(一)---- thirdScriptError sdk uncaught third Error module "npm/lodash/_nodeUtil.js
近期一直在學小程式,作為新手,比較了下mpvue和wepy兩個小程式框架,mpvue作為美團剛出來的vuejs開發看起來很不錯,學習成本很低,但是對於在實際專案開發中,mpvue剛出來,很多資料,比如踩坑,比較少,而we
Flutter接入現有Android工程踩坑之旅
把Flutter作為一個模組接入到現有的Android工程,Flutter有官方推薦方案 Add Flutter to existing apps,通過這樣的工程配置,可以在debug支援HotReload,也可以輸出Release包供釋出。不過在使用過程中有一些需要調整的地方,特此記錄希望對大家能有借鑑意義
快應用開發踩坑之旅
前言 嘗試一款新的開發框架的時候勢必會遇見各種各樣的問題。可能因為一開始不熟悉文件,導致配置錯誤,或是api使用錯誤。當然開發的時候我們也不能確認框架沒有問題,是否存在bug。所以在某些出錯的情況下,我們也許會不斷懷疑自己,懷疑框架,最終懷疑人生。這時候就需要開
AIDL踩坑之旅
AIDL 踩坑之旅 ---- 通過掃雷遊戲學習 AIDL 跨程序在兩個APP之間進行通訊 某天下班在地鐵上玩半年前寫的掃雷小遊戲的時候, 突然產生了一個想法, 能不能設計一套演算法讓遊戲自動進行呢? 由程式來尋找最優解, 瞬間完成掃雷, 但是由於我對演算
新手學習VUE踩坑之旅---methods裡面使用箭頭函式要注意this
VUE的methods物件裡面如果函式使用箭頭函式會導致this指向的不是vue例項$vm 例子:想寫一個點選事件:點選輸入框的“x”,即可清空文字框的內容 首先為輸入框增加一個ref屬性(ref=“inputUser”),然後為“x”加一個點選事件(@click=“deleteInp”)
大疆無人機Android版SDK開發踩坑之旅(一)----前言
最近一段時間一直在做大疆無人機安卓版開發,這水也是挺深的,不仔細看官網SDK的介紹就會遇到各種各樣的坑,簡單記錄一下,希望可以讓其他人少走一些彎路。 安卓端用到的SDK大概有兩種:Android SDK和Android UX SDK Android SDK(官網介紹): 開發人員可以通過SDK
vue踩坑之旅
1.vue專案外部js(es5,es6),css檔案的引入; 方法一: 在index.html通過script ,link標籤引入,要將檔案先放入static資料夾(靜態資源)下, 注意不能放src下 方法二: 如果是es6,參照export和
cnpm私有倉庫踩坑之旅
為了方便團隊內部成員程式碼的共用,不寫重複而有無意義的程式碼,打算搭建團隊內部私有的cnpm倉庫。 Start 從cnpm.org clone 整個專案。 git clone https://github.com/cnpm/cnpmjs.o