react技术栈教程(基础篇)
关注公众号
获取最新福利
微信公众号

react技术栈教程(基础篇)

时间: 02/27/2017作者: cllgeek浏览量: 1984

1. 导论

1.1 概述

本书全面介绍react相关技术栈,从最简单的开始讲起,循序渐进、由浅入深,力求清晰易懂。所有章节都带有大量的代码实例,便于理解和模仿,可以用到实际项目中,即学即用。 本书适合初学者学习react框架的入门到深入教程,也适合当作日常使用的参考手册。

1.2 自序

我想写这本书,主要原因是自己需要。

编程时,往往需要查阅资料,确定准确用法。如果涉及重要概念,还应该适当讲解。可是大多数时候,现实都不是如此。找到的资料冗长难懂,抓不住重点,且大多数是英文资料。

学习过程中,我做了很多笔记。日积月累,数量相当庞大。遇到问题,我首先查自己的笔记,如果笔记里没有,再到网上查,最后回过头把笔记补全。终于有一天,我意识到可以把笔记做成书,这就是这本书的由来。

我想用自己的语言叙述react,按照自己的方式编排章节,便于将来的查阅。当然,另一个写作动力是觉得这些内容对他人有用,毕竟我花了那么多时间,整理成书可以节省其他人的时间。

正因为脱胎于笔记,这本教程跟其他react技术栈教程有所不同。

1.3 学习建议

我想说的是,学习任何一门语言、技术都没有捷径可走。只有强于练习,结合理论实践,都说纸上得来终觉浅,绝知此事要躬行。学习技术就三点:

  • 读-就是从基础看起,通读源码
  • 问-遇到问题谷歌或者stackOverflow(至于为什么不用百度,你懂的...),还有就是沟通交流学习
  • 练-实践,个人认为是最重要的一环,想想自己学了那么多,不自己动手做做项目,怎么能有成就感呢?

2. reactjs简介

2.1 reactjs由来

React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript MVC 框架都不满意,就决定自己写一套,用来架设 Instagram 的网站。做出来以后,发现这套东西很好用,就在2013年5月开源了。由于 React 的设计思想极其独特,属于革命性创新,性能出众,代码逻辑却非常简单。所以,越来越多的人开始关注和使用,认为它可能是将来 Web 开发的主流工具。 ReactJS官网地址:http://facebook.github.io/react/ Github地址:https://github.com/facebook/react

2.2 对ReactJS的认识及ReactJS的优点

首先,对于React,有一些认识误区,这里先总结一下: React不是一个完整的MVC框架,最多可以认为是MVC中的V(View),甚至React并不非常认可MVC开发模式; React的服务器端Render能力只能算是一个锦上添花的功能,并不是其核心出发点,事实上React官方站点几乎没有提及其在服务器端的应用; 有人拿React和Web Component相提并论,但两者并不是完全的竞争关系,你完全可以用React去开发一个真正的Web Component; React不是一个新的模板语言,JSX只是一个表象,没有JSX的React也能工作。

2.2.1 ReactJS的背景和原理

在Web开发中,我们总需要将变化的数据实时反应到UI上,这时就需要对DOM进行操作。而复杂或频繁的DOM操作通常是性能瓶颈产生的原因(如何进行高性能的复杂DOM操作通常是衡量一个前端开发人员技能的重要指标)。React为此引入了虚拟DOM(Virtual DOM)的机制:在浏览器端用Javascript实现了一套DOM API。基于React进行开发时所有的DOM构造都是通过虚拟DOM进行,每当数据变化时,React都会重新构建整个DOM树,然后React将当前整个DOM树和上一次的DOM树进行对比,得到DOM结构的区别,然后仅仅将需要变化的部分进行实际的浏览器DOM更新。而且React能够批处理虚拟DOM的刷新,在一个事件循环(Event Loop)内的两次数据变化会被合并,例如你连续地先将节点内容从A变成B,然后又从B变成A,React会认为UI不发生任何变化,而如果通过手动控制,这种逻辑通常是极其复杂的。尽管每一次都需要构造完整的虚拟DOM树,但是因为虚拟DOM是内存数据,性能是极高的,而对实际DOM进行操作的仅仅是Diff部分,因而能达到提高性能的目的。这样,在保证性能的同时,开发者将不再需要关注某个数据的变化如何更新到一个或多个具体的DOM元素,而只需要关心在任意一个数据状态下,整个界面是如何Render的。 如果你像在90年代那样写过服务器端Render的纯Web页面那么应该知道,服务器端所要做的就是根据数据Render出HTML送到浏览器端。如果这时因为用户的一个点击需要改变某个状态文字,那么也是通过刷新整个页面来完成的。服务器端并不需要知道是哪一小段HTML发生了变化,而只需要根据数据刷新整个页面。换句话说,任何UI的变化都是通过整体刷新来完成的。而React将这种开发模式以高性能的方式带到了前端,每做一点界面的更新,你都可以认为刷新了整个页面。至于如何进行局部更新以保证性能,则是React框架要完成的事情。

