一个渲染UI的框架

react的功能其实很单一,就是负责页面渲染的功能,它的使用离不开其它工具的使用,比如数据管理工具redux,路由管理react-router等,因为react只负责UI的渲染,而与之不同的是Angular几乎不需要这些,自身已经包含了这些。

组件化

与angular以及其它mvc框架不同,使用react就会发现,它没有controller控制器,甚至它连数据监听都么有。每个react组件都有两部分组成,数据和视图。数据就是state(props也算,不过它是别人的数据,组件只能用,不能修改),它决定着视图如何显示,控制着视图的更新,虽然这个作用有点监听的意味了,但本质却是不一样的。这里使用的是setState函数,它会自动调用render,触发视图重新渲染。如果只是修改了state中的数据,而不去调用setState,是不会触发更新的。这也说明了react并不是一个mvc框架,只是一个负责UI的框架,对于数据变化这些都不关心,只关心结果——视图。react组件就是一个有独立功能的视图模块,然后零星的小组件就可以组成一个大组件,再组成一个页面,而每一个组件之间互不影响,功能独立,这样的好处就是可以多次复用。

Virtual Dom

说到virtual dom 绝不是一两句话能说清楚的。做为前端应该都对jquery再熟悉不过了,比如jquery对相关id,class元素的操作,前提是这个元素在页面上存在,做出修改后,也是直接将真实的dom重新渲染,如果元素不存在也就没法操作了。而virtual dom正好相反的,它会在渲染前通过在virtual dom中比较,只渲染修改过的部分,比如一个有100个dom的页面,只修改了其中一个,那么页面重新渲染的也只有这一个。
举个简单的例子来说吧。Javascript 和 DOM相当于两座城市,他们之间通过一条收费的路连接,js每次对DOM的操作都要通过这里,要交费。那么操作的越多,要交的钱就越多,代价就越大。这时候,virtual dom 出现了,它就相当于是一部手机,有啥事先商量一下要不要见面,只有需要的时候才操作一次。这样就大大减少了操作的次数。
通过react-developer工具,可以看到页面中的一个dom在virtual dom中是以这样的结构存在的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
$$typeof: Symbol(react.element),
_owner: null,
_self: null,
_source: null,
_store: Object,
key: null,
props:{
children: Object,
className: 'footer-select'
onClick: onClick()
},
ref:null,
type:"span",
__proto__:Object
}

在react中,一个页面就是有无数个这样的结构组成的virtual dom tree组成。

React 组件生命周期

React组件在初始化的时候,会有几个过程

  1. 设置默认props:es5使用getDefaultProps,在新的es7中使用static defaultProps
  2. 设置默认state:es5使用getInitialState,es6中直接在constructor里使用this.state ,此时可以使用props
  3. componentWillMount():组件初始化,但是还没有渲染之前,整个生命周期中只会调用一次,此时可以使用state, props等
  4. render():react中很重要的过程,在这个过程中创建virtual dom,此时并不需要做diff运算的。
  5. componentDidMount():组件渲染后调用一次,把真实dom展示出来,此时可以使用this.getDOMNode()操作真实dom节点

React组件在更新的时候,会有几个过程

  1. componentWillReceiveProps(nextProps):组件初绍化的时候不调用,只有props有变化时候调用
  2. shouldComponentUpdate(nextProps, nextState):React中性能优化中很重要的一个函数,在接受新的state和props时候会调用。此函数作用就是比较state和props与原来的是否是相同的,如果是相同的就返回false,阻止更新,避免生成相同的新dom与原来的进行diff计算,节省性能。
  3. componentWillUpdate(nextProps, nextState):组件即将更新时调用
  4. render():进行diff计算,并更新新的dom
  5. componentDidUpdate():组件重新更新完成,此时可以操作dom节点

React组件在卸载的时候,会有几个过程

  1. componentWillUnmount():组件卸载的时候调用,清除事件,数据等(父组件卸载时,子组件也会被自动卸载)

render()在整个过程中会多次被调用,但是第一次调用与后面调用作用会稍有不同,第一次只是创建virtual dom,而不会进行diff运算。

React render()触发

在React中render()是会被多次触发的,假如shouldComponentUpdate默认不做修改。

  1. 组件首次渲染Initial
  2. 调用this.setState函数(这里需要注意的是,setState可能是异步的,并不会每次都触发render)
  3. 父组件更新时,子组件触发render(与props是否改变、父子组件之间是否有数据交互无关)
  4. 调用this.forceUpdate
    可以参考下图
    alt

React setState()

  • 不能直接修改state

    1
    2
    // 错误
    this.state.name = '张三';

    而只能通过 setState

    1
    2
    // 正确
    this.setState({name: '张三'});

    唯一可以调用this.state的地方,就是构造函数contructor里面。

  • state 更新可能是异步的
    this.props 和 this.state 可能是异步更新的,不能依赖他们的值计算下一个state(状态)。

    1
    2
    3
    4
    // 错误
    this.setState({
    result: this.state.result + this.props.increment,
    });

    正常的做法是,使用setState的另一种用法,接受一个函数,返回一个对象。
    这个函数会有两个参数:第一个参数是上一个state(状态),第二个参数就是props

    1
    2
    3
    4
    // 正确
    this.setState((prevState, props) => ({
    result: prevState.result + props.increment
    }));

React 数据管理Redux

什么是redux ?

redux相当于在我们的顶层组件上又包裹了一层组件,作用就是实现各组件之间的通信,数据运算、状态管理及共享。

本文地址: http://wsdever.github.io/2018/01/10/20180112/