16、React系列之--React 路由

版权声明:本文为博主原创文章,未经博主允许不得转载。

PS:转载请注明出处
作者:TigerChain
地址:http://www.jianshu.com/p/b55cf53e633a
本文出自 TigerChain简书

React 教程系列

教程简介

  • 1、阅读对象

本篇教程适合初学者,老鸟直接略过,如果有误,欢迎指出,谢谢。

一、什么是路由 ?

路由是什么我们可能不太理解,但是我说一个东西我们一定知道,就是"路由器",路由器的功能用一句话概括就是:数据从一个网络到另一个网络就是靠路由来完成的[当然路由器的功能不仅仅于此]。

我们说的程序开发中的路由不是指路由器和网络协议中的路由,但是基本思想是一样的。而路由又可以分为前端路由和后端路由。

我们来看一个路由的简易图吧,有了这个图,大家对路由就有一个大致的了解了。

router.png

1、后端路由

举个栗子,分配一个站点,服务器地址是: http://192.168.1.200:8080
,在这个网站中提供了三个界面,分别是:

http://192.168.1.200:8080/index.html
http://192.168.1.200:8080/about/aboutus.html
http://192.168.1.200:8080/feedback.html

当我们在浏览器中敲入 http://192.168.1.200:8080/index.html 来访问页面时,web 服务器接收到这个请求,然后把 /index.html 解析出来,然后找到 index.html 并展示出来,这就是路由分发,路由的分发是通过路由来完成的。

2、前端路由

虽然前端路由和后端路由在实现技术上差别,但是原理都 TM 的一样。在 H5 的 history Api 之前,前端的路由功能都是使用通过 hash「散列值得」 来实现的。 hash 能兼容低版本的浏览器。比如:

http://192.168.1.200:8080/#/index.html
http://192.168.1.200:8080/#/about/aboutus.html
http://192.168.1.200:8080/#/feedback.html

由于 web 服务不会解析 # 后面的东西,可是 js 是可以拿到 # 后面的东西的,有一个方法就是 window.location.hash 来读取,通过这个方法来匹配不同的功能上。

PS 我们进一步来说,举个例子,有一个地址为:

http://www.xxx.com/path/a/b/c.html?key1=Tiger && key2=Chain && key3=fuck#/path/d/e.html
  • 1、我们来把这个地址解析一下:
http:协议
www.xxx.com:域名
/path/a/b/c.html:路径,即服务器上的资源
?key1=Tiger && key2=Chain && key3=fuck:这个很好理解 Get 请求的参数
#/path/d/e.html:hash 也叫散列值,也叫锚点

上面的 hash 是和浏览器交互的,其它的都是和服务器进行交互的。

  • 2、hash 的改变,不会导致浏览器的刷新,我们来验证一下。

先看我们要完成的效果:

customrouter.gif

我们看到了 hash 的改变不会导致浏览器的刷新,并且我们使用原生的 JS 定义了一个简单的 SPA 「单页应用」。

  • 3、源码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title> HashDemo </title>
</head>
<body>

<h3>我是 Hash 的一个 Demo</h3>
<hr />
<a href="#hash1">#hash1</a>
<a href="#hash2">#hash2</a>
<a href="#hash3">#hash3</a>
<a href="#hash4">#hash4</a>

<p/>
<div id = "show-hash-result" style="color:blue">
点击上面链接,并观察浏览器
</div>



<h4>自定义一个简单的单面路由页</h4>
<div id="nav">
<ul>
    <li><a href="#/index.html">首页</a></li>
    <li><a href="#/server">服务</a></li>
    <li><a href="#/mine">我的</a></li>
</ul>
</div>
<div id="result"></div>


<style>

#nav {
  margin: 0;
  border: 0;
    height: 40px;
    width: 300px;
    border-top: #060 2px solid;
    margin-top: 10px;
    border-bottom: #060 2px solid;
    background-color: #690;
}

#nav ul {
  margin: 0;
  border: 0;
  list-style: none;
  line-height: 40px;
}

#nav li {
    display: block;
    float: left;
}

#nav a {
    display: block;
    color: #fff;
    text-decoration: none;
    padding: 0 20px;
}

#nav a:hover {
    background-color: #060;
}
</style>


<script type="text/javascript">
window.addEventListener("hashchange", function(){
  //变化后输出当前地址栏中的值
  document.getElementById("show-hash-result").innerHTML = location.hash;

  console.log(window.location.hash) ;
});

</script>