2.2.2 组件化

虚拟DOM(virtual-dom)不仅带来了简单的UI开发逻辑,同时也带来了组件化开发的思想,所谓组件,即封装起来的具有独立功能的UI部件。React推荐以组件的方式去重新思考UI构成,将UI上每一个功能相对独立的模块定义成组件,然后将小的组件通过组合或者嵌套的方式构成大的组件,最终完成整体UI的构建。例如,Facebook的instagram.com整站都采用了React来开发,整个页面就是一个大的组件,其中包含了嵌套的大量其它组件,大家有兴趣可以看下它背后的代码。 如果说MVC的思想让你做到视图-数据-控制器的分离,那么组件化的思考方式则是带来了UI功能模块之间的分离。我们通过一个典型的Blog评论界面来看MVC和组件化开发思路的区别。 对于MVC开发模式来说,开发者将三者定义成不同的类,实现了表现,数据,控制的分离。开发者更多的是从技术的角度来对UI进行拆分,实现松耦合。 对于React而言,则完全是一个新的思路,开发者从功能的角度出发,将UI分成不同的组件,每个组件都独立封装。 在React中,你按照界面模块自然划分的方式来组织和编写你的代码,对于评论界面而言,整个UI是一个通过小组件构成的大组件,每个组件只关心自己部分的逻辑,彼此独立。 1437447742946406.jpg React认为一个组件应该具有如下特征: (1)可组合(Composeable):一个组件易于和其它组件一起使用,或者嵌套在另一个组件内部。如果一个组件内部创建了另一个组件,那么说父组件拥有(own)它创建的子组件,通过这个特性,一个复杂的UI可以拆分成多个简单的UI组件; (2)可重用(Reusable):每个组件都是具有独立功能的,它可以被使用在多个UI场景; (3)可维护(Maintainable):每个小的组件仅仅包含自身的逻辑,更容易被理解和维护;

3. reactjs基础

3.1 react安装

React 可以直接下载使用,下载包中也提供了很多学习的实例。 本教程使用了 React 的版本为 15.3.0,你可以在官网 http://facebook.github.io/react/ 下载最新版。 你也可以直接使用bootcdn的 React CDN 库,地址如下:

<script src="//cdn.bootcss.com/react/15.3.0/react.min.js"></script>
<script src="//cdn.bootcss.com/react/15.3.0/react-dom.min.js"></script>
<script src="https://cdn.bootcss.com/babel-core/5.5.0/browser.min.js"></script>

使用实例 以下实例输出了Hello,world!

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Hello React!</title>
    <script src="https://cdn.bootcss.com/react/15.3.0/react.min.js"></script>
    <script src="https://cdn.bootcss.com/react/15.3.0/react-dom.min.js"></script>
    <script src="https://cdn.bootcss.com/babel-core/5.5.0/browser.min.js"></script>
    <div id="example"></div>
    <script type="text/babel">
      ReactDOM.render(
        <h1>Hello, world!</h1>,
        document.getElementById('example')
      );
    </script>
  </body>
</html>

