【深入react技术栈】第1、2章学习笔记

一、初入React世界

1.5 React声明周期

react生命周期分为两类:

  • 当组件在挂载或卸载时

  • 当组件接收新的数据时,即组件更新时

初始化组件

import React, { Component, PropTypes } from 'react';
class App extends Component {
    // 类型检查
    static propTypes = {
        // ...
    };
    // 默认类型
    static defaultProps = {
        // ...
    };

    constructor(props) {
        super(props);
        this.state = {
            // ...
        };
    }

    componentWillMount() {
        // ...
    }
    // 在其中使用setState 会更新组件
    componentDidMount() {
        // ...
    }

    render() {
        return <div>This is a demo.</div>
    }
}

挂载或卸载过程

组件的挂载
  • componentWillMount( )
  • render( )
  • componentDidMount( )
组件的卸载
  • componentWillUnmount( ),执行一些清理方法,如事件回收或是清除定时器

数据更新过程

更新过程指父组件向下传递props或组件自身执行setState方法时发生的一系列更新动作。

  • componentWillReceiveProps(nextProps)
componentWillReceiveProps(nextProps){
    // this.setState({});
}
  • shouldComponentUpdate(nextProps,nextState)
shouldComponentUpdate(nextProps,nextState){
    // 条件判断
    // return true; 
}
  • componentWillUpdate(nextProps,nextState) 此时不能执行setState
  • render( )
  • componentDidUpdate(prevProps,prevState)

如果自身的state更新了,那么会依次执行shouldComponentUpdate、componentWillUpdate 、render 和 componentDidUpdate。

如果组件是由父组件更新 props 而更新的,那么在 shouldComponentUpdate 之前会先执行componentWillReceiveProps 方法。


image

1.6 React与DOM

1.6.1 ReactDOM

其API非常少,只有findDOMNode,unmountComponentAtNode和render

  • findDOMNode
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
  class App extends Component {
     componentDidMount() {
     // this 为当前组件的实例
     const dom = ReactDOM.findDOMNode(this);
   }
   render() {}
}

findDOMNode只对已经挂载的组件有效,DOM 真正被添加到 HTML 中的生命周期方法是componentDidMount 和 componentDidUpdate 方法

  • render

把 React 渲染的Virtual DOM 渲染到浏览器的 DOM 当中,就要使用 render 方法

1.6.2 ReactDOM的不稳定方法

unstable_renderSubtreeIntoContainer( ),它与render方法的区别在于是否传入父节点

1.6.3 refs

指向组件的实例

二、漫谈React

2.1 事件系统

React基于 Virtual DOM实现了一个SyntheticEvent(合成事件)层,我们呢所定义的事件会接收到一个SyntheticEvent对象的实例。所有事件都自动绑定到最外层上。

<button onClick={this.handleClick}>点击</button>
事件委托

事件处理函数绑定到结构的最外层,使用一个统一的事件监听器,这个事件监听器上维持了一个映射来保存所有组件内部的事件监听和处理函数。当组件挂载或卸载时,只是在这个统一的事件监听器上插入或删除一些对象;当时间发生时,首先被这个统一的事件监听器处理,然后在映射里找到真正的事件处理函数并调用。

自动绑定

在React组件中,自动绑定this为当前组件。但在使用ES6 classes或纯函数时,这种自动绑定就不存在了,需要手动实现绑定。

  • bind方法
  • 构造器内声明 完成绑定
  • 箭头函数
在componentDidMount中完成原生事件的绑定,一定要在组件卸载时手动移除。
2.1.5 对比React合成事件与JS原生事件
事件传播与阻止事件传播

原生:捕获阶段、事件处理阶段、冒泡阶段。阻止传播兼容问题。(IE和DOM2)

react:没有捕获阶段。阻止传播e.preventDefault( )

事件类型

合成事件的实践类型是原生事件类型的一个子集

事件绑定方式
事件对象

原生有兼容问题(IE和W3C标准),react不存在兼容问题

2.2 表单

受控组件

每当表单的状态发生变化时,都会被写入到组件的state中,这种组件在React中被称为受控组件(controlled component)。
受控组件更新state的流程:

(1)可以通过在初始 state 中设置表单的默认值

(2)每当表单的值发生变化时,调用onChange事件处理器

(3)事件处理器通过合成事件对象e拿到改变后的状态,并更新state

(4)setState触发视图的重新渲染,完成表单组件值得更新

2.3 样式处理

基本样式设置
  • className prop添加自定义样式
  • 行内样式要使用对象设置
const style={
    color:'white',
    // 渲染成10px
    height:10
};
const component = <Component style={style}/>
CSS Modules

此处见P79页

2.4 组件间通信

2.4.1 父组件向子组件通信

通过props 向子组件传递信息

2.4.2 子组件向父组件通信
  • 利用回调函数
  • 利用自定义事件机制
2.4.3 跨级组件通信

React中,我们可以使用 context 来实现跨级父子组件间的通信

// listItem组件
class ListItem extends Component {
  static contextTypes = {
    color: PropTypes.string,
  };
  render() {
    const { value } = this.props;

    return (
      <li style={{background: this.context.color}}>{value}</li>
    )
  }
 }


