Lesson1
1、首先確保安裝了Node.js和npm依賴包管理工具
2、在git上克隆官方示例程序
git clone https://github.com/reactjs/react-router-tutorial
cd react-router-tutorial
cd lessons/01-setting-up
npm install
npm start
運(yùn)行完成后打開(kāi):http://localhost:8080
就可以在瀏覽器看見(jiàn): "Hello React Router"
打開(kāi)modules/App.js,更改里面內(nèi)容,可以看到瀏覽器上的內(nèi)容隨時(shí)變化!
Lesson2 : rendering a router
1、打開(kāi)index.js
首先import Router、Route和hashHistory,然后render Reouter替換原來(lái)的App組件。這是由于:React Router 是一個(gè)組件。Router 本身是一個(gè)容器,真正的路由都要由Route去定義。
// ...
import { Router, Route, hashHistory } from 'react-router'
render(( <Router history={hashHistory}>
<Route path="/" component={App}/>
</Router>), document.getElementById('app'))
上面訪問(wèn)的路由為“/”,組件是App。路由的切換由URL得hash變化決定,即URL的#部分發(fā)生變化。
2、添加Scene
.modules/About.js
.modules/Repos.js
// modules/About.js
import React from 'react'export
default React.createClass({ render() { return <div>About</div> }})
// modules/Repos.js
import React from 'react'export
default React.createClass({ render() { return <div>Repos</div> }})
把場(chǎng)景添加到路由里面
// insert into index.js
import About from './modules/About'
import Repos from './modules/Repos'
render(( <Router history={hashHistory}>
<Route path="/" component={App}/>
{/* add the routes here */}
<Route path="/repos" component={Repos}/>
<Route path="/about" component={About}/>
</Router>), document.getElementById('app'))
現(xiàn)在訪問(wèn): http://localhost:8080/#/about 和 http://localhost:8080/#/repos
Lesson3 Navigating with Link
Link類(lèi)似于之前用過(guò)的<a/>標(biāo)簽
// modules/App.js
import React from 'react'import { Link } from 'react-router'
export default React.createClass({
render() {
return ( <div>
<h1>React Router Tutorial</h1>
<ul role="nav">
<li><Link to="/about">About</Link></li>
<li><Link to="/repos">Repos</Link></li>
</ul>
</div> ) }})
訪問(wèn):http://localhost:8080
lesson4 嵌套的路由
這個(gè)功能可以做出類(lèi)似Nav的功能,Route里面可以嵌套多個(gè)Route
// index.js// ...
render((
<Router history={hashHistory}>
<Route path="/" component={App}>
{/* make them children of App */}
<Route path="/repos" component={Repos}/>
<Route path="/about" component={About}/>
</Route>
</Router>), document.getElementById('app'))
在App里面
// modules/App.js// ...
render() {
return (
<div>
<h1>React Router Tutorial</h1>
<ul role="nav">
<li><Link to="/about">About</Link></li>
<li><Link to="/repos">Repos</Link></li>
</ul>
{/* add this */}
{this.props.children}
</div>
) }
則UI結(jié)構(gòu)如下:
<App>
<About/>
</App>
<App>
<Repos/>
</App>
Lesson 5 Active Links
通過(guò)行內(nèi)樣式表改變Link的樣式:
// modules/App.js
<li><Link to="/about" activeStyle={{ color: 'red' }}>About</Link></li>
<li><Link to="/repos" activeStyle={{ color: 'red' }}>Repos</Link></li>
也可以添加className在css文件中設(shè)置樣式
// modules/App.js
<li><Link to="/about" activeClassName="active">About</Link></li>
<li><Link to="/repos" activeClassName="active">Repos</Link></li>
創(chuàng)建css文件,并在index.html中引入
// index.html
<link rel="stylesheet" href="index.css" />
我們不用為每個(gè)Link都添加activeClassName,這里我們使用一個(gè)spread operator
創(chuàng)建modules/NavLink.js
// modules/NavLink.js
import React from 'react'
import { Link } from 'react-router'
export default React.createClass({
render() {
return <Link {...this.props} activeClassName="active"/>
}
})
在modules/App.js中這樣寫(xiě):
// modules/App.js
import NavLink from './NavLink'
<li><NavLink to="/about">About</NavLink></li>
<li><NavLink to="/repos">Repos</NavLink></li>
Lesson 6 URL Params
1、添加一個(gè)帶參數(shù)的路由
建立一個(gè)文件:modules/Repo.js
// modules/Repo.js
import React from 'react'
export default React.createClass({
render() {
return (
<div> <h2>{this.props.params.repoName}</h2> </div>
)
}})
2、在index.js里面添加新路由
// import Repo
import Repo from './modules/Repo'
render((
<Router history={hashHistory}>
<Route path="/" component={App}>
<Route path="/repos" component={Repos}/>
{/* add the new route */}
<Route path="/repos/:userName/:repoName" component={Repo}/>
<Route path="/about" component={About}/>
</Route>
</Router>), document.getElementById('app'))
3、在Repo.js里面添加跳轉(zhuǎn)的Link
// Repos.js
import { Link } from 'react-router'
export default React.createClass({
render() {
return (
<div>
<h2>Repos</h2>
{/* add some links */}
<ul>
<li><Link to="/repos/reactjs/react-router">React Router</Link></li>
<li><Link to="/repos/facebook/react">React</Link></li>
</ul>
</div> )
}})
附:path屬性可以匹配通配符
<Route path="/hello/:name">
:paramName 匹配URL的一個(gè)部分,知道遇到下一個(gè)/、?、#為止。路徑參數(shù)可以通過(guò)this.props.params.paramName獲取
// 匹配 /hello/michael
// 匹配 /hello/ryan
<Route path="/hello(/:name)">
()表示URL的這個(gè)部分是可選的
// /hello
// /hello/micale
// /hello/ryan
<Route path="/files/.">
匹配任意字符直到下一個(gè)字符為止,非貪婪模式
// /files/hello.jpg
// /files/hello.html
<Route path="/files/">
// /files/
// /files/a
// /files/a/b
<Route path="/*/.jpg">
**匹配任意字符,直到遇到下一個(gè)/、?、#為止,貪婪模式
// /files/hello.jpg
// /files/path/to/file.jpg
路由匹配規(guī)則是從上到下執(zhí)行,一旦發(fā)現(xiàn)匹配,就不再匹配其余的規(guī)則了。
Lesson 7 更多的嵌套
1、將Repo的路由放進(jìn)Repos的路由
//index.js
<Route path="/repos" component={Repos}>
<Route path="/repos/:userName/:repoName" component={Repo}/>
</Route>
//Repos.js
<div>
<h2>Repos</h2>
<ul>
<li><Link to="/repos/reactjs/react-router">React Router</Link></li>
<li><Link to="/repos/facebook/react">React</Link></li>
</ul>
{/* will render Repo.js when at /repos/:userName/:repoName */} #{this.props.children}
</div>
2、添加NavLink
// modules/Repos.js
import NavLink from './NavLink'
<li><NavLink to="/repos/reactjs/react-router">React Router</NavLink></li>
<li><NavLink to="/repos/facebook/react">React</NavLink></li>
Lesson 8 Index Routes
當(dāng)我們?cè)L問(wèn)/在app里出現(xiàn)了一個(gè)空白,我們想讓他渲染一個(gè)Home的component
IndexRoute顯示指定Home是根路由的子組件,即指定默認(rèn)情況下加載的子組件。IndexRoute組件沒(méi)有路徑參數(shù)path
//modules/Home.js
import React from 'react'
export default React.createClass({
render() {
return <div>Home</div>
}
})
在App里如果沒(méi)有任何場(chǎng)景被渲染的時(shí)候,就渲染Home
// modules/App.js
import Home from './Home'
<div>
{/* ... */}
{this.props.children || <Home/>}
</div>
添加IndexRoute
// index.js
import { Router, Route, hashHistory, IndexRoute } from 'react-router'
import Home from './modules/Home'
render((
<Router history={hashHistory}>
<Route path="/" component={App}>
{/* add it here, as a child of / */}
<IndexRoute component={Home}/>
<Route path="/repos" component={Repos}>
<Route path="/repos/:userName/:repoName" component={Repo}/>
</Route>
<Route path="/about" component={About}/>
</Route>
</Router>), document.getElementById('app'))
IndexRoute沒(méi)有path ,
Lesson 9 Index Links
添加可以返回Home的方法,IndexLink
//App.js
import { IndexLink } from 'react-router'
<li><IndexLink to="/" activeClassName="active">Home</IndexLink></li>
使用onlyActiveOnIndex Property
<li><Link to="/" activeClassName="active" onlyActiveOnIndex={true}>Home</Link></li>
或
<li><NavLink to="/" onlyActiveOnIndex={true}>Home</NavLink></li>
Lesson 10 Clean URLs with Browser History
配置瀏覽器歷史
//index.js
// bring in browserHistory instead of hashHistory
import { Router, Route, browserHistory, IndexRoute } from 'react-router'
render((
<Router history={browserHistory}>
{/* ... */}
</Router>
), document.getElementById('app'))
點(diǎn)擊鏈接,然后刷新,會(huì)得到
Cannot GET /repos
這是因?yàn)榉?wù)器端配置的原因,目前的服務(wù)器不知道如何處理URL
在package.json中添加:
"start": "webpack-dev-server --inline --content-base . --history-api-fallback"
同時(shí)要將我們的文件引用,相對(duì)路徑為絕對(duì)路徑
//index.html
<link rel="stylesheet" href="/index.css">
<script src="/bundle.js"></script>
重啟服務(wù)器:npm start
Lesson 11 Production-ish Server
Webpack dev server 不是生產(chǎn)模式的服務(wù)器,下面配置生產(chǎn)模式下的服務(wù)器
安裝依賴模塊:
npm install express if-env compression --save
更新package.json
"scripts": {
"start": "if-env NODE_ENV=production && npm run start:prod || npm run start:dev",
"start:dev": "webpack-dev-server --inline --content-base . --history-api-fallback",
"start:prod": "webpack && node server.js"
},
此時(shí)的腳本說(shuō)明,如果運(yùn)行npm start 啟動(dòng)服務(wù)器,如果在生產(chǎn)環(huán)境下執(zhí)行start:prod,如果不是,執(zhí)行start:dev
在根目錄下創(chuàng)建server.js
//server.js
var express = require('express')
var path = require('path')
var compression = require('compression')
var app = express()
// serve our static stuff like index.css
app.use(express.static(__dirname))
// send all requests to index.html so browserHistory in React Router works
app.get('*', function (req, res) {
res.sendFile(path.join(__dirname, 'index.html'))
})
var PORT = process.env.PORT || 8080
app.listen(PORT, function() {
console.log('Production Express server running at localhost:' + PORT)
})
現(xiàn)在運(yùn)行:NODE_ENV=production npm start
現(xiàn)在就有了生產(chǎn)環(huán)境下的app
此時(shí)運(yùn)行:http://localhost:8080/package.json就可以獲得json文件的內(nèi)容,讓我們重新更新文件的路徑來(lái)解決這個(gè)問(wèn)題
創(chuàng)建一個(gè)public的文件夾,并且把index.html 和 index.css文件放進(jìn)去
1、更新server.js指向正確的路徑:
// server.js
app.use(express.static(path.join(__dirname, 'public')))
app.get('*', function (req, res) {
res.sendFile(path.join(__dirname, 'public', 'index.html'))
})
2、更新webpack編譯時(shí)的輸出路徑
// webpack.config.js
output: {
path: 'public',
}
3、在服務(wù)器啟動(dòng)腳本中,添加該路徑
"start:dev": "webpack-dev-server --inline --content-base public --history-api-fallback",
4、添加一些代碼
// webpack.config.js
var webpack = require('webpack')
module.exports = {
//...
plugins: process.env.NODE_ENV === 'production' ? [ new #webpack.optimize.DedupePlugin(), new #webpack.optimize.OccurrenceOrderPlugin(), new #webpack.optimize.UglifyJsPlugin() ] : [],
//...
}
在express中添加conpression
// server.js
var compression = require('compression')
var app = express()
// must be first!
app.use(compression())
現(xiàn)在我們可以在生產(chǎn)模式下啟動(dòng)服務(wù)器了
NODE_ENV=production npm start
Lesson 12 Navigating Programatically
之前使用的都是Link進(jìn)行路由的跳轉(zhuǎn),而實(shí)際使用更多的使用表單提交,按鈕點(diǎn)擊來(lái)進(jìn)行路由的切換
在Repos創(chuàng)建一個(gè)表單提交
// modules/Repos.js

那么可以使用以下方式保存路由

Lesson 13 服務(wù)器端渲染
Lesson 14 結(jié)束語(yǔ)
附:組件介紹
1、Redirect組件:<Redirect>用于路由的跳轉(zhuǎn),即訪問(wèn)一個(gè)路由,就會(huì)自動(dòng)跳轉(zhuǎn)到另一個(gè)路由.
<Route path="inbox" component={Inbox}>
{/* 從 /inbox/messages/:id 跳轉(zhuǎn)到 /messages/:id */}
<Redirect from="messages/:id" to="/messages/:id" />
</Route>
訪問(wèn)/inbox/message/5,會(huì)自動(dòng)跳到/messages/5
2、IndexRedirect 組件
訪問(wèn)根路由的時(shí)候,將用戶重定向到某個(gè)子組件。
3、Link 組件取代<a>標(biāo)簽,
4、IndexLink 如果連接到根路由不要使用Link要使用IndexLink(使用路由的精確匹配)
5、history屬性
Router的history屬性監(jiān)聽(tīng)瀏覽器地址的變化,并將URL解析為一個(gè)地址對(duì)象,供React Router 匹配
6、表單處理:Link組件用于正常的用戶點(diǎn)擊跳轉(zhuǎn),但有時(shí)候還需要表單跳轉(zhuǎn)、點(diǎn)擊按鈕跳轉(zhuǎn)。
有兩種方案:1、使用browserHistory.push
2、使用context對(duì)象
7、路由的鉤子
每個(gè)路由都有Enter和Leave的鉤子,當(dāng)用戶進(jìn)入或者離開(kāi)該路由時(shí)觸發(fā)