官网https://github.com/reactjs/react-router-tutorial学习

01-setting-up

from https://github.com/reactjs/react-router-tutorial/tree/master/lessons/01-setting-up
Clone the Tutorial


git clone https://github.com/reactjs/react-router-tutorial
cd react-router-tutorial
cd lessons/01-setting-up
npm install
npm start
Now open up http://localhost:8080

运行环境Ok了,仔细读一下相关代码及关联,这一节无太多知识点,问题1,8080从哪里配置出来的

02-rendering-a-route

vi index.js

// 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'))
// 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>
  }
})

注意,运行后界面上出不来啥
Now visit http://localhost:8080/#/about and http://localhost:8080/#/repos

// 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>
    )
  }
})

运行时界面出来了,感觉modules的App.js是个总的模块,调用其他的模块,作view展示用,根目录下的index.js作来做好route关联,
到了06发现这个理解不对的 index.js调用各个modules,如有的modules里是展示内容的话,就按先后调用展示出来而已

04-nested-routes

// 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'))
// 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>
    )
  }
// ...

问题1: 为何一直首页下面直接显示repos.js中的内容?

A: 这是因为有个hashHistory在里面,最后一次点开的是repos,所以展示这个,再用F5刷都没用
## 问题2:如果重启一下node,啥都没有点的时候,出来的是啥?
A:如果上次最后一次点的是about, 重启Node,刷新第一次,显示的还是about的内容,
## 问题3:试着对着其他的文档,换成browserHistory【生产项目都用这个,其他的不用,为啥】,这个不出现_k的hash键值对,出不出有啥关系?
A: 换成browserHistory 切记留意10中提到的几处修改,一个是package.json,一个是index.html调用绝对路径

Active Links

One way that Link is different from a is that it knows if the path it links to is active so you can style it differently.

Active Styles

Let's see how it looks with inline styles, add activeStyle to your Links.

// modules/App.js
<li><Link to="/about" activeStyle={{ color: 'red' }}>About</Link></li>
<li><Link to="/repos" activeStyle={{ color: 'red' }}>Repos</Link></li>
Now as you navigate, the active link is red.

Active Class Name

You can also use an active class name instead of inline-styles.

// modules/App.js
<li><Link to="/about" activeClassName="active">About</Link></li>
<li><Link to="/repos" activeClassName="active">Repos</Link></li>
We don't have a stylesheet on the page yet though. Lets add one-extra point if you can add a link tag from memory.

// index.html
<link rel="stylesheet" href="index.css" />
And the CSS file:

.active {
  color: green;
}
You'll need to manually refresh the browser since Webpack isn't building our index.html.

Nav Link Wrappers

Most links in your site don't need to know they are active, usually just primary navigation links need to know. It's useful to wrap those so you don't have to remember what your activeClassName or activeStyle is everywhere.

We will use a spread operator here, the three dots. It clones our props and in this use case it clones activeClassName to our desired component for us to benefit from.

Create a new file at modules/NavLink.js that looks like this:

// modules/NavLink.js
import React from 'react'
import { Link } from 'react-router'

export default React.createClass({
  render() {
    return <Link {...this.props} activeClassName="active"/>
  }
})
Now you can go change your links to NavLinks.

// modules/App.js
import NavLink from './NavLink'

// ...

<li><NavLink to="/about">About</NavLink></li>
<li><NavLink to="/repos">Repos</NavLink></li>
Oh, how beautiful upon the renders is the composability of components.

问题1 NavLink.js 里面有一行

问题2 还是这行,这里的 active与css里的一样,通过这个方式让 react与css联动了?【当然 index.html里有调用】

06-params

Adding a Route with Parameters

Let's teach our app how to render screens at /repos/:userName/:repoName.

First we need a component to render at the route, make a new file at modules/Repo.js that looks something like this:

// modules/Repo.js
import React from 'react'

export default React.createClass({
  render() {
    return (
      <div>
        <h2>{this.props.params.repoName}</h2>
      </div>
    )
  }
})
Now open up index.js and add the new route.

// ...
// 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'))
Now we can add some links to this new route in Repos.js.

// 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>
    )
  }
})
Now go test your links out. Note that the parameter name in the route path becomes the property name in the component. Both repoName and userName are available on this.props.params of your component. You should probably add some prop types to help others and yourself out later.

