ReactJS高阶之Router

使用示例

使用 react-router 实现前端路由:建立在history上,方便进行路由管理

  1. 下载

     > npm install react-router --save
     > npm install history --save
    
  2. 加载

     //app.js
     var React=require("react");
     var ReactDOM=require("react-dom");
    
     var ReactRouter=require("react-router");
     var Router=ReactRouter.Router;
     var Route=ReactRouter.Route;
    
  3. 使用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"));
      
  4. 定义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

  1. 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"));
    
  2. createMemoryHistory (test)

  3. 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

  1. 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;
    
  2. app.jsx 实现Client端渲染:

     var routes=require("./src/routes");
     var createBrowserHistory =require("history/lib/createBrowserHistory");
     ReactDOM.render(<Router routes={routes} history={createBrowserHistory()} />,
         document.getElementById("app"));
    
  3. app.js 编译生成的Client端可执行的js文件

     > browserify app.jsx -t babelify --presets react -o app.js --extension .jsx
    
  4. 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>
    
  5. 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');
     });
    
  6. 执行node server启动服务查看效果

Issues

1. Add <Routes baseHref>

Issue on Github

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

Issue on Github

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

Source

<Router createElement={createElement} />

function createElement(Component, props) {
   // Add myprop to props for all route components
    _.extend(props, {myprop: "foo"})
  return <Component {...props}/>
}

Reference