Jean-Xu

导航

 

概述

说起 前端路由,如果你用过前端 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

posted on 2017-01-17 16:20  Jean-Xu  阅读(9218)  评论(0编辑  收藏  举报