深入React技术栈学习笔记

chapter-1

React简介

  1. React为方便View层组件化,承载了构建HTML结构化页面的职责。
  2. React把真实DOM树转换成JavaScript对象树,也就是Virtual DOM,每次数据更新后,重新计算Virtual DOM,并和上一次生成的Virtual DOM做对比,对发生变化的部分做批量更新。React也提供了直观的shouldComponentUpdate生命周期回调,来减少数据变化后不必要的Virtual DOM对比过程,以保证性能。
    它最大的好处其实还在于方便和其他平台集成,比如react-native是基于Virtual DOM渲染出原生控件,因为React组件可以映射为对应的原生控件。在输出的时候,是输出Web DOM,还是Android控件,还是iOS控件,就由平台本身决定了。因此,react-native有一个口号——LearnOnce,Write Anywhere

数据流

在react中,数据是自顶向下单向流动的,即从父组件到子组件。这条规则让组件件的关系变得简单可预测。

React 性能提升

  • shouldComponentUpdate

  • pureComponent

    React15.3 中新加了一个类PureComponent,前身是 PureRenderMixin ,和 Component 基本一样,只不过会在 render 之前帮组件自动执行一次shallowEqual(浅比较),来决定是否更新组件,浅比较类似于浅复制,只会比较第一层。使用 PureComponent 相当于省去了写 shouldComponentUpdate 函数,当组件更新时,如果组件的 propsstate

    1. 引用和第一层数据都没发生改变, render 方法就不会触发,这是我们需要达到的效果。
    2. 虽然第一层数据没变,但引用变了,就会造成虚拟 DOM 计算的浪费。
    3. 第一层数据改变,但引用没变,会造成不渲染,所以需要很小心的操作数据。

生命周期

挂载

  • constructor(props)
  • componentWillMount() 在render前调用。(only one time)
  • render()
  • componentDidMount() 在render后调用。(only one time)

卸载

  • componentWillUnmount(),执行事件回收或者清除定时器

更新

  • componentWillReceiveProps(nextProps)
  • shouldComponentUpdate(nextProps,nextState){
    return boolean;
    }
  • componentWillUpdate(nextProps,nextState),不能在这里执行setState。
  • render()
  • componentDidUpdate(prevProps,prevState)

无状态组件没有生命周期,没有shouldComponentUpdate,因此每次都会更新。

生命周期流程图

image.png

image.png

chapter-2

事件系统(合成事件机制)

vdom在内存中以对象的形式存在,想要在对象上添加事件相对简单。react 基于vdom实现了一个(SyntheticEvent)合成事件层。我们所定义的事件处理器都会收到一个SyntheticEvent对象实例,支持事件冒泡,可以使用stopPropagation()和preventDefault()来阻止冒泡。

事件委托

所有事件都绑定在结构的最外层,使用一个统一的事件监听器,这个事件监听器上维持了一个映射来保存所有组件内部的事件监听和处理函数。简化了事件处理和回收机制,提升了效率。

自动绑定this

在React组件中,每个方法的上下文都会指向该组件的实例,即自动绑定this为当前组件。而且React还会对这种引用进行缓存,以达到cpu和内存的最优化。在使用es6 class或者纯函数时,这种自动绑定就不复存在了,需要手动实现this绑定。

  • 构造函数内声明。
  • 箭头函数。

受控组件与非受控组件

表单组件几个重要属性

  • value:类型为text的input组件,textarea组件以及select组件都借助value prop来展示应用的状态。
  • checked:类型为radio或checkbox的组件借助值为boolean类型的selected prop来展示应用的状态。
  • selected:该属性可作用与select组件下面点option上,React不建议这种方式表示状态,而推荐在select组件上使用value的方式。
  • (事件)onchange。

受控组件

每当表单的状态发生变化时,都会被写入到组件的state中,这种组件称为受控组件。在受控组件中,组件渲染出的状态与它的value或者checked prop 像对应。React通过这种方式消除了组件的局部状态,使得应用的整个状态更加可控。React受控组件更新state的流程如下:

  1. 可以通过在初始state中设置表单的默认值(仅渲染一次)。
  2. 每当表单的值发生变化时,调用onChange事件处理器。
  3. 事件处理器通过合成事件对象e拿到改变后的状态,并更新应用的state。
  4. setState触发视图的重新渲染,完成表单组件值的更新。

非受控组件

如果一个表单组件没有value props(单选按钮和复选框对应的时checked prop)时,就可以称为非受控组件。可以使用defaultValue和defaultChecked prop来表示组件的默认状态。
非受控组件是一种反模式,它的值不受组件自身的state或prop控制,通常需要为其添加ref prop来访问渲染后的底层dom元素。

对比

性能:在受控组件中,每次表单的值发生变化时,都会调用一次onChange事件处理器,这确实会有一些性能上的损耗,虽然使用非受控组件不会出现这些问题,但仍不提倡使用非受控组件,这个问题可以通过Flux/Redux应用架构等方式来达到统一组件状态的目的。

组件通信

父组件向子组件通信

  • 通过props

子组件向父组件通信

  • 利用回调函数。
  • 利用自定义事件机制(类似发布订阅模式,vue中的event bus)。

(子组件)跨级组件通信

  • 使用context,但是不推荐,有点类似全局变量,出问题很难定位。
  • 使用高阶组件。

没有嵌套关系的组件通信

  • 利用自定义事件机制。
    componentDidMount中订阅事件,在componentWillUnmount中取消事件订阅。

高阶组件(HOC)

高阶组件类似高阶函数,他接受React组件作为输入,输出一个新的React组件。

实现高阶组件

  • 属性代理(props proxy):高阶组件通过被包裹的React组件来操作props。
  • 反向继承(inheritance inversion):高阶组件继承于被包裹的React组件。

(Extra)-Redux

核心概念

  • state:应用唯一数据源,存在store中。
  • action:发起(dispath)action修改state。
  • reducer:连接action和state。reducer(state,action)=>return newState。

三大原则

  • 单一数据源。
  • state是只读的。
  • 使用纯函数来执行修改。

(Extra)-React-Router

代码分割

通过 react-loadable,可以做到路由级别动态加载,或者更细粒度的模块级别动态加载:

1
2
3
4
5
6
const AsyncHome = Loadable({
loader: () => import('../components/Home/Home'),//loader接受一个promise对象,结合webpack代码切割
loading: LoadingPage
})

<Route exact path="/" component={AsyncHome} />

withRouter

目的就是让被修饰的组件可以从属性中获取history,location,match,路由组件可以直接获取这些属性,而非路由组件就必须通过withRouter修饰后才能获取这些属性了,比如:

1
<Route path='/' component={App}/>

App组件就可以直接获取路由中这些属性了,但是,如果App组件中如果有一个子组件Foo,那么Foo就不能直接获取路由中的属性了,必须通过withRouter修饰后才能获取到。