<script type="text/javascript">
  //自定义一个路由规则
  function CustomRouter(){
    this.routes = {};
    this.curUrl = '';

    this.route = function(path, callback){
        this.routes[path] = callback || function(){};
    };

    this.refresh = function(){
        this.curUrl = location.hash.slice(1) || '/';
        if(this.curUrl.indexOf('/')!=-1){ //这里粗略的把 hash 过滤掉
            this.routes[this.curUrl]();
        }

    };

    this.init = function(){
        window.addEventListener('load', this.refresh.bind(this), false);
        window.addEventListener('hashchange', this.refresh.bind(this), false);
    }
  }

  //使用路由规则
  var R = new CustomRouter();
  R.init();
  var res = document.getElementById('result');

  R.route('/hash1',function () {
    document.getElementById("show-hash-result").innerHTML = location.hash;
  })

  R.route('/index.html', function() {
    res.style.height='150px';
    res.style.width='300px';
     res.style.background = 'green';
     res.innerHTML = '<html>我是首页</html>';

  });
  R.route('/server', function() {
    res.style.height='150px';
    res.style.width='300px';
     res.style.background = 'orange';
     res.innerHTML = '我是服务页面';
  });
  R.route('/mine', function() {
     res.style.background = 'red';
     res.style.height='150px';
     res.style.width='300px';
     res.innerHTML = '我的界面';
  });

</script>

</body>
</html>

代码没有整理和抽取,感兴趣的可以再深层次的去改 「改到你满意为止」。

  • h5 的 history

window 的 history 提供了对浏览器历史记录的访问功能,并且它暴露了一些方法和属性,让你在历史记录中自由的前进和后退,并且在 H5 中还可以操作历史记录中的数据。

history 的 API 如下:

interface History {
    readonly attribute long length;
    readonly attribute any state;
    void go(optional long delta);
    void back();
    void forward();
    //h5 引进以下两个方法
    void pushState(any data, DOMString title, optional DOMString? url = null);
    void replaceState(any data, DOMString title, optional DOMString? url = null);
    };
  • 1、back():在历史记录是后退
history.back() ;
  • 2、forward():在历史记录中前进
history.forward();
  • 3、go():移动到指定的历史记录点
history.go(-1)
  • 4、length:为 history 的属性,显示 history 的长度

关于 h5 的 history 推荐看这里,详细的讲解了 history,非常值得的看。

这里重点说 h5 的 pushState,完全代替 hash 并且更优雅。

废话不多说,直接上效果图

h5history.gif

以上我们实现了一个非常简单的 SPA ,我们可以看到我们使用 histoy pushState 和 onpopstate 实现和上面 hash 一样的功能。

源码

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title> H5 HistoryDemo </title>
</head>
<body>

  <ul>
    <li><a onclick="home()">首页</a></li>
    <li><a onclick="about()">关于</a></li>
    <li><a onclick="mine()">我的</a></li>
  </ul>

  <div id="showContent" style="height:250px;width:200px;background:green">home</div>

<script>

function home() {

history.pushState({name:'home',id:1}, null, "?page=home");

showCard("home");
}


function about() {
history.pushState({
  id:2,
  name:"about"
},null,"?page=about") ;

showCard("about");
}


function mine() {

history.pushState({
  id:3,
  name:"mine"
},null,"?name=chen&age=30") ;

showCard("mine");
}

function showCard(name) {
   document.getElementById("showContent").innerHTML = name;
}

//点击返回键盘的时候显示历史
window.onpopstate = function (event) {
  var content = "";
  if(event.state) {
    content = event.state.name;
  }
  showCard(content);
}

</script>
</body>
</html>

PS:基于 h5 history 实现的路由和最初的路由基本上一致。

http://192.168.1.200:8080/index
http://192.168.1.200:8080/about/aboutus
http://192.168.1.200:8080/feedback

从性能上来说,后端路由每次向 server 发起一个请求,server 要解析响应,就会有延时,而前端路由只是变换了一下路径,没有网络延时这一说,所以性能「用户体验」是大大的提升了。

经过以上我们对路由有了一个大体的理解,我们接下来说我们的重点 React 的路由功能。

二、React 路由

React 官方没有给出一个明确的组件,推荐使用三方的一个叫 React Router 的组件。「当然我们不使用 React Router 也可以完成路由功能,比如传统的 hash 功能,没有问题,但是用了它就非常方便和好用」。

我们都是知道 React 玩的就是组件化,所以 React Router 也是一个组件。

