概述
說起 前端路由,如果你用過前端 MV* 框架構建 SPA 應用(單頁面應用),對此一定不陌生。
傳統開發中的 路由,是由服務端根據不同的用戶請求地址 URL,返回不同內容的頁面,而前端路由則將這些任務通過 JS 在瀏覽器端完成,SPA 應用 則是前端路由的最佳適用場景,因為它結構簡單,只需更新頁面部分顯示內容 也不必每次都從服務端獲取內容。
react-router 是官方指定和維護的 React 路由庫,它通過管理 URL,實現組件間切換,和狀態 (state) 的變化。
准備工作
官方示例教程 react-router-tutorial 寫的貼心又詳細,一共14節,本文內容以官方教程為准,分成三章:
在學習前,需要你對 React 的 JSX 語法有初步的認識,如果了解 ES6 語法更好,后續的 React 系列教程我都會用 ES6 來寫。建議初學者跟着教程 碼一遍代碼~ 為了照顧初學者,我寫的可能會啰嗦些,大神勿噴~
首先你需要安裝 Node.js 和 npm 包管理工具,命令行工具推薦 Git。瀏覽器不能直接解析 JSX 和 ES6 語法,所以需要一個編譯打包工具 這里選擇 webpack,全局安裝 webpack —— 命令行輸入:
npm install webpack -g
創建項目目錄:react-router-tutorial,
接着分別創建:文件夾module,index.html,index.js(入口文件),
package.json(定義了項目所需的各個模塊,和配置信息)
webpack.config.js(webpack 配置文件),目錄結構如下:
index.html:
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即可瀏覽結果。
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 組件
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