页面中,点about只是出来about内容,点repos,就把repos里的调用全展示出来了。又是一个路由展示

07-more-nesting

More Nesting

Notice how the list of links to different repositories goes away when we navigate to a repository? What if we want the list to persist, just like the global navigation persists?

Try to figure that out before reading on.

...

First, nest the Repo route under the Repos route. Then go render this.props.children in 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>
Active Links

Let's bring in our NavLink from before so we can add the active class name to these links:

// modules/Repos.js
// import it
import NavLink from './NavLink'

// ...
<li><NavLink to="/repos/reactjs/react-router">React Router</NavLink></li>
<li><NavLink to="/repos/facebook/react">React</NavLink></li>
// ...
Notice how both the /repos link up top and the individual repo links are both active? When child routes are active, so are the parents.

我把大段代码和文字都放在代码段的原因,是我自己发现,有些不是代码,但是变量啥的,在网页上复制会变, 比如 npm install –save react 就会变成 -save 少这么一个横,比较奇怪

08-index-routes

When we visit / in this app it's just our navigation and a blank page. We'd like to render a Home component there. Lets create a Home component and then talk about how to render it at /.

// modules/Home.js
import React from 'react'

export default React.createClass({
  render() {
    return <div>Home</div>
  }
})
One option is to see if we have any children in App, and if not, render Home:

// modules/App.js
import Home from './Home'

// ...
<div>
  {/* ... */}
  {this.props.children || <Home/>}
</div>
//...
This would work fine, but its likely we'll want Home to be attached to a route like About and Repos in the future. A few reasons include:

Participating in a data fetching abstraction that relies on matched routes and their components.
Participating in onEnter hooks
Participating in code-splitting
Also, it just feels good to keep App decoupled from Home and let the route config decide what to render as the children. Remember, we want to build small apps inside small apps, not big ones!

Let's add a new route to index.js.

// index.js
// new imports:
// add `IndexRoute` to 'react-router' imports
import { Router, Route, hashHistory, IndexRoute } from 'react-router'
// and the Home component
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'))
Now open http://localhost:8080 and you'll see the new component is rendered.

Notice how the IndexRoute has no path. It becomes this.props.children of the parent when no other child of the parent matches, or in other words, when the parent's route matches exactly.

Index routes can twist people's brains up sometimes. Hopefully it will sink in with a bit more time. Just think about a web server that looks for index.html when you're at /. Same idea, React Router looks for an index route if a route's path matches exactly.
Have you noticed in our app that we don't have any navigation to get back to rendering the Home component?

Lets add a link to / and see what happens:

// in App.js
// ...
<li><NavLink to="/">Home</NavLink></li>
// ...
Now navigate around. Notice anything weird? The link to Home is always active! As we learned earlier, parent routes are active when child routes are active. Unfortunately, / is the parent of everything.

For this link, we want it to only be active when the index route is active. There are two ways to let the router know you're linking to the "index route" so it only adds the active class (or styles) when the index route is rendered.

IndexLink

First let's use the IndexLink instead of NavLink

// App.js
import { IndexLink } from 'react-router'

// ...
<li><IndexLink to="/" activeClassName="active">Home</IndexLink></li>
Fixed! Now this link is only "active" when we're at the index route. Go ahead and click around to see.

onlyActiveOnIndex Property

We can use Link as well by passing it the onlyActiveOnIndex prop (IndexLink just wraps Link with this property for convenience).

<li><Link to="/" activeClassName="active" onlyActiveOnIndex={true}>Home</Link></li>
That's fine, but we already abstracted away having to know what the activeClassName is with Nav.

Remember, in NavLink we're passing along all of our props to Link with the {...spread} syntax, so we can actually add the prop when we render a NavLink and it will make its way down to the Link:

<li><NavLink to="/" onlyActiveOnIndex={true}>Home</NavLink></li>

10-clean-urls

Clean URLs with Browser History

The URLs in our app right now are built on a hack: the hash. It's the default because it will always work, but there's a better way.