实例解析: 实例中我们引入了三个库: react.min.js 、react-dom.min.js 和 browser.min.js:

  • react.min.js - React 的核心库
  • react-dom.min.js - 提供与 DOM 相关的功能
  • browser.min.js - 用于将 JSX 语法转为 JavaScript 语法
    ReactDOM.render( <h1>Hello, world!</h1>, document.getElementById('example'));
    
    以上代码将一个h1标题,插入id="example"节点中。 注意: 如果我们需要使用 JSX,则 <script> 标签的 type 属性需要设置为 text/babel。 通过 npm 使用 React 我们建议在 React 中使用 CommonJS 模块系统,比如 browserify 或 webpack,本教程使用 webpack。
  • 第一步、安装全局包
    $ npm install babel -g
    $ npm install webpack -g
    $ npm install webpack-dev-server --save-dev # 或者 npm install webpack -g
    
  • 第二步、创建根目录 创建一个根目录,目录名为:reactApp,再使用 npm init 初始化,生成 package.json 文件:
    $ mkdir reactApp
    $ cd reactApp/
    $ npm init
    name: (reactApp) geekjc-react-test
    version: (1.0.0) description: geekjc react 测试
    entry point: (index.js) 
    test command: 
    git repository: 
    keywords: 
    author: 
    license: (ISC) 
    About to write to /Users/tianqixin/www/reactApp/package.json:
    { 
    "name": "geekjc-react-test", 
    "version": "1.0.0", 
    "description": "geekjc react 测试", 
    "main": "index.js",
    "scripts": { 
    "test": "echo \"Error: no test specified\" && exit 1" 
    }, 
    "author": "", 
    "license": "ISC"
    }
    Is this ok? (yes)
    
  • 第三步、添加依赖包及插件 因为我们要使用 React, 所以我们需要先安装它,--save 命令用于将包添加至 package.json 文件。
    $ npm install react --save
    $ npm install react-dom --save
    
    同时我们也要安装一些 babel 插件
    $ npm install babel-core
    $ npm install babel-loader
    $ npm install babel-preset-react
    $ npm install babel-preset-es2015
    
  • 第四步、创建文件 接下来我们创建一些必要文件: ``` $ touch index.html $ touch App.js $ touch main.js $ touch webpack.config.js
  • 第五步、设置编译器,服务器,载入器 打开 webpack.config.js 文件添加以下代码:
    var config = { 
    entry: './main.js', 
    output: {
      path:'./', 
      filename: 'index.js', 
    }, 
    devServer: { 
      inline: true, 
      port: 7777 
    }, 
    module: { 
      loaders: [ { 
           test: /\.jsx?$/, 
           exclude: /node_modules/, 
           loader: 'babel', 
           query: {
               presets: ['es2015', 'react']
           } 
       }] 
    } 
    }
    module.exports = config;
    
  • entry: 指定打包的入口文件 main.js
  • output:配置打包结果,path定义了输出的文件夹,filename则定义了打包结果文件的名称。
  • devServer:设置服务器端口号为 7777,端口后你可以自己设定 。
  • module:定义了对模块的处理逻辑,这里可以用loaders定义了一系列的加载器,以及一些正则。当需要加载的文件匹配test的正则时,就会调用后面的loader对文件进行处理,这正是webpack强大的原因。 现在打开 package.json 文件,找到 "scripts" 中的 "test" "echo \"Error: no test specified\" && exit 1" 使用以下代码替换:

    "start": "webpack-dev-server --hot"
    

    替换后的 package.json 文件 内容如下:

    $ cat package.json 
    {
    "name": "geekjc-react-test",
    "version": "1.0.0",
    "description": "geekjc react 测试",
    "main": "index.js",
    "scripts": {
    "start": "webpack-dev-server --hot"
    },
    "author": "",
    "license": "ISC",
    "dependencies": {
      "react": "^15.3.0",
      "react-dom": "^15.3.0"
    }
    }
    

    现在我们可以使用 npm start 命令来启动服务。--hot 命令会在文件变化后重新载入,这样我们就不需要在代码修改后重新刷新浏览器就能看到变化。

  • 第六步、index.html 设置 div id = "app" 为我们应用的根元素,并引入 index.js 脚本文件。

    <!DOCTYPE html>
    <html>
    
    <head>
       <meta charset = "UTF-8">
       <title>React App - geekjc(geekjc.com)</title>
    </head>
    
    <body>
       <div id = "app"></div>
       <script src = "index.js"></script>
    </body>
    </html>
    
  • 第七步、App.js 和 main.js 这是第一个 react 组件。后面的章节我们会详细介绍 React 组件。这个组件将输出 Hello World!!!。 App.js 文件代码
    import React from 'react';
    class App extends React.Component {
    render() {
       return (
          <div>
             Hello World!!!<br />
             欢迎来到geekjc学习!!!
          </div>
       );
    }
    }
    export default App;
    
    我们需要引入组件并将其渲染到根元素 App 上,这样我们才可以在浏览器上看到它。 main.js 文件代码
    import React from 'react';
    import ReactDOM from 'react-dom';
    import App from './App.js';
    ReactDOM.render(<App />, document.getElementById('app'))
    
    注意: 如果想要组件可以在任何的应用中使用,需要在创建后使用 export 将其导出,在使用组件的文件使用import 将其导入。
  • 第八步、运行服务 完成以上配置后,我们即可运行该服务:
    $ npm start
    
    通过浏览器访问 http://localhost:8888/,输出结果如下 : react安装demo

完整实例下载 以上测试实例各文件代码下载地址:http://static.geekjc.com/download/reactApp.zip注意:这个demo可作为后面练习的scaffold

3.2 react jsx

React JSX React 使用 JSX 来替代常规的 JavaScript。 JSX 是一个看起来很像 XML 的 JavaScript 语法扩展。 我们不需要一定使用 JSX,但它有以下优点:

  • JSX 执行更快,因为它在编译为 JavaScript 代码后进行了优化。
  • 它是类型安全的,在编译过程中就能发现错误。
  • 使用 JSX 编写模板更加简单快速。

使用 JSX 可以在上一章的demo中练习jsx语法 JSX 看起来类似 HTML ,我们可以看下实例:

ReactDOM.render( 
           <h1>Hello, world!</h1>, 
           document.getElementById('example')
);

我们可以在以上代码中嵌套多个 HTML 标签,需要使用一个 div 元素包裹它,实例中的 p 元素添加了自定义属性 data-myattribute,添加自定义属性需要使用 data- 前缀。

ReactDOM.render(
  <div>
  <h1>geekjc</h1>
  <h2>欢迎学习 React</h2>
    <p data-myattribute = "somevalue">这是一个很不错的 JavaScript 库!</p>
    </div>
  ,
  document.getElementById('example')
);

独立文件 你的 React JSX 代码可以放在一个独立文件上,例如我们创建一个 helloworld_react.js 文件,代码如下:

ReactDOM.render( 
  <h1>Hello, world!</h1>, 
  document.getElementById('example')
);

然后在 HTML 文件中引入该 JS 文件:

<body> 
    <div id="example"></div>
<script type="text/babel" src="helloworld_react.js"></script>
</body>

JavaScript 表达式 我们可以在 JSX 中使用 JavaScript 表达式。表达式写在花括号 {} 中。实例如下:

ReactDOM.render( 
        <div> <h1>{1+1}</h1>
        </div> , document.getElementById('example')
);

在 JSX 中不能使用 if else 语句,但可以使用 conditional (三元运算) 表达式来替代。以下实例中如果变量 i 等于 1 浏览器将输出 true, 如果修改 i 的值,则会输出 false.

ReactDOM.render( 
    <div> 
    <h1>{i == 1 ? 'True!' : 'False'}</h1> 
   </div> 
   ,
   document.getElementById('example')
);

样式 React 推荐使用内联样式。我们可以使用 camelCase 语法来设置内联样式. React 会在指定元素数字后自动添加 px 。以下实例演示了为 h1 元素添加 myStyle 内联样式:

var myStyle = { 
    fontSize: 100, 
    color: '#FF0000'
};
ReactDOM.render( 
    <h1 style = {myStyle}>geekjc</h1>, 
    document.getElementById('example')
);

注释 注释需要写在花括号中,实例如下:

ReactDOM.render( 
   <div> 
     <h1>geekjc</h1> 
     {/*注释...*/} 
  </div>, 
  document.getElementById('example')
);

数组 JSX 允许在模板中插入数组,数组会自动展开所有成员:

var arr = [ 
   <h1>geekjc</h1>, 
   <h2>紧跟时代的步伐</h2>,
];
ReactDOM.render( 
   <div>{arr}</div>, 
   document.getElementById('example')
);

HTML 标签 vs. React 组件 React 可以渲染 HTML 标签 (strings) 或 React 组件 (classes)。 要渲染 HTML 标签,只需在 JSX 里使用小写字母的标签名。

var myDivElement = <div className="foo" />;
ReactDOM.render(myDivElement, document.getElementById('example'));

要渲染 React 组件,只需创建一个大写字母开头的本地变量。

var MyComponent = React.createClass({/*...*/});
var myElement = <MyComponent someProperty={true} />;
ReactDOM.render(myElement, document.getElementById('example'));

React 的 JSX 使用大、小写的约定来区分本地组件的类和 HTML 标签。 注意:由于 JSX 就是 JavaScript,一些标识符像 class和 for不建议作为 XML 属性名。作为替代,React DOM 使用 className 和 htmlFor来做对应的属性。

3.3 react组件

React 组件 本章节我们将讨论如何使用组件使得我们的应用更容易来管理。 接下来我们封装一个输出 "Hello World!" 的组件,组件名为 HelloMessage:

var HelloMessage = React.createClass({
  render: function() {
    return <h1>Hello World!</h1>;
  }
});
ReactDOM.render(
  <HelloMessage />,
  document.getElementById('example')
);

实例解析: React.createClass 方法用于生成一个组件类 HelloMessage 实例组件类并输出信息。 注意,原生 HTML 元素名以小写字母开头,而自定义的 React 类名以大写字母开头,比如 HelloMessage 不能写成 helloMessage。除此之外还需要注意组件类只能包含一个顶层标签,否则也会报错。

如果我们需要向组件传递参数,可以使用 this.props 对象,实例如下:

var HelloMessage = React.createClass({
  render: function() {
    return <h1>Hello {this.props.name}</h1>;
  }
});
ReactDOM.render(
  <HelloMessage name="geekjc" />,
  document.getElementById('example')
);

以上实例中 name 属性通过 this.props.name 来获取。 注意,在添加属性时, class 属性需要写成 className ,for 属性需要写成 htmlFor ,这是因为 class 和 for 是 JavaScript 的保留字。

复合组件 我们可以通过创建多个组件来合成一个组件,即把组件的不同功能点进行分离。 以下实例我们实现了输出网站名字和网址的组件:

var WebSite = React.createClass({
  render: function() {
    return (
      <div>
        <Name name={this.props.name} />
        <Link site={this.props.site} />
      </div>
    );
  }
});
var Name = React.createClass({
  render: function() {
    return (
      <h1>{this.props.name}</h1>
    );
  }
});
var Link = React.createClass({
  render: function() {
    return (
      <a href={this.props.site}>
        {this.props.site}
      </a>
    );
  }
});
React.render(
  <WebSite name="geekjc" site=" http://www.geekjc.com" />,
  document.getElementById('example')
);

实例中 WebSite 组件使用了 Name 和 Link 组件来输出对应的信息,也就是说 WebSite 拥有 Name 和 Link 的实例。

3.4 react state(状态)

React State(状态) React 把组件看成是一个状态机(State Machines)。通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致。 React 里,只需更新组件的 state,然后根据新的 state 重新渲染用户界面(不要操作 DOM)。 以下实例中创建了 LikeButton 组件,getInitialState 方法用于定义初始状态,也就是一个对象,这个对象可以通过 this.state 属性读取。当用户点击组件,导致状态变化,this.setState 方法就修改状态值,每次修改以后,自动调用 this.render 方法,再次渲染组件。

var LikeButton = React.createClass({
  getInitialState: function() {
    return {liked: false};
  },
  handleClick: function(event) {
    this.setState({liked: !this.state.liked});
  },
  render: function() {
    var text = this.state.liked ? '喜欢' : '不喜欢';
    return (
      <p onClick={this.handleClick}><b>{text}</b>我。点我切换状态。
      </p>
    );
  }
});
React.render(
  <LikeButton />,
  document.getElementById('example')
);

3.5 react props

React Props state 和 props 主要的区别在于 props 是不可变的,而 state 可以根据与用户交互来改变。这就是为什么有些容器组件需要定义 state 来更新和修改数据。 而子组件只能通过 props 来传递数据。 使用 Props 以下实例演示了如何在组件中使用 props:

var HelloMessage = React.createClass({
  render: function() {
    return <h1>Hello {this.props.name}</h1>;
  }
});

ReactDOM.render(
  <HelloMessage name="geekjc" />,
  document.getElementById('example')
);

实例中 name 属性通过 this.props.name 来获取。 默认 Props 你可以通过 getDefaultProps() 方法为 props 设置默认值,实例如下:

var HelloMessage = React.createClass({
  getDefaultProps: function() {
    return {
      name: 'geekjc'
    };
  },
  render: function() {
    return <h1>Hello {this.props.name}</h1>;
  }
});
ReactDOM.render(
  <HelloMessage />,
  document.getElementById('example')
);

State 和 Props 以下实例演示了如何在应用中组合使用 state 和 props 。我们可以在父组件中设置 state, 并通过在子组件上使用 props 将其传递到子组件上。在 render 函数中, 我们设置 name 和 site 来获取父组件传递过来的数据。

var WebSite = React.createClass({
  getInitialState: function() {
    return {
      name: "geekjc",
      site: "http://www.geekjc.com"
    };
  },

  render: function() {
    return (
      <div>
        <Name name={this.state.name} />
        <Link site={this.state.site} />
      </div>
    );
  }
});
var Name = React.createClass({
  render: function() {
    return (
      <h1>{this.props.name}</h1>
    );
  }
});
var Link = React.createClass({
  render: function() {
    return (
      <a href={this.props.site}>
        {this.props.site}
      </a>
    );
  }
});
React.render(
  <WebSite />,
  document.getElementById('example')
);

Props 验证 Props 验证使用 propTypes,它可以保证我们的应用组件被正确使用,React.PropTypes 提供很多验证器 (validator) 来验证传入数据是否有效。当向 props 传入无效数据时,JavaScript 控制台会抛出警告。 以下实例创建一个 Mytitle 组件,属性 title 是必须的且是字符串,如果是一个数字则会报错 :

var title = "geekjc";
// var title = 123;
var MyTitle = React.createClass({
  propTypes: {
    title: React.PropTypes.string.isRequired,
  },
  render: function() {
     return <h1> {this.props.title} </h1>;
   }
});
ReactDOM.render(
    <MyTitle title={title} />,
    document.getElementById('example')
);

如果 title 使用数字变量,控制台会出现以下错误信息: react props验证 更多验证器说明如下:

React.createClass({
  propTypes: {
    // 可以声明 prop 为指定的 JS 基本数据类型,默认情况,这些数据是可选的
   optionalArray: React.PropTypes.array,
    optionalBool: React.PropTypes.bool,
    optionalFunc: React.PropTypes.func,
    optionalNumber: React.PropTypes.number,
    optionalObject: React.PropTypes.object,
    optionalString: React.PropTypes.string,

    // 可以被渲染的对象 numbers, strings, elements 或 array
    optionalNode: React.PropTypes.node,

    //  React 元素
    optionalElement: React.PropTypes.element,

    // 用 JS 的 instanceof 操作符声明 prop 为类的实例。
    optionalMessage: React.PropTypes.instanceOf(Message),

    // 用 enum 来限制 prop 只接受指定的值。
    optionalEnum: React.PropTypes.oneOf(['News', 'Photos']),

    // 可以是多个对象类型中的一个
    optionalUnion: React.PropTypes.oneOfType([
      React.PropTypes.string,
      React.PropTypes.number,
      React.PropTypes.instanceOf(Message)
    ]),

    // 指定类型组成的数组
    optionalArrayOf: React.PropTypes.arrayOf(React.PropTypes.number),

    // 指定类型的属性构成的对象
    optionalObjectOf: React.PropTypes.objectOf(React.PropTypes.number),

    // 特定 shape 参数的对象
    optionalObjectWithShape: React.PropTypes.shape({
      color: React.PropTypes.string,
      fontSize: React.PropTypes.number
    }),

    // 任意类型加上 `isRequired` 来使 prop 不可空。
    requiredFunc: React.PropTypes.func.isRequired,

    // 不可空的任意类型
    requiredAny: React.PropTypes.any.isRequired,

    // 自定义验证器。如果验证失败需要返回一个 Error 对象。不要直接使用 `console.warn` 或抛异常,因为这样 `oneOfType` 会失效。
    customProp: function(props, propName, componentName) {
      if (!/matchme/.test(props[propName])) {
        return new Error('Validation failed!');
      }
    }
  },
  /* ... */
});

3.6 react组件API

React 组件 API 在本章节中我们将讨论 React 组件 API。我们将讲解以下7个方法:

  • 设置状态:setState
  • 替换状态:replaceState
  • 设置属性setProps
  • 替换属性replaceProps
  • 强制更新:forceUpdate
  • 获取DOM节点:findDOMNode
  • 判断组件挂载状态:isMounted

设置状态:setState

setState(object nextState[, function callback])

参数说明

  • nextState,将要设置的新状态,该状态会和当前的state合并
  • callback,可选参数,回调函数。该函数会在setState设置成功,且组件重新渲染后调用。

合并nextState和当前state,并重新渲染组件。setState是React事件处理函数中和请求回调函数中触发UI更新的主要方法。 关于setState 不能在组件内部通过this.state修改状态,因为该状态会在调用setState()后被替换。 setState()并不会立即改变this.state,而是创建一个即将处理的state。setState()并不一定是同步的,为了提升性能React会批量执行state和DOM渲染。 setState()总是会触发一次组件重绘,除非在shouldComponentUpdate()中实现了一些条件渲染逻辑。 实例

var Counter = React.createClass({
  getInitialState: function () {
    return { clickCount: 0 };
  },
  handleClick: function () {
    this.setState(function(state) {
      return {clickCount: state.clickCount + 1};
    });
  },
  render: function () {
    return (<h2 onClick={this.handleClick}>点我!点击次数为: {this.state.clickCount}</h2>);
  }
});
ReactDOM.render(
  <Counter />,
  document.getElementById('message')
);

实例中通过点击 h2 标签来使得点击计数器加 1。 替换状态:replaceState

replaceState(object nextState[, function callback])
  • nextState,将要设置的新状态,该状态会替换当前的state
  • callback,可选参数,回调函数。该函数会在replaceState设置成功,且组件重新渲染后调用。

replaceState()方法与setState()类似,但是方法只会保留nextState中状态,原state不在nextState中的状态都会被删除。

设置属性:setProps

setProps(object nextProps[, function callback])
  • nextProps,将要设置的新属性,该状态会和当前的props合并
  • callback,可选参数,回调函数。该函数会在setProps设置成功,且组件重新渲染后调用。

设置组件属性,并重新渲染组件。 props相当于组件的数据流,它总是会从父组件向下传递至所有的子组件中。当和一个外部的JavaScript应用集成时,我们可能会需要向组件传递数据或通知React.render()组件需要重新渲染,可以使用setProps()。 更新组件,我可以在节点上再次调用React.render(),也可以通过setProps()方法改变组件属性,触发组件重新渲染。

替换属性:replaceProps

replaceProps(object nextProps[, function callback])
  • nextProps,将要设置的新属性,该属性会替换当前的props
  • callback,可选参数,回调函数。该函数会在replaceProps设置成功,且组件重新渲染后调用。

replaceProps()方法与setProps类似,但它会删除原有

props

强制更新:forceUpdate

forceUpdate([function callback])

参数说明

  • callback,可选参数,回调函数。该函数会在组件render()方法调用后调用。

forceUpdate()方法会使组件调用自身的render()方法重新渲染组件,组件的子组件也会调用自己的render()。但是,组件重新渲染时,依然会读取this.props和this.state,如果状态没有改变,那么React只会更新DOM。 forceUpdate()方法适用于this.props和this.state之外的组件重绘(如:修改了this.state后),通过该方法通知React需要调用render() 一般来说,应该尽量避免使用forceUpdate(),而仅从this.props和this.state中读取状态并由React触发render()调用。

获取DOM节点:findDOMNode

DOMElement findDOMNode()
  • 返回值:DOM元素DOMElement

如果组件已经挂载到DOM中,该方法返回对应的本地浏览器 DOM 元素。当render返回nullfalse时,this.findDOMNode()也会返回null。从DOM 中读取值的时候,该方法很有用,如:获取表单字段的值和做一些 DOM 操作。

判断组件挂载状态:isMounted

bool isMounted()
  • 返回值:truefalse,表示组件是否已挂载到DOM中

isMounted()方法用于判断组件是否已挂载到DOM中。可以使用该方法保证了setState()forceUpdate()在异步场景下的调用不会出错。 本文参考:http://itbilu.com/javascript/react/EkACBdqKe.html

3.7 react组件生命周期

React 组件生命周期 在本章节中我们将讨论 React 组件的生命周期。 组件的生命周期可分成三个状态:

  • Mounting:已插入真实 DOM
  • Updating:正在被重新渲染
  • Unmounting:已移出真实 DOM

生命周期的方法有:

  • componentWillMount 在渲染前调用,在客户端也在服务端。

  • componentDidMount : 在第一次渲染后调用,只在客户端。之后组件已经生成了对应的DOM结构,可以通过this.getDOMNode()来进行访问。 如果你想和其他JavaScript框架一起使用,可以在这个方法中调用setTimeout, setInterval或者发送AJAX请求等操作(防止异部操作阻塞UI)。

  • componentWillReceiveProps 在组件接收到一个新的prop时被调用。这个方法在初始化render时不会被调用。

  • shouldComponentUpdate 返回一个布尔值。在组件接收到新的props或者state时被调用。在初始化时或者使用forceUpdate时不被调用。 可以在你确认不需要更新组件时使用。

  • componentWillUpdate在组件接收到新的props或者state但还没有render时被调用。在初始化时不会被调用。

  • componentDidUpdate 在组件完成更新后立即调用。在初始化时不会被调用。

  • componentWillUnmount在组件从 DOM 中移除的时候立刻被调用。

这些方法的详细说明,可以参考官方文档。 以下实例在 Hello 组件加载以后,通过 componentDidMount 方法设置一个定时器,每隔100毫秒重新设置组件的透明度,并重新渲染:

var Hello = React.createClass({
  getInitialState: function () {
    return {
      opacity: 1.0
    };
  },
  componentDidMount: function () {
    this.timer = setInterval(function () {
      var opacity = this.state.opacity;
      opacity -= .05;
      if (opacity < 0.1) {
        opacity = 1.0;
      }
      this.setState({
        opacity: opacity
      });
    }.bind(this), 100);
  },
  render: function () {
    return (
      <div style={{opacity: this.state.opacity}}>
        Hello {this.props.name}
      </div>
    );
  }
});
ReactDOM.render(
  <Hello name="world"/>,
  document.body
);

以下实例初始化 statesetNewnumber 用于更新 state。所有生命周期在 Content 组件中。

var Button = React.createClass({
  getInitialState: function() {
    return {
      data:0
    };
  },
  setNewNumber: function() {
    this.setState({data: this.state.data + 1})
  },
  render: function () {
      return (
         <div>
            <button onClick = {this.setNewNumber}>INCREMENT</button>
            <Content myNumber = {this.state.data}></Content>
         </div>
      );
    }
})
var Content = React.createClass({
  componentWillMount:function() {
      console.log('Component WILL MOUNT!')
  },
  componentDidMount:function() {
       console.log('Component DID MOUNT!')
  },
  componentWillReceiveProps:function(newProps) {
        console.log('Component WILL RECEIVE PROPS!')
  },
  shouldComponentUpdate:function(newProps, newState) {
        return true;
  },
  componentWillUpdate:function(nextProps, nextState) {
        console.log('Component WILL UPDATE!');
  },
  componentDidUpdate:function(prevProps, prevState) {
        console.log('Component DID UPDATE!')
  },
  componentWillUnmount:function() {
         console.log('Component WILL UNMOUNT!')
  },

    render: function () {
      return (
        <div>
          <h3>{this.props.myNumber}</h3>
        </div>
      );
    }
});
ReactDOM.render(
   <div>
      <Button />
   </div>,
  document.getElementById('example')
);

3.8 react Ajax

React AJAX React 组件的数据可以通过 componentDidMount 方法中的 Ajax 来获取,当从服务端获取数据库可以将数据存储在 state 中,再用 this.setState 方法重新渲染 UI。 当使用异步加载数据时,在组件卸载前使用 componentWillUnmount 来取消未完成的请求。 以下实例演示了获取 Github 用户最新 gist 共享描述:

var UserGist = React.createClass({
  getInitialState: function() {
    return {
      username: '',
      lastGistUrl: ''
    };
  },
  componentDidMount: function() {
    this.serverRequest = $.get(this.props.source, function (result) {
      var lastGist = result[0];
      this.setState({
        username: lastGist.owner.login,
        lastGistUrl: lastGist.html_url
      });
    }.bind(this));
  },
  componentWillUnmount: function() {
    this.serverRequest.abort();
  },
  render: function() {
    return (
      <div>
        {this.state.username} 用户最新的 Gist 共享地址:
        <a href={this.state.lastGistUrl}>{this.state.lastGistUrl}</a>
      </div>
    );
  }
});
ReactDOM.render(
  <UserGist source="https://api.github.com/users/octocat/gists" />,
  mountNode
);

3.9 react表单与事件

React 表单与事件 本章节我们将讨论如何在 React 中使用表单。 一个简单的实例 在实例中我们设置了输入框 input 值value = {this.state.data}。在输入框值发生变化时我们可以更新 state。我们可以使用onChange 事件来监听 input 的变化,并修改 state。

var HelloMessage = React.createClass({
  getInitialState: function() {
    return {value: 'Hello geekjc!'};
  },
  handleChange: function(event) {
    this.setState({value: event.target.value});
  },
  render: function() {
    var value = this.state.value;
    return <div>
            <input type="text" value={value} onChange={this.handleChange} /> 
            <h4>{value}</h4>
           </div>;
  }
});
ReactDOM.render(
  <HelloMessage />,
  document.getElementById('example')
);

上面的代码将渲染出一个值为 Hello geekjc! 的 input 元素,并通过 onChange 事件响应更新用户输入的值。 实例 2 在以下实例中我么将为大家演示如何在子组件上使用表单。 onChange 方法将触发 state 的更新并将更新的值传递到子组件的输入框的 value 上来重新渲染界面。 你需要在父组件通过创建事件句柄 (handleChange) ,并作为 prop (updateStateProp) 传递到你的子组件上。

var Content = React.createClass({
  render: function() {
    return  <div>
            <input type="text" value={this.props.myDataProp} onChange={this.props.updateStateProp} /> 
            <h4>{this.props.myDataProp}</h4>
            </div>;
  }
});
var HelloMessage = React.createClass({
  getInitialState: function() {
    return {value: 'Hello geekjc!'};
  },
  handleChange: function(event) {
    this.setState({value: event.target.value});
  },
  render: function() {
    var value = this.state.value;
    return <div>
            <Content myDataProp = {value} 
              updateStateProp = {this.handleChange}></Content>
           </div>;
  }
});
ReactDOM.render(
  <HelloMessage />,
  document.getElementById('example')
);

React 事件 以下实例演示通过 onClick 事件来修改数据:

var HelloMessage = React.createClass({
  getInitialState: function() {
    return {value: 'Hello geekjc!'};
  },
  handleChange: function(event) {
    this.setState({value: 'geekjc教程'})
  },
  render: function() {
    var value = this.state.value;
    return <div>
            <button onClick={this.handleChange}>点我</button>
            <h4>{value}</h4>
           </div>;
  }
});
ReactDOM.render(
  <HelloMessage />,
  document.getElementById('example')
);

当你需要从子组件中更新父组件的 state 时,你需要在父组件通过创建事件句柄 (handleChange) ,并作为 prop (updateStateProp) 传递到你的子组件上。实例如下:

var Content = React.createClass({
  render: function() {
    return  <div>
              <button onClick = {this.props.updateStateProp}>点我</button>
              <h4>{this.props.myDataProp}</h4>
           </div>
  }
});
var HelloMessage = React.createClass({
  getInitialState: function() {
    return {value: 'Hello geekjc!'};
  },
  handleChange: function(event) {
    this.setState({value: 'geek教程'})
  },
  render: function() {
    var value = this.state.value;
    return <div>
            <Content myDataProp = {value} 
              updateStateProp = {this.handleChange}></Content>
           </div>;
  }
});
ReactDOM.render(
  <HelloMessage />,
  document.getElementById('example')
);

3.10 react Refs

React Refs React 支持一种非常特殊的属性 Ref ,你可以用来绑定到 render() 输出的任何组件上。 这个特殊的属性允许你引用 render() 返回的相应的支撑实例( backing instance )。这样就可以确保在任何时间总是拿到正确的实例。 使用方法 绑定一个 ref 属性到 render 的返回值上:

<input ref="myInput" />

在其它代码中,通过 this.refs 获取支撑实例:

var input = this.refs.myInput;
var inputValue = input.value;
var inputRect = input.getBoundingClientRect();

完整实例 你可以通过使用 this 来获取当前 React 组件,或使用 ref 来获取组件的引用,实例如下:

var MyComponent = React.createClass({
  handleClick: function() {
    // 使用原生的 DOM API 获取焦点
    this.refs.myInput.focus();
  },
  render: function() {
    //  当组件插入到 DOM 后,ref 属性添加一个组件的引用于到 this.refs
    return (
      <div>
        <input type="text" ref="myInput" />
        <input
          type="button"
          value="点我输入框获取焦点"
          onClick={this.handleClick}
        />
      </div>
    );
  }
});

ReactDOM.render(
  <MyComponent />,
  document.getElementById('example')
);

实例中,我们获取了输入框的支撑实例的引用,子点击按钮后输入框获取焦点。 我们也可以使用 getDOMNode()方法获取DOM元素。 新版本的React已经不推荐我们使用ref string转而使用ref callback,例子如下:

class App extends React.Component {
  render() {
    return (
      <div>
        <div ref={div => {
          this._div = div
        }}>app
        </div>
        <Child ref={child => this._child = child}/>
        <button onClick={()=> {
          console.log(this._div);
          console.log(this._child);
        }}>log refs
        </button>
      </div>
    )
  }
}
极客教程网
——一个你值得来的网站
编程笔记:学习分享,与君共勉!
轮子库:介绍各种库、插件的用途
在线工具:极客工具,在线工具,在线运行
前端导航:前端导航,前端资源聚合平台
分享
QQ群:
495489065

Copyright © 2017 - ~ All Rights Reserved. Made By Snow 备案号:粤ICP备15001588号-4