《React:引领未来的用户界面框架》读书笔记

一、JSX

1 JSX定义及优点

  • JavaScript XML
  • 每个JSX节点都对应一个js函数
  • JSX好处:语义化,抽象化(React版本更新时代码改动得少),关注点分离(逻辑+标签封装在组件中)

2 使用组件

  • 使用动态值:{}中的任何东西都会被求值:变量,函数求值,字符串数组自动求值为拼接字符串。
  • 子节点:{this.props.children},React会将放在该组件标签之间的任何东西渲染出来。举例:定义组件Divider
    1
    2
    3
    4
    5
    6
    7
    8
    9
    var Divider = React.createClass({
    render: function(){
    return (
    <div className="divider clearfix">
    <h2>{this.props.children}</h2><hr />
    </div>
    );
    }
    });

使用组件:
<Divider>Questions</Divider>这里的Questions就是this.props.children
渲染输出的结果为:

1
2
3
<div className="divider clearfix">
<h2>Questions</h2><hr />
</div>

3 JSX不同于HTML的地方:

  1. 用花括号设置组件的内联属性
  2. 条件判断:不要在花括号中写if-else,应使用:
    (1) 三目运算符
    1
    2
    3
    4
    5
    render: function(){
    return <div className={
    this.state.isComplete ? 'is-complete' : ''
    }>...</div>
    }

(2)设置一个变量并在属性中引用它
(3)将逻辑转化到函数中

1
2
3
4
5
6
7
8
9
10
11
12
//(2)(3)类似
getIsComplete: function(){
return this.state.isComplete ? 'is-complete' : '';
},
render: function(){
return <div className={this.getIsComplete()}>...</div>
}
//或
render: function(){
var isComplete = this.getIsComplete;
return <div className={isComplete}>...</div>
}

(4)使用&&运算符

1
2
3
4
render: function(){
var isComplete = this.getIsComplete;
return <div className={this.state.isComplete && 'is-complete'}...</div>
}

  1. 特殊属性key和ref:
  • key是组件的唯一标识符,React根据它智能地决定应该重用还是销毁并重新创建某个组件。当两个已经渲染于DOM中的组件交换位置时,React能匹配对应的key而不需要重新渲染DOM。提升了渲染性能。
  • 父组件在render方法之外保持对子组件的引用。可以在组件中的任何位置使用this.refs.refName获取这个引用(是支持实例,不是真正的DOM节点),通过this.refs.refName.getDOMNode()访问真实的DOM节点。
  1. 事件:捕获事件 => 设置属性

  2. 注释:
    (1) 作为子节点:用花括号,可多行

    1
    2
    3
    4
    5
    6
    7
    <div>
    {/*
    comments
    with multiple lines
    */}
    <input name="email" placeholder="Email Address">
    </div>

(2) 作为内联属性:单行/多行

1
2
3
4
5
6
7
8
9
10
<div>
<input
/*
commonts with multiple lines
*/
hello
name="email" //a single-line commont
placeholder="Email Address"
>
</div>

  1. 特殊属性:为了不和javascript语法冲突
    label标签里的for属性要用htmlFor;
    class属性要用className

  2. 样式

4 没有JSX的React

二、组件生命周期

  • 生命周期API
  • 是钩子函数
  • 组件是个状态机:对于特定的输入总会返回一致的输出
  • 组件的整个生命周期中,其propsstate改变了则DOM表现也有相应变化

1 实例化

当一个组件第一次实例化时依次调用:
(1) getDefaultProps

  • 一个组件类只会调用一次。
  • 没有被父组件指定props属性的新建实例会通过这个方法返回的对象设置默认的props值。
  • 对象和数组,会在所有实例中共享,而不是拷贝。

(2) getInitialState

  • 组件类的每个实例有且只有一次调用
  • 在这个方法里可以访问到this.props

(3) componentWillMount

  • 在首次渲染完成前被调用
  • render前最后一次修改组件state的机会

(4) render

  • 创建一个虚拟DOM,表示组件的输出
  • 组件唯一一个必需的方法
  • 只能通过this.propsthis.state访问数据
  • 可以返回null,false或任何React组件
  • 只能出现一个顶级组件(所以经常用<div>包裹返回的一组元素)
  • 不能改变组件的状态,不能修改DOM的输出 => “纯净”

(5) componentDidMount
真实的DOM被渲染后,可在该方法内部通过this.getDOMNode()访问到真实DOM

注意:该组件类后续所有应用将不会调用getDefaultProps方法,而是依次调用上面第2到5个方法。

2 存在期

组件已经渲染完成,用户可以与它交互(通过鼠标键盘等触发事件处理器)。用户会改变组件或整个应用的state,新的state会流入组件树。依次调用:
(1) componentWillReceiveProps
组件的props随时可以通过父辈组件来更改。在该方法中可以更改props对象和更新state(使用setState())。