1、废话不多说,我们直接写一个简单的 SPA Demo 感受一下

效果如下:


reactrouterspa.gif

从图可以看到我们使用 React router 实现一个简单的 SPA 应用。如果大家细心看了前面的知识点,这个没有什么好说的。

2、下面我们撸码,通过代码来直观的感受一下 「这次我使用的是 windows 开发,不过不影响,你可以直接在 mac 开发」。

  • 1、老步骤,我们在指定的目录下创建一个叫 reactrouterdemo 的文件夹

比如:

D:\study\react-study\lesson03\reactrouter\reactrouterdemo
  • 2、在命令行中 win 中是 cmd ,mac 下是 ter

进入到 reactrouterdemo 目录中创建项目

yarn init // 一路回车即可
yarn add react react-dom webpack webpack-dev-server babel-core babel-loader babel-preset-es2015 babel-preset-react babel-preset-stage-0 --dev //安装依赖的插件,不清楚,可以看 webpack 这一节

  • 3、在 reactrouterdemo 中新建 app 和 public 文件夹和 .babelrc 和 webpack.config.js,在 public 下面新建 index.html,在 app 中新建 main.js 如下所示:
reactrouerfolder.png

然后每个文件中对应的内容

.babelrc

# .babelrc

{
  "presets": ["react", "es2015","stage-0"]
}

webpack.config.js

# webpack.config.js

module.exports = {
  entry:  __dirname + "/app/main.js",//已多次提及的唯一入口文件
  output: {
    path: __dirname + "/public",//打包后的文件存放的地方
    filename: "bundle.js"//打包后输出文件的文件名
  },
  devServer:{
  contentBase: "./public",//本地服务器所加载的页面所在的目录
//  colors: true,//终端中输出结果为彩色
  historyApiFallback: true,//不跳转
  inline: true//实时刷新
},
//新增加部分
 module:{
   loaders:[
   //babel配置
   {
     test:/\.js$/,
     exclude: /node_modules/,
     loader: 'babel-loader'
   }
 ]
 }
}

index.html

# index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>ReactRouter Demo</title>
</head>
<body>
  <div id="container"></div>
  <script src="bundle.js"></script>
</body>
</html>

这没有什么好说的,如果看过 webpack 等章节,我们就很快的创建这些东西。

接下来我们安装 react-router

yarn add react-router --dev
# main.js

import React from 'react' ;
import ReactDOM from 'react-dom' ;

import { Router, Route, Link ,hashHistory} from 'react-router';

/**
 * 定义 Home 组件
 * @type {String}
 */
class Home extends React.Component{
  render(){
    return(<div>
      Home
    </div>) ;
  }
}

/**
 * 定义 About 子页面组件
 * @type {String}
 */
class About extends React.Component{
  render(){
    return(
      <div>About</div>
    ) ;
  }
}

 class App extends React.Component {
  render() {
    return (
      <div>
        <h1>App</h1>
        <ul>
          <li><Link to="/home">首页</Link></li>
          <li><Link to="/about">关于我</Link></li>
        </ul>
      </div>
    );
  }
}

ReactDOM.render((
  <Router>
    <Route  path="/" component={App}/>
    <Route path="/home" component={Home} />
    <Route path="/about" component={About} />
  </Router>
), document.getElementById('container'));

最后配置 package.json 文件的脚本,添加下面一句。

package.json

 "scripts": {
    "start": "webpack-dev-server --progress --port 8888"
  }

好了,到此为止我们基本上写完一个简单的 SPA 页面了,让我们来验证一下吧,我们在命令行中输入 yarn start,一切 ok ,我们打开浏览器输入 localhost:8888,如果没有什么大问题,你毛也看不到,这个时候不要慌,打开 chrome 调试工具 「别给我说你没有使用 chorme 调试 web 页面,如果没有自己去面面壁」。结果如下

reacterror.png

What The Fuck ,什么鬼吗,按照文档 「这个翻译文档过时了」来的居然出错了,好吧,解决问题,把上面框出来的问题到 Google 上一搜,答案在这里,来吧上一张图

reactrouterv4pro.png

大体意思是说,如果你使用的 react-router v4 版本 「上一个稳定版本是 2.8.1」,那么就要使用 react-router-dom,并且这是 v4 版本中的更新 「 v4 中有很多更新」。啥是react-router-dom 呢,你只要知道一般我们前端就用这个包来进行WEB开发了,这一篇文章详细的介绍了,也就是使用 react-router-dom 完全可以替代 react-router 。