// List组件
class List extends Component {
  static childContextTypes = {
    color: PropTypes.string,
  };
  getChildContext() {
    return {
        color: 'red'
    }
  }
}

父组件中定义了 ChildContext,这样从这一层开始的子组件都可以拿到定义的context。

2.5 组件间抽象

高阶组件(HOC)

它接受React组件作为输入,输出一个新的React组件。
实现高阶组件的方法:

1.属性代理

高阶组件通过被包裹的React组件来操作props,使原始组件具备高阶组件对它的修饰。

属性代理构建高阶组件时,执行生命周期的过程类似于堆栈调用:

didmount--HOC didmount-- (HOCs didmount)--(HOCs willUnMount)--HOC willUnMount--unmount
import React, { Component } from 'react';

const MyContainer = (WrappedComponent) => {
  class extends Component {
    render() {
      return <WrappedComponent {...this.props}>
    }
  }
}

当然我们也可以用 decorator 语法糖来转换,简化了高阶组件的调用

import React, { Component } from 'react';
@MyContainer
class MyComponent extends Component {
  render();
}

export default MyComponent;
  • 控制props
// 新组件会多一个text的prop
import React, { Component } from 'react';
const MyContainer = (WrappedComponent) => {
  class extends Component {
    render() {
      const newProps = {
         text: newText,
      };
      return <WrappedComponent {...this.props} {...newProps}>
    }
  }
}
  • 通过refs使用引用

高阶组价中,我们可以接受 refs 使用 WrappedComponent 的引用。

import React, { Component } from 'react';
const MyContainer = (WrappedComponent) => {
  class extends Component {
    proc(wrappedComponentInstance) {
      wrappedComponentInstance.method();
    }
    render() {
      const props = Object.assign({}, this.props, {
          ref: this.proc.bind(this),
      });
      return <WrappedComponent {...props}>
    }
  }
}
  • 抽象state

抽象一个input组件

import React, { Componet } from 'react';
const MyContainer = (WrappedComponent) => {
  class extends Component {
    constructor(props) {
      super(props);
      this.state = {
        name:'',
      }

      this.onNameChange = this.onNameChange.bind(this);
    }
  }
 onNameChange(event) {
      this.setState({
          name: event.target.value,
      });
    }

    render() {
      const newProps = {
        name: {
          value: this.state.name,
          onChange: this.onNameChange,
        }
      }
      return <WrappedComponent {...this.props} {...newProps} />
    }
}

通过这样的封装,我们就得到了一个被控制的input组件

@MyContainer
class MyComponent extends Component {
  render() {
    return <input name="name" {...this.props.name}>
  }
}
2.反向继承

高阶组件继承于被包裹的React组件,HOC的调用顺序和队列是一样的。

didmount--HOC didmount-- (HOCs didmount)--willUnMount--HOC willUnMount--(HOCs willUnMount)
  • 渲染劫持

指高阶组件可以控制WrappedComponent的渲染过程。不能操作组件的子组件。

// 实例一:条件渲染
const MyContainer = (WrappedComponent) => {
  class extends WrappedComponent {
    render() {
      if(this.props.loggedIn) {
        return super.render();
      } else {
        return null;
      }
    }
  }
}

// 实例二:对render结果进行修改
const MyContainer = (WrappedComponent) => {
   class extends WrappedComponent {
    render() {
       const elementsTree = super.render();
       let newProps = {};

      if(elementsTree && elementsTree.type === 'input'){
        newProps = {value: 'May the force be with you'};
      }
      const props = Object.assign({}, elementsTree.props, newProps);
      const newElementsTree = React.cloneElement(elementsTree, props, elementsTree.props.childre);
      return newElementsTree;
     }
   }
}
  • 控制state
4. 组件参数

调用高阶组件时传入一些参数

import React, { Component } from 'react';
function HOCFactory(...params) {
  return function HOCFactory(WrappedComponent) {
    return class HOC extends Component {
      render() {
        return <WrappedComponent {...this.props}>
      }
    }
   }
}

// 使用
HOCFactoryFactory(params)(WrappedComponent);
// 或者
@HOCFactoryFactory(params);
class WrappedComponent extends React.Component{}

2.6 组件的性能优化

纯函数
  • 给定相同的输入,它总能返回相同的输出
  • 过程没有副作用
  • 没有额外的状态依赖
PureRender

原理:重新实现了 shouldComponentUpdate 生命周期方法,让当前传入的 props
和 state 与之前的作浅比较

// 使用
import React, { Component } from 'react';
import PureRenderMixin from 'react-addons-pure-render-mixin';
class App extends Component{
    constructor(props){
        super(props);
        this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);
    }
    render(){
        return <div className={this.props.className}>foo</div>
    }
}
  • 设置子组件

对于设置了子组件的React组件,在调用shouldComponentUpdate时,均返回true。这时,为免组件重复渲染,给父组件设置PureRender

Immutable

Immutable Data一旦创建,就不能再更改。对Immutable对象进行修改添加删除操作都会返回一个新的Immutable对象。

运用Immutable.js 修改shouldComponentUpdate,可以极大的提高性能。

key

标识当前项的唯一性,用来做Virtual DOM diff

react-addons-perf

官方插件,性能测试工具

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