使用示例
使用 react-router 实现前端路由:建立在history上,方便进行路由管理
下载
> npm install react-router --save > npm install history --save
加载
//app.js var React=require("react"); var ReactDOM=require("react-dom"); var ReactRouter=require("react-router"); var Router=ReactRouter.Router; var Route=ReactRouter.Route;
使用Router
方式1:
var routes=( <Router> <Route path="/" component={App}> <Route path="child/:id" component={Child} /> <Route path="*" component={NoMatch} /> </Route> </Router> ); ReactDOM.render(routes,document.getElementById("app"));
方式二:
var routes={ path:'/', component:App, childRoutes:[ {path:"/child/:id",component:Child}, {path:"*",component:NoMatch} ] }; ReactDOM.render(<Router routes={routes} />,document.getElementById("app"));
定义Components
var App=React.createClass({ render:function(){ return ( <div> <Header/> <div className="container"> {this.props.children || "Welcome !"} </div> </div> ) } }); var Header=React.createClass({ render:function(){ return (<h1>Header</h1>) } }) var Child=React.createClass({ render:function(){ return ( <h3> I am Child {this.props.params.id} ! </h3> ) } }); var NoMatch=React.createClass({ render:function(){ return (<h3>No Match !</h3>) } })
PS: 路由的生命周期处理 进入时装载组件离开时卸载组件,不会一下把所有的组件都装载进来
History
createHashHistory
(default) : 通过 hash 来实现,可以通过 window.location.hash 读取到 # 后的内容//has queryKey, eg: ?_k=ckuvup -> remove queryKey: ReactDOM.render(<Router routes={routes} history={createHashHistory({queryKey:false})}/>, document.getElementById("app"));
createMemoryHistory
(test)createBrowserHistory
(production): run on server,HTML5 才有的新 API,可以用来操作浏览器的 session history (会话历史)var createBrowserHistory =require("history/lib/createBrowserHistory"); ReactDOM.render(<Router routes={routes} history={createBrowserHistory()} />, document.getElementById("app"));
Run Based on nodeJS (cmd: node app):
const express = require('express') const path = require('path') const port = process.env.PORT || 3000 const app = express() // serve static assets normally app.use(express.static(__dirname + '/public')) // handle every other route with index.html, which will contain // a script tag to your application's JavaScript file(s). app.get('*', function (request, response){ response.sendFile(path.resolve(__dirname, 'public', 'index.html')) }) app.listen(port) console.log("server started on port " + port)
使用Isomorphic
routes.jsx
定义需要同构的React Routes:var Main=require("./src/main"); var Child=require("./src/child"); var routes={ path:'/', component:Main, childRoutes:[ {path:"/child/:id",component:Child}, {path:"*",component:NoMatch} ] }; module.exports=routes;
app.jsx
实现Client端渲染:var routes=require("./src/routes"); var createBrowserHistory =require("history/lib/createBrowserHistory"); ReactDOM.render(<Router routes={routes} history={createBrowserHistory()} />, document.getElementById("app"));
app.js
编译生成的Client端可执行的js文件> browserify app.jsx -t babelify --presets react -o app.js --extension .jsx
index.ejs
定义主页面,加载上面生成的Client的js文件app.js
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Hello React-Router </title> </head> <body> <div id="app"><%- content %></div> <script type="text/javascript" src="/libs/react/react.js"></script> <script type="text/javascript" src="/libs/react/react-dom.js"></script> <script type="text/javascript" src="/app.js"></script> </body> </html>
server.js
定义Server端响应渲染require('babel-core/register'); var React=require("react"); var ReactDOMServer=require("react-dom/server"); var Router=require('react-router'); var match=Router.match; var RoutingContext=Router.RoutingContext; var routes = require("./src/routes"); var express=require("express"); var app = express(); app.set('view engine', 'ejs'); app.use(express.static('public')); app.use(function(req, res, next) { console.log(req.url); match({routes,location:req.url},function(error, redirectLocation, renderProps){ if (error) { res.status(500).send(error.message) } else if (redirectLocation) { res.redirect(302, redirectLocation.pathname + redirectLocation.search) } else if (renderProps) { console.log("render react router"); var element=React.createFactory(RoutingContext)(renderProps); var elementStr=ReactDOMServer.renderToString(element); //res.status(200).send(elementStr); res.render("index",{content:elementStr}); } else { res.status(404).send('Not found') } }) }) app.listen(3000,function(err, result){ if (err) console.log(err); console.log('Listening at localhost:3000'); });
执行
node server
启动服务查看效果
Issues
1. Add <Routes baseHref>
import { createHistory, useBasename } from 'history'
const history = useBasename(createHistory)({
basename: '/my-custom-root'
})
<Router history={history}>
<Route path="/" component={App}></Route>
</Router>
<Link path="/some_path">some_path</Link>
2. Passing props to routes
React.render((
<Router>
<Route path="/" component={RootComp} myprop="foo">
<Route path="foo" component={FooComp}>
<Route path="bar" component={BarComp} />
</Route>
</Route>
</Router>
), document.getElementById('example'));
Option 1) With route attributes
class RootComp extends React.Component {
render: {
this.props.route.myprop // == "foo" (this.props.routes[0].myprop)
}
}
Option 2) To first level children with React.cloneElement()
class RootComp extends React.Component {
render: {
React.cloneElement(this.props.children, {myprop: this.route.myprop})
}
}
class FooComp extends React.Component {
render: {
this.props.myprop // == "foo"
}
}
class BarComp extends React.Component {
render: {
this.props.myprop // == undefined
}
}
Option 3) To all route components with Router.createElement
<Router createElement={createElement} />
function createElement(Component, props) {
// Add myprop to props for all route components
_.extend(props, {myprop: "foo"})
return <Component {...props}/>
}