PS: 如果你的项目中已经在使用react-router之前的版本,那一定要慎重的更新,因为新的版本是一次非常大的改动,如果你要更新,工作量并不小。关于 React-router 2.8 左右的版本可以看阮一峰的:http://www.ruanyifeng.com/blog/2016/05/react_router.html?utm_source=tool.lu 文章,非常好。

  • 4、安装 react-router-dom 组件

上面说了,react-router-dom 可以迭代 react-router ,那么我们先把 react-router 卸载了,然后安装 react-router-dom

yarn remove react-router //卸载 react-router
yarn add react-router-dom --dev

然后修改 main.js,这里只修改一句话:

将 mport { Router, Route, Link } from 'react-router'; 修改为 

import {
  BrowserRouter as Router,
  Route,
  Link
} from 'react-router-dom';

然后看结果,还是出不来,看 chorme 的输出台,这下变了,不是上面的错误了,而是

routeronechild.png

大概意思就是 <Router> 标签只能有一个子元素,这好办,加一个 div 不就搞定了。

修改地方是 :

adddiv2router.png

然后再看结果,神奇般出来了我们开头的效果,真不容易呀。

总结:我们在开发 web 页面的时候就使用 react-router-dom 来代替以前的 react-router 就可以了。他们的用法稍微有不同:

  • react-router-dom 不能嵌套使用 「我没有试出来」。
  • react-router-dom <Router> 结点只能有一个子元素,类似于 React 的 render 方法。

以上我们就简单的开发一个 React-router 的 SPA ,我们对 React-router v4 也有了一个大概的解,下面我们具体说说 React-router
v4 的一些用法吧。

到此为止,我们对 react router 有一个大体的认识,下一节们再详细的说说 react-router v4 的一些用法。

三、react-router v4 介绍

1、react-router v4 没有嵌套之用法了

2、传递参数「url 传参」

经过上面的了解我们知道,React-router v4 中,我们在 Router 组件中可以写任意的标签,但是只能有一个根标签。

url 传参,我们使用通配符 :id ,下面我们结合代码一起看看。

  • 1、在上面代码的基础上我们修改 main.js
# main.js

import React from 'react' ;
import ReactDOM from 'react-dom' ;

import {
  BrowserRouter as Router,
  Route,
  HashRouter,
  Link
} from 'react-router-dom';

/**
 * 定义 Home 组件
 * @type {String}
 */
class Home extends React.Component{
  render(){
    return(<div>
      Home
    </div>) ;
  }
}

/**
 * 定义 About 子页面组件
 * @type {String}
 */
class About extends React.Component{
  render(){
    return(
      <div>About</div>
    ) ;
  }
}

 class App extends React.Component {
  render() {
    return (
      <div>
        <h1>App</h1>
        <ul>
          <li><Link to="/home">首页</Link></li>
          <li><Link to="/about">关于我</Link></li>
          <li><Link to="/haha">haha</Link></li>
        </ul>
      </div>
    );
  }
}

// 在 react-router v4.0 中获取 接收参数使用 this.props.match.params.属性名字
class Haha extends React.Component{
  render(){
    return(
      <div>
      <h3> ID: {this.props.match.params.id}</h3>

      match.url:  {this.props.match.url}

      </div>
    ) ;

  }
}

// 无状态的组件
// const Haha = ({ match }) => (
//   <div>
//     <h3>ID: {match.params.id}</h3>
//   </div>
// )


ReactDOM.render((
  <HashRouter>
    <div>
    <Route   path="/" component={App}/>
    <Route path="/home" component={Home} />
    <Route path="/about" component={About} />

    <Route path="/:id" component={Haha}/>
  </div>
  </HashRouter>
), document.getElementById('container'));


  • 2、直接运行看效果
react-router-url-param.gif

通过上图我们可以看到 :id 匹配的是 home,about,haha,我们再看 chorme 调试工具,很清楚的看到 li 标签中的 link 其实就是一个 a 标签,to 就相当于 href 属性。

细习的朋友可能发现了,我这里使用 HashRouter 代替了 Router,那么这个有什么作用呢,答案就是在上面的运行结果中,我们看到点击首页等链接的时候,地址栏中自动加了一个 # ,也就说 HashRouter 是前面的说的 hash 的方式实现路由。

如何取得 id 的参数呢,我们通过

his.props.match.params.id

来取得,这里的 id 就是你 :id 中的 id ,名字可以自己改。

四、React-router 实例