(2) shouldComponentUpdate
大部分时间没必要使用,准确测量出性能瓶颈后用于性能调优:

  • 如果组件不需要渲染新的propsstate,该方法返回false: React会跳过render前后的钩子函数componentWillUpdatecomponentDidUpdate
  • 会调用该方法的React插件:PureRenderMixin,用于组件及其子组件均为“纯净的”(对于相同的propsstate,总是渲染出相同的DOM)。它比较当前和接下来的propsstate,相同则返回false。但只是浅比较对象,仅用于propsstate为简单数据结构时。如果包含复杂数据结构,应使用forceUpdate()

(3) componentWillUpdate
componentWillMount类似。组件接收到新的propsstate,渲染之前调用。该方法中不可以更新propsstate

(4) render

(5) componentDidUpdate
componentDidMount类似。可以更新已经渲染好的DOM

3 销毁&清理期

当组件被使用完成后,调用componentWillUnmount方法清理该实例。在componentDidMount中添加的定时器、事件监听器等,需要在该方法中撤销。

4 不能把计算后的值赋给state

getInitialState中不能通过this.props来创建state。如果要通过props计算值赋给state,则应该放在render中执行。这样才能保证计算后的值不会与派生出它的props值不同步。
React优势:专注于维护数据的单一来源。

三、数据流

React单向数据流:父节点 -> 子节点
组件只需从父节点获取props渲染即可。顶层组件的props如果改变,React会递归向下遍历整棵组件树,重新渲染所有使用这个属性的组件。
React组件 => 函数:参数为propsstate,返回结果为一个虚拟DOM

props

可以为任意数据结构。
设置属性的方法:挂载到组件上;调用组件实例的setProps方法(很少用)。
不能通过setPropsthis.props修改!组件不能自己修改自己的props
props可以用来添加事件处理器:

1
2
3
//最近项目用到这两种绑定事件处理器的方式:区别?
onClick={() => { addClue(false); }}
onClick={handleBack}

PropTypes

组件的配置对象,验证props

state

  • stateprops的区别:前者只存在于组件的内部
  • state可用于确定元素的视图状态
  • state不能用this.state直接修改,只能通过this.setState修改,后者一旦调用,render就会被调用。如果render返回值有变化,虚拟DOM就更新,真实的DOM也会更新。

state和props中的各是哪些数据

  • 不要在state中保存计算出的值,只保存最简单的数据,即组件正常工作时的必要数据,如:复选框的勾选状态,输入框的值,下拉选框是否显示的布尔值等。
  • 不要把props复制到state中,尽可能把props当数据源。

四、事件处理

  • 通过内联属性绑定事件处理器
  • 在事件处理器中更新组件的状态,触发组件重绘。
  • 更新状态:使用this.setState(obj)仅仅影响某一个或某几个状态属性,即:将obj与原stateObj合并,this.replaceState(obj)是将新的obj代替原来的stateObj。不要使用这两种方式以外的方式修改state
  • render函数中可以读取this.state,根据状态进行渲染
  • 事件对象event:和原生js一样。比如通过event.target.value获取input表单的值。React将原生事件封装在SyntheticEvent实例当中,而不是将原生的浏览器事件对象传给事件处理器。但SyntheticEvent在表现和功能上同浏览器的原生事件对象一致,且消除了某些跨浏览器差异。通过SyntheticEvent.nativeEvent访问浏览器原生事件

五、组件的复合

父、子组件的通信:

  • 使用属性:父组件通过属性传入一个回调函数,子组件在需要时进行调用
    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
    //父组件
    var AnswerMultipleChoiceQuestion = React.createClass({
    ...
    handleChanged: function(value){
    this.setState({value: value});
    },
    renderChoices: function(){
    return this.props.choices.map(function(choice, i){
    return AnswerRadioInput({ //子组件
    ...
    onChanged: this.handleChanged //将父组件的函数挂在到子组件的属性中
    });
    }.bind(this)); //为了在子组件中能访问到父组件的函数
    },
    ...
    });
    //子组件
    var AnswerRadioInput = React.createClass({
    propTypes: {
    ...
    onChanged: React.PropTypes.func.isRequired
    },
    handleChanged: function(e){
    var checked = e.target.checked;
    this.setState({checked: checked});
    if(checked){
    this.props.onChanged(this.props.value);//子组件在这里调用了父组件的函数
    }
    },
    render: function(){
    return (
    <label htmlFor={this.state.id}>
    <input type="radio"
    ...
    onChanged={this.handleChanged}/>//子组件将父组件的函数挂载起来,以便通过this.props.onChanged调用
    {this.props.label}
    </label>
    );
    }
    });

六、DOM操作

需要操作底层DOM的场景:

  • 与一个没有使用React的第三方类库进行整合
  • 执行一个React没有原生支持的操作。比如要通过innerHTML访问原生HTML内容

    访问受React控制的DOM节点

  1. 首先要通过this.refs.refName访问到负责控制这些DOM的组件(该组件要添加ref属性,且值唯一)
  2. 访问到组件后,通过this.resf.refName.getDOMNode()访问真正的DOM节点。但是只能在组件挂载之后访问,即:该方法的执行环境不能是render,可以是componentDidMount和事件处理器函数。
分享
0%