基於react-router的單頁應用
序
現在用react寫單頁應用基本上都是用react-router做前端路由了吧!最近在使用react-router的過程中遇到了不少問題,在這裡總結一下。
瀏覽器url
react-router預設提供的history是 createHashHistory ,即它用到的是 URL 中的 hash(#
)部分去建立形如
example.com/#/some/path
的路由,所以你會看到url多了類似 _key=s1gvrm
的 query,這真的很難看。而且這也不是官方在實際生產應用中所推薦的。要改變這種情況的話,我們需要去使用
createBrowserHistory ,在我們的程式碼中引入history,並在Router元件處呼叫
方法即可。
import React from "react";
import ReactDOM from "react-dom";
import createBrowserHistory from "history/lib/createBrowserHistory"
import { Router,Route,Link,browserHistory,IndexRoute,IndexLink } from "react-router";
class App extends React.Component{
render(){
return(
...
);
}
};
ReactDOM.render((
<Router history={ createBrowserHistory() }>
...
</Router>
),document.getElementById("app"));
其實關於url還有許多的細節,因為react-router本身就是構建於history之上的。單頁應用的url只不過是針對於react- router的一個標示而已,介面間的跳轉全然由react-router決定,react-router檢查當前url隨後渲染匹配的路由元件,所以在 瀏覽器上直接輸入一個正確的非根路徑你甚至只能看到404。
元件通訊
這是一個用react寫程式碼永遠都繞不開的話題。react-router是基於react開發的,所以它的每一個route都是一個元件。路由組 件間的通訊一般藉由Link來實現,而根據路由引數的不同,還可以分成兩種。這個很容易理解,因為這兩種方式和我們平時寫的後臺路由並沒有什麼太大差別。
1 param,param通過/:param的方式傳遞。
比如說我們現在有一個顯示訊息列表的路由元件,我們需要在點選每一個訊息的時候能跳轉到顯示被點選訊息詳情的路由元件。這個時候我們訊息詳情路由元件可以是這樣定義的:
<Route path="News_detail/:news_id" component={ news_detail }/>
在訊息列表路由元件那裡我們讓每一條訊息都是這樣的一個Link:
<Link to={ `News_detail/${ element.timestamp }` } >{ element.news }</Link>這樣的話我們的訊息詳情元件就能通過 this.props.params 獲取到訊息列表元件傳遞而來的 element.timestamp 引數了。
2 query
<Link to="/Activity_Publish" query={{ timestamp : element.timestamp }}>編輯</Link>
在 /Activity_Publish 對映的路由元件裡面可以通過
var { query } = this.props.location;
var timestamp = query.timestamp;
獲取到query引數。
相對於param,侷限性更小,你可以根據需要傳遞更多的引數,只不過有點類似於get請求,傳遞的引數會附帶在url後面,看起來有點醜,而且幾乎無隱蔽性可言。
3 state
不過還是有第三種方式的,就是state,這種方式藉助了location 物件,
location 物件
可以簡單的認為是 url 的物件形式表示,這裡要提的是
location.state
,每個 URL 都會對應一個 state 物件,你可以在物件裡儲存資料,但這個資料卻不會出現在 url 中。實際上,資料被存在了 sessionStorage 中,所以這種方式簡直就是query的加強版。
<Link to="/Activity_Publish" state={{ timestamp : element.timestamp }}>編輯</Link>
按需載入
react-router協同webpack的使用可以實現元件的按需載入,而且 這種按需載入完全是非同步的,這點特別酷炫,你不再需要一口氣載入那麼大的js檔案,即使裡面包含著許多使用者甚至都不會使用到的web元件。你可以只根據需 要載入使用者瀏覽的那些元件,這個舉措將會幫你大大的降低首屏渲染的時間。
實現這個功能很簡單,比如在一開始你的路由是這樣子的:
...
import News_detail from "./marriage_component/activity/news_detail.jsx";
...
class App extends React.Component{
render(){
...
}
};
ReactDOM.render((
<Router history={ createBrowserHistory() }>
<Route path="/marriage_app" component={ App }>
...
<Route path="/marriage_app/News_detail/:news_id" component={ News_detail }/>
...
把它改成這樣就行了:
...
//import News_detail from "./marriage_component/activity/news_detail.jsx";
...
class App extends React.Component{
render(){
...
}
};
ReactDOM.render((
<Router history={ createBrowserHistory() }>
<Route path="/marriage_app" component={ App }>
...
<Route path="/marriage_app/News_detail/:news_id" getComponent={ (nextState, callback) =>{
require.ensure( [ ], (require) => {
callback(null, require("./marriage_component/activity/news_detail").default)
}) } }/>
...
一開始的元件不再需要被匯入,webpack會幫你在需要的時候引入它。
最後
這種前後端分離的模式真的很贊,但似乎對SEO並不是很友好。如果真的需要SEO,又追求於實現前後端的真正解耦,是不是以Nodejs為中間層的架構模式會成為一種解決方案呢?