react_笔记

React的基本认识

  • Facebook开源的一个js库
  • 一个用于构建用户界面的js库
  • React的核心思想: 通过构建可复用组件来构建用户界面
  • React的特点
    • Declarative(声明式编码)
    • Component-Based(组件化编码)
    • Learn Once, Write Anywhere(支持客户端与服务器渲染)
    • 高效
    • 单向数据流
  • React高效的原因
    • 虚拟(virtual)DOM, 不总是直接操作DOM
    • 高效的DOM Diff算法, 最小化页面重绘
  • ReactJS官网
  • Github地址

使用React

  • 相关js库
    • react.js: React的核心库
    • react-dom.js: 提供操作DOM的扩展库
    • babel.min.js: 解析JSX语法代码转为纯JS语法代码的库
  • 在页面中导入js

    1
    2
    3
    <script type="text/javascript" src="../js/react.js"></script>
    <script type="text/javascript" src="../js/react-dom.js"></script>
    <script type="text/javascript" src="../js/babel.min.js"></script>
  • 编码

    1
    2
    3
    4
    <div id="example"></div>
    <script type="text/babel"> //必须用babel
    ReactDOM.render(<h1>Hello, React!</h1>, document.getElementById('example'));
    </script>

认识JSX

  • 虚拟DOM

    1
    2
    // 这就是一个简单的虚拟DOM对象
    var element = React.createElement('h1', {id:'myTitle'}, 'hello');
    • React提供了一些API来创建一种 特别 的一般js对象
    • 虚拟DOM对象最终都会被React转换为真实的DOM
    • 我们编码时基本只需要操作react的虚拟DOM相关数据, react会转换为真实DOM变化而更新界面
  • JSX
    • 全称: JavaScript XML
    • react定义的一种类似于XML的JS扩展语法: XML+JS
    • 作用: 用来创建react虚拟DOM(元素)对象
      • 注意1: 它不是字符串, 也不是HTML/XML标签
      • 注意2: 它最终产生的就是一个JS对象
    • 标签名任意: HTML标签或其它标签
    • 标签属性任意: HTML标签属性或其它
    • 基本语法规则
      • 遇到 < 开头的代码, 以标签的语法解析: html同名标签转换为html同名元素, 其它标签需要特别解析
      • 遇到以 { 开头的代码,以JS的语法解析: 标签中的js代码必须用{}包含
    • babel.js的作用
      • 浏览器的js引擎是不能直接解析JSX语法代码的, 需要babel转译为纯JS的代码才能运行
      • 只要用了JSX,都要加上type=”text/babel”, 声明需要babel来处理
  • 渲染虚拟DOM(元素)
    • 语法: ReactDOM.render(virtualDOM, containerDOM) :
    • 作用: 将虚拟DOM元素渲染到真实容器DOM中显示
    • 参数说明
      • 参数一: 纯js或jsx创建的虚拟dom对象
      • 参数二: 用来包含虚拟DOM元素的真实dom元素对象(一般是一个div)
  • 创建虚拟DOM的2种方式:

    • 纯JS(一般不用):

      1
      React.createElement('h1', {id:'myTitle'}, title)
    • JSX:

      1
      <h1 id='myTitle'>{title}</h1>
  • 例:动态展示列表数据

    1
    2
    3
    4
    5
    6
    7
    const names = ['Tom2', 'Jack2', 'Bob2'];
    ReactDOM.render(
    <ul>
    { names.map((name, index) => <li key={index}>{name}</li>) }
    </ul>,
    document.getElementById('xxx')
    )
  • JSX语法注意:

    • 标签必须有结束
    • 标签的class属性必须改为className属性
    • 标签的style属性值必须为: 双大括号

Component基础

  • 自定义组件(Component) :

    • React的核心语法和概念
    • 作用类似于angular中自定义标签指令
    • 组件的2个基本概念

      • 组件类(工厂函数/构造函数/类): MyCompoent
      • 组件标签:
        -定义组件类
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        //方式1: 工厂(无状态)函数(最简洁, 推荐使用)
        function MyComponent1() {
        return <h1>自定义组件标题11111</h1>;
        }
        //方式2: ES6类语法(复杂组件, 推荐使用)
        class MyComponent3 extends React.Component {
        render () {
        return <h1>自定义组件标题33333</h1>;
        }
        }
        //方式3: ES5老语法(不推荐使用了)
        var MyComponent2 = React.createClass({
        render () {
        return <h1>自定义组件标题22222</h1>;
        }
        });
    • 渲染组件标签

      1
      ReactDOM.render(<MyComponent/>, document.getElementById('example'));
  • 注意:

    • 返回的组件类必须首字母大写
    • 虚拟DOM元素必须只有一个根元素
    • 虚拟DOM元素必须有结束标签
  • ReactDOM.render()渲染组件标签的基本流程
    • React内部会创建组件实例对象
    • 得到包含的虚拟DOM并解析为真实DOM
    • 插入到指定的页面元素内部

组件3大属性之一: props属性

  • 每个组件对象都会有props(properties的简写)属性
  • 组件标签的所有属性都保存在props中
  • 内部读取某个属性值: this.props.propertyName
  • 作用: 通过标签属性从组件外向组件内传递数据(只读)
  • 对props中的属性值进行类型限制和必要性限制

    1
    2
    3
    4
    Person.propTypes = {
    name: React.PropTypes.string.isRequired,
    age: React.PropTypes.number.isRequired
    }
  • 扩展属性: 将对象的所有属性通过props传递

    1
    <Person {...person}/>
  • 默认属性值

    1
    2
    3
    Person.defaultProps = {
    name: 'Mary'
    };
  • 组件类的构造函数

    1
    2
    3
    4
    constructor (props) {
    super(props);
    console.log(props); // 查看所有属性
    }

组件的3大属性之二: refs属性

  • refs属性

    • 组件内的标签都可以定义ref属性来标识自己
    • 在组件中可以通过this.refs.refName来得到对应的真实DOM对象
    • 作用: 用于操作指定的ref属性的dom元素对象(表单标签居多)
      1
      2
      <input ref='username'>
      this.refs.username //返回input对象
  • 事件处理

    • 通过onXxx属性指定组件的事件处理函数(注意大小写)
      • React使用的是自定义(合成)事件, 而不是使用的DOM事件
      • React中的事件是通过委托方式处理的(委托给组件最外层的元素)
    • 通过event.target得到发生事件的DOM元素对象
      1
      2
      3
      4
      <input onFocus={this.handleClick}/>
      handleFocus(event) {
      event.target //返回input对象
      }
  • 强烈注意

    • 组件内置的方法中的this为组件对象
    • 在组件中自定义的方法中的this为null
      • 强制绑定this
      • 箭头函数(ES6模块化编码时才能使用)

组件3大属性之: state属性

  • 组件被称为”状态机”, 通过更新组件的状态值来更新对应的页面显示(重新渲染)
  • 初始化状态:

    1
    2
    3
    4
    5
    6
    7
    constructor (props) {
    super(props);
    this.state = {
    stateProp1 : value1,
    stateProp2 : value2
    };
    }
  • 读取某个状态值

    1
    this.state.statePropertyName
  • 更新状态—->组件界面更新

    1
    2
    3
    4
    this.setState({
    stateProp1 : value1,
    stateProp2 : value2
    })

实现一个双向绑定的组件

  • React是单向数据流
  • 需要通过onChange监听手动实现
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    class App extends React.Component {
    constructor() {
    super()
    this.state = {
    value: ''
    }
    }
    handleChange(e) {
    const value = e.target.value;
    this.setState({ value })
    }
    render() {
    const { value } = this.state.value;
    return (
    <div>
    <input type="text" value={value} onChange={this.handleChange.bind(this)}/>
    <p>{this.state.value}</p>
    </div>
    )
    }
    }
    ReactDOM.render(<App/>, document.getElementById('example'));

组件的生命周期

  • 组件的三个生命周期状态:
    • Mount:插入真实 DOM
    • Update:被重新渲染
    • Unmount:被移出真实 DOM
  • React 为每个状态都提供了两种勾子(hook)函数,will 函数在进入状态之前调用,did 函数在进入状态之后调用
    • componentWillMount()
    • componentDidMount() : 已插入真实DOM, 在render之后才会执行
    • componentWillUpdate(object nextProps, object nextState)
    • componentDidUpdate(object prevProps, object prevState)
    • componentWillUnmount()
  • 生命周期流程:
    • 第一次初始化渲染显示: render()
      • constructor(): 创建对象初始化state
      • componentWillMount() : 将要插入回调
      • render() : 用于插入虚拟DOM回调
      • componentDidMount() : 已经插入回调
    • 每次更新props
      • componentWillReceiveProps() : 将要接收父组件传来的props
      • shouldComponentUpdate() : 子组件是不是应该更新 返回true更新,返回false不更新
      • componentWillUpdate() : 将要更新回调
      • render() : 更新(重新渲染)
      • componentDidUpdate() : 已经更新回调
    • 每次更新state: this.setSate()
      • componentWillUpdate() : 将要更新回调
      • render() : 更新(重新渲染)
      • componentDidUpdate() : 已经更新回调
    • 删除组件
      • ReactDOM.unmountComponentAtNode(document.getElementById(‘example’)) : 移除组件
      • componentWillUnmount() : 组件将要被移除回调
  • 注意:
    • 一般会在componentDidMount()中: 开启监听, 发送ajax请求
    • 可以在componentWillUnmount()做一些收尾工作: 停止监听
  • 生命周期例子:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    import React from 'react';
    import ReactDOM from 'react-dom';
    class Component extends React.Component{
    // 构造函数
    constructor(props){
    super(props)
    this.state = {
    data: 'Old State'
    }
    console.log('初始化数据','constructor');
    }
    // 组件将要加载
    componentWillMount(){
    console.log('componentWillMount');
    }
    // 组件加载完成
    componentDidMount(){
    console.log('componentDidMount');
    }
    // 将要接收父组件传来的props
    componentWillReceiveProps(){
    console.log('componentWillReceiveProps');
    }
    // 子组件是不是应该更新
    shouldComponentUpdate(){
    console.log('shouldComponentUpdate');
    return true;
    }
    // 组件将要更新
    componentWillUpdate(){
    console.log('componentWillUpdate');
    }
    // 组件更新完成
    componentDidUpdate(){
    console.log('componentDidUpdate');
    }
    // 组件即将销毁
    componentWillUnmount(){
    console.log('componentWillUnmount');
    }
    // 处理点击事件
    handleClick(){
    console.log('更新数据:');
    this.setState({
    data: 'New State'
    });
    }
    // 渲染
    render(){
    console.log('render')
    return (
    <div>
    <div>Props: {this.props.data}</div>
    <button onClick={()=>{this.handleClick()}}>更新组件</button>
    </div>
    );
    }
    }
    class App extends React.Component{
    // 构造函数
    constructor(props){
    super(props)
    this.state = {
    data: 'Old Props',
    hasChild: true
    }
    console.log('初始化数据','constructor');
    }
    onPropsChange(){
    console.log('更新props:');
    this.setState({
    data: 'New Props'
    });
    }
    onDestoryChild(){
    console.log('干掉子组件:');
    this.setState({
    hasChild: false
    });
    }
    render(){
    return (
    <div>
    {
    this.state.hasChild ? <Component data={this.state.data}/> : null
    }
    <button onClick={()=>{this.onPropsChange()}}>改变Props</button>
    <button onClick={()=>{this.onDestoryChild()}}>干掉子组件</button>
    </div>
    );
    }
    }
    ReactDOM.render(
    <App/>,
    document.getElementById('app')
    );

应用编码:

  • 拆分组件
  • 数据结构的设计
  • props设计
  • state设计
  • 自定义事件处理(消息发布/订阅): pubsub.js
    • PubSub.subscribe(“事件名”, function(eventName, data){})
    • PubSub.publish(“事件名”, data) : 发布事件

组件总结

  • 组件的组成
    • html
    • css
    • js
    • 图片
  • 为什么要整组件化开发
    • 应用开发越来越复杂(结构/行为)–> 简化开发—>拆分界面—>多个组件
    • 多个界面有相同的功能(可能有一些变化部分)–>提高复用
      –>抽取成一个组件(使用props技术解决变化部分)–>各个界面只使用组件标签就可以轻松实现效果