提及 前端路由,若是你用過前端 MV* 框架構建 SPA 應用(單頁面應用),對此必定不陌生。html
傳統開發中的 路由,是由服務端根據不一樣的用戶請求地址 URL,返回不一樣內容的頁面,而前端路由則將這些任務經過 JS 在瀏覽器端完成,SPA 應用 則是前端路由的最佳適用場景,由於它結構簡單,只需更新頁面部分顯示內容 也沒必要每次都從服務端獲取內容。前端
react-router 是官方指定和維護的 React 路由庫,它經過管理 URL,實現組件間切換,和狀態 (state) 的變化。node
官方示例教程 react-router-tutorial 寫的貼心又詳細,一共14節,本文內容以官方教程爲準,分紅三章:react
在學習前,須要你對 React 的 JSX 語法有初步的認識,若是瞭解 ES6 語法更好,後續的 React 系列教程我都會用 ES6 來寫。建議初學者跟着教程 碼一遍代碼~ 爲了照顧初學者,我寫的可能會囉嗦些,大神勿噴~webpack
首先你須要安裝 Node.js 和 npm 包管理工具,命令行工具推薦 Git。瀏覽器不能直接解析 JSX 和 ES6 語法,因此須要一個編譯打包工具 這裏選擇 webpack,全局安裝 webpack —— 命令行輸入:git
npm install webpack -g
建立項目目錄:react-router-tutorial,
接着分別建立:文件夾module,index.html,index.js(入口文件),
package.json(定義了項目所需的各個模塊,和配置信息)
webpack.config.js(webpack 配置文件),目錄結構以下:es6
下載以上目錄文件github
index.html:web
1 <!DOCTYPE html> 2 <html lang="zh-CN"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>My First React Router App</title> 6 </head> 7 <body> 8 <div id=app></div> 9 <!-- 編譯後的js文件 --> 10 <script src="bundle.js"></script> 11 </body> 12 </html>
package.json:
其中 npm start 爲 Node.js 啓動模塊的默認命令,這裏用 webpack-dev-server 來運行一個小型 Node.js Express 服務器,它會實時將代碼編譯打包在內存中(注意:這個過程並不會在你的項目文件夾裏 產生任何文件)
啓動項目時,只需在命令行輸入npm start,訪問http://localhost:8080/index.html便可瀏覽結果。npm
1 { 2 "name": "tutorial", 3 "version": "1.0.0", 4 "description": "", 5 "main": "index.js", 6 "scripts": { 7 "start": "webpack-dev-server --inline --hot --content-base ." 8 }, 9 "author": "", 10 "license": "ISC", 11 "dependencies": { 12 "react": "^0.14.7", 13 "react-dom": "^0.14.7", 14 "react-router": "^2.0.0" 15 }, 16 "devDependencies": { 17 "babel-core": "^6.5.1", 18 "babel-loader": "^6.2.2", 19 "babel-preset-es2015": "^6.5.0", 20 "babel-preset-react": "^6.5.0", 21 "http-server": "^0.8.5", 22 "webpack": "^1.12.13", 23 "webpack-dev-server": "^1.14.1" 24 } 25 }
webpack.config.js:
1 module.exports = { 2 // 入口文件 3 entry: './index.js', 4 5 // 輸出文件 6 output: { 7 filename: 'bundle.js', 8 publicPath: '' 9 }, 10 11 module: { 12 loaders: [ 13 // 匹配到js或jsx文件後 使用 babel-loader 來處理 14 // '?'後面是該loader的參數設置(使用了es6和react轉碼器) 15 { 16 test: /\.js[x]?$/, 17 exclude: /node_modules/, 18 loader: 'babel-loader?presets[]=es2015&presets[]=react' 19 } 20 ] 21 } 22 };
如今來 安裝package.json中全部的 依賴模塊,命令行輸入:
npm install
上個廁所或喝杯咖啡 一會就行了,若是你還感受安裝過程慢,也能夠換成淘寶鏡像的安裝方式(先安裝鏡像命令):
npm install -g cnpm --registry=https://registry.npm.taobao.org cnpm install
建立modules/App.js
App.js:
import React from 'react'; // 定義App組件 export default class App extends React.Component { constructor(props) { super(props); } render() { return ( <h1>React Router Tutorial</h1> ); } }
使用 ES6 的import語句代替以前的require()方法來導入模塊,使用class來建立」類」(js中根本不存在類,class只是語法糖)extends用來繼承React.Component,constructor(){}爲構造函數方法,export default定義了模塊對外的接口 也就是」類」App,這裏定義了一個叫 APP 的根組件
若是你對 ES6 的寫法感到困惑,能夠看下這篇文章:如何將 react 中的 ES5 寫法轉化成 ES6
在入口文件index.js中導入App組件 並將它渲染到id爲app的容器裏:
index.js:
import React from 'react'; import ReactDOM from 'react-dom'; import App from './modules/App'; ReactDOM.render( <App/>, document.getElementById('app') );
啓動應用:
npm start
瀏覽器打開http://localhost:8080/index.html就能夠看到如下結果:
ok, 第一個 React 應用啓動成功~ 下面將開始 react-router 的部分。
Router 也是一個組件,但它不會被用來渲染任何內容
ReactDOM.render(<Router/>, document.getElementById('app'))
修改 index.js
1. 導入Router,Route,hashHistory
2. 在render中用Router代替App
下面代碼中的import { Router, Route, hashHistory }是ES6導入模塊的另外一種用法,大括號中指定了從react-router模塊裏導入的變量名,變量名必須與被導入模塊對外接口的名稱相同。
import React from 'react'; import ReactDOM from 'react-dom'; import { Router, Route, hashHistory } from 'react-router' import App from './modules/App'; ReactDOM.render(( <Router history={hashHistory}> <Route path="/" component={App}/> </Router> ), document.getElementById('app'));
啓動應用npm start,訪問http://localhost:8080,會看到和上一節同樣的結果,瀏覽器地址欄變成了http://localhost:8080/#/?_k=bw8nlm,這個稍後解釋~
Router 組件 使用了hashHistory管理路由的歷史,經過監聽切換 URL 的 hash 變化來動態渲染組件。
這裏的path=」/」 表明根路徑,component={App} 意思是渲染組件 App。
當用戶訪問http://localhost:8080或http://localhost:8080/#/時,組件App將被渲染到document.getElementById(‘app’)中。
建立2個新組件:modules/About.js,modules/Repos.js
About.js:
1 import React from 'react'; 2 3 export default class About extends React.Component { 4 constructor(props) { 5 super(props); 6 } 7 8 render() { 9 return ( 10 <div1>About</div> 11 ); 12 } 13 }
Repos.js:
1 import React from 'react'; 2 3 export default class Repos extends React.Component { 4 constructor(props) { 5 super(props); 6 } 7 8 render() { 9 return ( 10 <div>Repos</div> 11 ); 12 } 13 }
而後,修改 index.js:
import React from 'react'; import ReactDOM from 'react-dom'; import { Router, Route, hashHistory } from 'react-router' import App from './modules/App'; // 增長 About 和 Repos 組件 import About from './modules/About' import Repos from './modules/Repos' // 增長2個新路由 ReactDOM.render(( <Router history={hashHistory}> <Route path="/" component={App}/> <Route path="/about" component={About}/> <Route path="/repos" component={Repos}/> </Router> ), document.getElementById('app'));
Route 組件能夠定義多個路由,訪問http://localhost:8080/#/about和http://localhost:8080/#/reops會看到瀏覽中分別渲染About和Repos組件,可見path屬性值定義了 URL 中 #號 以後的路徑參數
Link 組件 幾乎等同於<a/>標籤,是應用中較經常使用的組件
修改 App.js,在組件App中增長一個導航:
1 import React from 'react'; 2 // 導入Link組件 3 import { Link } from 'react-router'; 4 5 export default class App extends React.Component { 6 constructor(props) { 7 super(props); 8 } 9 10 render() { 11 return ( 12 <div> 13 <h1>React Router Tutorial</h1> 14 <ul role="nav"> 15 <li><Link to="/about">About</Link></li> 16 <li><Link to="/repos">Repos</Link></li> 17 </ul> 18 </div> 19 ); 20 } 21 }
Link 組件中的to屬性定義了 URL 中 #號 以後的路徑參數,因此要和 Route 組件中的 path值相對應。
訪問http://localhost:8080看到以下結果:
點擊連接 About 會渲染組件 About,點瀏覽器的回退按鈕 會返回渲染根組件,再點擊 Repos 又會渲染組件Repos,看起來不錯~
上節中,咱們在組件App中添加了一個 導航<ul role=」nav」>…</ul>,但一般,導航應該在每一個視圖中都出現,在不使用 React Router 的狀況下,最簡單的辦法就是將<ul role=」nav」>…</ul>塞到每一個組件裏
但在應用變的複雜時,這個辦法顯然 不夠簡潔,因此 React 提供了另外一個更好的解決辦法:嵌套路由,就是將Route 組件嵌套,分爲兩步:
第一步,修改 index.js,嵌套<Route/>:
1 import React from 'react'; 2 import ReactDOM from 'react-dom'; 3 import { Router, Route, hashHistory } from 'react-router' 4 import App from './modules/App'; 5 import About from './modules/About' 6 import Repos from './modules/Repos' 7 8 ReactDOM.render(( 9 <Router history={hashHistory}> 10 <Route path="/" component={App}> 11 <Route path="/about" component={About}/> 12 <Route path="/repos" component={Repos}/> 13 </Route> 14 </Router> 15 ), document.getElementById('app'));
第二步,修改 App.js,在App組件內部經過this.props.children屬性嵌套進子組件:
1 import React from 'react'; 2 import { Link } from 'react-router'; 3 4 // 增長 this.props.children 用來渲染子組件 5 export default class App extends React.Component { 6 constructor(props) { 7 super(props); 8 } 9 10 render() { 11 return ( 12 <div> 13 <h1>React Router Tutorial</h1> 14 <ul role="nav"> 15 <li><Link to="/about">About</Link></li> 16 <li><Link to="/repos">Repos</Link></li> 17 </ul> 18 </div> 19 20 {this.props.children} 21 ); 22 } 23 }
index.js中<Route path=」/about」 component={About}/>和 <Route path=」/repos」 component={Repos}/>就成爲<Route path=」/」 component={App}/>的子路由。訪問http://localhost:8080,會先渲染App組件,點擊About 後,會在App組件內部渲染About組件。
一樣的,當用戶訪問http://localhost:8080/#/about時,先渲染App組件,而後在內部渲染About組件:
1 <App> 2 <About/> 3 </App> 4 5 <App> 6 <Repos/> 7 </App>
本篇示例源碼:
react-router-demo-part1