1、本实例我们基于 ant design 来仿一下 ant design「以下简称 antd」 的网站「体验一下 spa 应用中 react-router的应用,本次选用 react-router@2.8.1 来开发」

对 ant design 不了解的可以看看官方:https://ant.design/index-cn,它是一个 UI 设计语言类似于 bootstrap ,并且它和 react 无缝结合

首先我们来看一下 ant design 的官网界面如下:

ant_design.gif

再来看看,我们仿的界面:

fant_design.gif

怎么样,大体上还是很像的吧,当然我只是为了演示 react-router 在 spa 应用中的应用,仿的肯定和原生没有百分百一样「css 如果要做的好,就要花费大量的时间和精力,本人的 css 能力还是很薄弱的」

2、好我废话不多,我们直接开始撸代码吧「在 mac 环境下,win 下是一样的」

  • 1、在指定目录新建 antddemo 目录,并且使用 yarn 初始化项目
mkdir antddemo
cd antddemo
yarn init 

然后一路回车即可

  • 2、在 antddemo 中 分别新建 src 文件夹,.babel 文件 ,webpack.config.js 文件等,目录结构如下:
├── index.html
├── package.json
├── src
│   ├── imgs
│   ├── js
│   │   ├── component
│   │   └── index.js
│   └── styles
├── webpack.config.js
├── yarn-error.log
└── yarn.lock

PS: index.js 就我们的入口 js 文件,imgs 用来存放本地图片,styles 用来放 css 文件,component 用来存放编写的 react 组件的

  • 3、安装所需要依赖库
yarn add react react-dom //回车,继续安装
yarn add babel-core babel-loader babel-preset-es2015 babel-preset-react babel-preset-stage-0 webpack webpack-dev-server style-loader url-loader css-loader antd react-hot-loader react-router@2.8.1 --dev

然后回车没有什么问题即就显示安装成功,我这里一口气所需要的依赖都安装好了,你也可以分别来安装。

  • 4、配置 .babelrc
{
  "presets":["react","stage-0","es2015"],
  "plugins": [
    ["import", { "libraryName": "antd", "style": "css" }]
  ]
}

其中的 plugin 是配置 antd 的,具体可以看官网,这里大概说一下,根据前面的经验我们知道一个 css 样式是可以被 import 进来的,如果我们添加了 css-loader 和 style-loader 的话,如果 antd 想被 import 引入来使用的话,我们还需要安装一个插件 babel-plugin-import「也是官司方推荐的」,具体可以看官方的按需要加载这一小结:https://ant.design/docs/react/introduce-cn

下面我们安装 babel-plugin-import

yarn add babel-plugin-import --dev

ps:.babelrc 里面的 plugin 就是 配置 babel-plugin-import 这玩意的

  • 5、下面看一下 webpack.config.js 配置
# webpack.config.js

module.exports = {
  entry:  __dirname + "/src/js/index.js",
  output: {
    path: __dirname + "/src/",  //打包后文件地址
    publicPath:"/src/",   //命令行模式下,一定要配置output.publicPath来指定编译后的包(bundle)的访问位置.
  filename: "bundle.js"  //打包后文件
  },
  module: {
    loaders: [
      //babel配置
    {
      test:/\.js$/,
      exclude: /node_modules/,
      loader: 'babel-loader'
    },
  // 为了不和 antd css 样式冲突,我们
     {
    test: /\.css$/,
    loader: 'css-loader?sourceMap&modules&localIdentName=[local]___[hash:base64:5]!!',
    exclude: /node_modules/
     },
  // style-loader css-loader 配置
   {
      test: /\.css$/,
      loader: 'style-loader!css-loader'
   },
   //加载本地图片 配置 url-loader
    {test:/\.(png|jpg)$/,loader:"url-loader?limit=8192&name=img/[name][hash:8].[ext]"
    }

    ]
  },

  devServer:{
    hot:true,
    historyApiFallback: true,//不跳转
    inline: true//实时刷新
  }
}

PS:这里要注意一点,由我们安装了 css-loader 和 antd ,我们需要对 antd 单独配置一下,就是把 node_modules 下的文件都 exclude 掉,不要让它走 css-modules,否则 css-loader 会把 antd 的 css 样式全部冲掉「你引入的 antd 样式死活都加载不出来,切记、切记、切记」,具体可以看这里:https://github.com/ant-design/ant-design/issues/3442

  • 6、组件的划分