Modern browsers let JavaScript manipulate the URL without making an http request, so we don't need to rely on the hash (#) portion of the url to do routing, but there's a catch (we'll get to it later).

Configuring Browser History

Open up index.js and import browserHistory instead of hashHistory.

// index.js
// ...
// bring in `browserHistory` instead of `hashHistory`
import { Router, Route, browserHistory, IndexRoute } from 'react-router'

render((
  <Router history={browserHistory}>
    {/* ... */}
  </Router>
), document.getElementById('app'))
Now go click around and admire your clean URLs.

Oh yeah, the catch. Click on a link and then refresh your browser. What happens?

Cannot GET /repos
Configuring Your Server

Your server needs to deliver your app no matter what URL comes in, because your app, in the browser, is manipulating the URL. Our current server doesn't know how to handle the URL.

The Webpack Dev Server has an option to enable this. Open up package.json and add --history-api-fallback.

    "start": "webpack-dev-server --inline --content-base . --history-api-fallback"
We also need to change our relative paths to absolute paths in index.html since the URLs will be at deep paths and the app, if it starts at a deep path, won't be able to find the files.

<!-- index.html -->
<!-- index.css -> /index.css -->
<link rel="stylesheet" href="/index.css">

<!-- bundle.js -> /bundle.js -->
<script src="/bundle.js"></script>
Stop your server if it's running, then npm start again. Look at those clean URLs :)

11-productionish-server

None of this has anything to do with React Router, but since we're talking about web servers, we might as well take it one step closer to the real-world. We'll also need it for server rendering in the next section.

Webpack dev server is not a production server. Let's make a production server and a little environment-aware script to boot up the right server depending on the environment.

Let's install a couple modules:

npm install express if-env compression --save
First, we'll use the handy if-env in package.json. Update your scripts entry in package.json to look like this:

// 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"
},
In the root directly, go open up webpack.config.js and add the publicPath '/' as per below:

// webpack.config.js
  output: {
    path: 'public',
    filename: 'bundle.js',
    publicPath: '/'
  },
When you run npm start it checks if the value of our NODE_ENV environment variable is production. If yes, it runs npm run start:prod, if not, it runs npm run start:dev.

Now we're ready to create a production server with Express and add a new file at root dir. Here's a first attempt:

// server.js
var express = require('express')
var path = require('path')

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)
})
Now run:

NODE_ENV=production npm start
# For Windows users:
# SET "NODE_ENV=production" && npm start
Congratulations! You now have a production server for this app. After clicking around, try navigating to http://localhost:8080/package.json. Whoops. Let's fix that. We're going to shuffle around a couple files and update some paths scattered across the app.

make a public directory.
Move index.html and index.css into it.
Now let's update server.js to point to the right directory for static assets:

// server.js
// ...
// add path.join here
app.use(express.static(path.join(__dirname, 'public')))

// ...
app.get('*', function (req, res) {
  // and drop 'public' in the middle of here
  res.sendFile(path.join(__dirname, 'public', 'index.html'))
})
We also need to tell webpack to build to this new directory:

// webpack.config.js
// ...
output: {
  path: 'public',
  // ...
}
And finally (!) add it to the --content-base argument to npm run start:dev script:

"start:dev": "webpack-dev-server --inline --content-base public --history-api-fallback",
If we had the time in this tutorial, we could use the WebpackDevServer API in a JavaScript file instead of the CLI in an npm script and then turn this path into config shared across all of these files. But, we're already on a tangent, so that will have to wait for another time.

Okay, now that we aren't serving up the root of our project as public files, let's add some code minification to Webpack and gzipping to express.

// webpack.config.js

// make sure to import this
var webpack = require('webpack')

module.exports = {
  // ...

  // add this handful of plugins that optimize the build
  // when we're in production
  plugins: process.env.NODE_ENV === 'production' ? [
    new webpack.optimize.DedupePlugin(),
    new webpack.optimize.OccurrenceOrderPlugin(),
    new webpack.optimize.UglifyJsPlugin()
  ] : [],

  // ...
}
And compression in express:

// server.js
// ...
var compression = require('compression')

var app = express()
// must be first!
app.use(compression())
Now go start your server in production mode:

NODE_ENV=production npm start
You'll see some UglifyJS logging and then in the browser, you can see the assets are being served with gzip compression.

到这里,感觉做成模板,特指 package.json, webpack.conf,以后项目直接拿来用,好象还少hot这几个功能

12-navigating

13-server-rendering

14-whats-next

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值