以上我们只是搭建了一个架子,那么既然 react 牛 b 的地方就是组件化,我们就把 antd 网页划分一下组件吧,由 antd 官网我们可以看到,上面底部和底部是不变的,我们可以分别抽出一个组件来,具体组件划分如下:

ant_component.png

由上图我们看到 antd 大体来说就分为这四个组件。并用只有内容区域是变化的,其它组件不变

  • 7、index.html
# index.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <link rel="stylesheet" href="src/styles/pc.css">
  <link rel="stylesheet" href="src/styles/main.css">
  <link rel="stylesheet" href="src/styles/common.css">
</head>
<body>
  <div id="container"></div>
  <script type="text/javascript" src="./src/bundle.js">

  </script>
</body>
</html>

和我们以前的 index.html 基本上没有什么区别,只不过我们引入几个 css 样式而已

  • 8、看主 js --> index.js
# index.js

import React,{Component} from 'react' ;
import ReactDOM from 'react-dom' ;

// 这里我们使用 react-router 2.8.1
import {hashHistory,Router,Route,IndexRoute,Link} from 'react-router' ;

//引入 pc 主界面组件
import PCIndex from './component/pc_index.js' ;

import AntdofReact from './component/antdofreact.js' ;
// 引入快速上手组件
import QuickStart from './component/quick_start.js' ;
//引入项目实践组件
import ProjectPractice from './component/projectpractice.js' ;
//引入在 react 中使用组件
import UseInReact from './component/useinreact.js' ;

//引入自定义主题组件
import CustomTheme from './component/customtheme.js' ;
//引入更新日志组件
import UpDateLog from './component/updatelog.js' ;


class Main extends React.Component {
  render(){
    return(
      // 定义路由
      <Router  history={hashHistory}>
        <Route path="/" component={PCIndex}>
          <IndexRoute  component={AntdofReact}></IndexRoute>
          <Route path="react/getting-started-cn" component={QuickStart}></Route>
          <Route path="react/practical-projects-cn" component={ProjectPractice}></Route>
          <Route path="react/use-with-create-react-app-cn" component={UseInReact}></Route>
          <Route path="react/customize-theme-cn" component={CustomTheme}></Route>
          <Route path="react/changelog-cn" component={UpDateLog}></Route>
        </Route>
      </Router>

    ) ;
  }
}

ReactDOM.render(
  <Main />,document.getElementById('container')
) ;

在这里我们路由规则都定定出来了。

  • 9、我们看看 pc_index.js 就像是一个盒子,组装头部、底部、和内容
import React, { Component, PropTypes } from 'react';

// 导入头部组件
import PCHeader from './pc_header.js' ;
//导入主体内容组件
import PCMAIN from './pc_main.js' ;
//导入底部组件
import PCFooter from './pc_footer.js' ;

//首页 包含公共的头部和底部
export default class PCIndex extends Component {
  constructor(props) {
    super(props);
  }

  render() {
    //取得 PCIndex中的子组件  下句就相当于 const children = this.props.children
    const {children} = this.props
    return (
      <div>
        <PCHeader />
        <PCMAIN maincontent={children}/>
        <PCFooter />
      </div>
    );
  }
}

PCIndex.propTypes = {
};

我们拿一个图来直观的看一下 pc_index.js 组件的组合作用

ant_component-2.png
  • 10、我们再来看看 pc_main.js 组件
import React, { Component, PropTypes } from 'react';

import PCMenu from './pc_menu.js' ;

// 内容组件
export default class PCMain extends Component {
  constructor(props) {
    super(props);
  }


  render() {
     const {maincontent} = this.props;
    return (
        <main className="main">
          {/* 菜单 */}
         <div className="menu">
            <PCMenu />
         </div>
          {/* 内容 */}
         <div className="content">
            {maincontent}
         </div>

        </main>
    );
  }
}

PCMain.propTypes = {
};

pc_main.js 组件也就是组合了菜单组件,和内容「也可以定义成一个组件,自己试试吧,React 中一切皆组件」

此时变成这样了,如下图所示:

ant_component-3.png

好了,以上就是核心代码,如果一个个 js 去说没有什么意义,浪费时间,可以自行下载 demo 体验一下

PS:后记,我们仿 antd 网站使用是的 React-router v2.8 来做的「让大家体验一下"曾今"(当然现在也很流行)流行的路由」,后面抽出时间,改成 React-router v4 版本。

demo地址:https://github.com/tigerchain/react-lesson/tree/master/lesson03/13-reactrouter

据说每个勤奋、努力、想成为牛 b 的人会点一个喜欢的

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容