react深入研究

深入react

react是一门很火的技术,虽然我用过它,但是并没有真正非常深入进去,所以还是要重新审视一番才行

  • Autobinding: 在 JavaScript 里创建回调的时候,为了保证 this 的正确性,一般都需要显式地绑定方法到它的实例上。有了 React,所有方法被自动绑定到了它的组件实例上。React 还缓存这些绑定方法,所以 CPU 和内存都是非常高效。而且还能减少打字!

  • 事件代理 : React 实际并没有把事件处理器绑定到节点本身。当 React 启动的时候,它在最外层使用唯一一个事件监听器处理所有事件。当组件被加载和卸载时,只是在内部映射里添加或删除事件处理器。当事件触发,React 根据映射来决定如何分发。当映射里处理器时,会当作空操作处理。参考 David Walsh 很棒的文章 了解这样做高效的原因。

react的优缺点

每一个框架的形成,必然有它存在的理由和一些不足,react也不例外,详细可了解这里

优点

Virtual Dom

React执行后得到的结果并不是真实的dom,而是一个轻量级的JavaScript对象,我们称之为virtual Dom
虚拟DOM是React的一大亮点,具有batching(批处理)和高效的Diff算法

innerHTMLvirtual dom在重绘页面时的区别:

  • innerHTML: render html string + 重新创建所有 DOM 元素
  • Virtual DOM: render Virtual DOM + diff + 必要的 DOM 更新

DOM VS MVVM
相比起 React,其他 MVVM 系框架比如 Angular, Knockout 以及 Vue、Avalon 采用的都是数据绑定:通过 Directive/Binding 对象,观察数据变化并保留对实际 DOM 元素的引用,当有数据变化时进行对应的操作。MVVM 的变化检查是数据层面的,而 React 的检查是 DOM 结构层面的。MVVM 的性能也根据变动检测的实现原理有所不同:Angular 的脏检查使得任何变动都有固定的 O(watcher count) 的代价;Knockout/Vue/Avalon 都采用了依赖收集,在 js 和 DOM 层面都是
O(change):

  • 脏检查:scope digest + 必要 DOM 更新
  • 依赖收集:重新收集依赖 + 必要 DOM 更新

可以看到,Angular 最不效率的地方在于任何小变动都有的和 watcher 数量相关的性能代价。但是!当所有数据都变了的时候,Angular 其实并不吃亏。依赖收集在初始化和数据变化的时候都需要重新收集依赖,这个代价在小量更新的时候几乎可以忽略,但在数据量庞大的时候也会产生一定的消耗。
详细可以戳这里 以及神奇的dom diff算法实现1 实现2

diff算法实现从O(n^3)到O(n)的转变是由下面三种策略实现:

  • tree diff只对两棵dom树的相对层级的节点进行比较,对于不同层级的节点,只有创建和删除操作
  • component diff 同一类型的组件,按照同源策略继续比较virtual dom,如果不是同一组件,则将该组件判为dirty component,从而替换整个组件的子节点
  • element diff 对元素的不同,其中有三个主要的函数insert_markup move_existing remove_node
使用JSX直观的定义用户界面

JSX是React的核心组成部分,它使用XML标记的方式去直接声明界面,界面组件之间可以互相嵌套。详细介绍

  //angular中的模板定义
    <ul class="unstyled">
      <li ng-repeat="todo in todoList.todos">
        <input type="checkbox" ng-model="todo.done">
        <span class="done-{{todo.done}}">{{todo.text}}</span>
      </li>
    </ul>
 //jsx的简单高效
     var lis = this.todoList.todos.map(function (todo) {
      return  (
        <li>
          <input type="checkbox" checked={todo.done}>
          <span className={'done-' + todo.done}>{todo.text}</span>
        </li>
      );
    });

    var ul = (
      <ul class="unstyled">
        {lis}
      </ul>
    );

JSX完美利用了JavaScript自带的语法和特性,我们只要记住HTML只是代码创建DOM的一种语法形式,就很容易理解JSX。而这种使用代码构建界面的方式,完全消除了业务逻辑和界面元素之间的隔阂,让代码更加直观和易于维护。

React并不会真正的绑定事件到每一个具体的元素上,而是采用事件代理的模式:在根节点document上为每种事件添加唯一的Listener,然后通过事件的target找到真实的触发元素。这样从触发元素到顶层节点之间的所有节点如果有绑定这个事件,React都会触发对应的事件处理函数。这就是所谓的React模拟事件系统。

尽管整个事件系统由React管理,但是其API和使用方法与原生事件一致。这种机制确保了跨浏览器的一致性:在所有浏览器(IE8及以上)都可以使用符合W3C标准的API,包括stopPropagation(),preventDefault()等等。对于事件的冒泡(bubble)和捕获(capture)模式也都完全支持。

  • 在jsx中使用事件
  <button onclick="checkAndSubmit(this.form)">Submit</button>
  • 在jsx中使用样式
    在JSX中使用样式和真实的样式也很类似,通过style属性来定义,但和真实DOM不同的是,属性值不能是字符串而必须为对象,例如:
<div style={{color: '#ff0000', fontSize: '14px'}}>Hello World.</div>
组件化

组件并不是一个新的概念,它意味着某个独立功能或界面的封装,达到复用、或是业务逻辑分离的目的。而React却这样理解界面组件:

所谓组件,就是状态机器

React将用户界面看做简单的状态机器。当组件处于某个状态时,那么就输出这个状态对应的界面。通过这种方式,就很容易去保证界面的一致性。

在React中,你简单的去更新某个组件的状态,然后输出基于新状态的整个界面。React负责以最高效的方式去比较两个界面并更新DOM树。

每个组件都会有一个render方法,这个方法返回组件的实例,最终整个界面得到一个虚拟DOM树,再由React以最高效的方式展现在界面上。

React使用组件来封装界面模块,整个界面就是一个大组件,开发过程就是不断优化和拆分界面组件、构造整个组件树的过程。可以认为组件类似于其他框架中Widget(或Control)的概念,但又有所不同。React中的界面一切皆为组件,而Widget一般只是嵌入到界面中为完成某个功能的独立模块。

组件之间的消息传递,请移步这里

组件的生命周期:
当组件在客户端被实例化,第一次被创建时,以下方法依次被调用:

  • getDefaultProps
  • getInitialState
  • componentWillMount
  • render
  • componentDidMount

当组件在服务端被实例化,首次被创建时,以下方法依次被调用:

  • getDefaultProps
  • getInitialState
  • componentWillMount
  • render

注意: props和state的区别:

  • props只能当做只读数据,不能直接改写 this.props
  • state只存在组件的内部,props 在所有实例中共享
  • 不要直接修改 this.state,要通过 this.setState 方法来修改。

每次修改 state,都会重新渲染组件,实例化后通过 state 更新组件,会依次调用下列方法:

  • shouldComponentUpdate
  • conponentWillUpdate
  • render
  • conponentDidUpdate

componentWillMount

该方法在首次渲染之前调用,也是再 render 方法调用之前修改 state 的最后一次机会。

componentDidMount

该方法不会在服务端被渲染的过程中调用。该方法被调用时,已经渲染出真实的 DOM,可以再该方法中通过this.getDOMNode()访问到真实的 DOM(推荐使用 ReactDOM.findDOMNode())。

由于组件并不是真实的 DOM 节点,而是存在于内存之中的一种数据结构,叫做虚拟 DOM (virtual DOM)。只有当它插入文档以后,才会变成真实的 DOM 。有时需要从组件获取真实 DOM 的节点,这时就要用到 ref 属性:

var Area = React.createClass({
    render: function(){
        this.getDOMNode(); //render调用时,组件未挂载,这里将报错

        return <canvas ref='mainCanvas'>
    },
    componentDidMount: function(){
        var canvas = this.refs.mainCanvas.getDOMNode();
        //这是有效的,可以访问到 Canvas 节点
    }
})

需要注意的是,由于 this.refs.[refName] 属性获取的是真实 DOM ,所以必须等到虚拟 DOM 插入文档以后,才能使用这个属性,否则会报错。

componentWillReceiveProps

组件的 props 属性可以通过父组件来更改,这时,componentWillReceiveProps 将来被调用。可以在这个方法里更新 state,以触发 render 方法重新渲染组件。

componentWillReceiveProps: function(nextProps){
    if(nextProps.checked !== undefined){
        this.setState({
            checked: nextProps.checked
        })
    }
}

shouldComponentUpdate

如果你确定组件的 props 或者 state 的改变不需要重新渲染,可以通过在这个方法里通过返回 false 来阻止组件的重新渲染,返回 `false 则不会执行 render 以及后面的 componentWillUpdate,componentDidUpdate 方法。

该方法是非必须的,并且大多数情况下没有在开发中使用。

shouldComponentUpdate: function(nextProps, nextState){
    return this.state.checked === nextState.checked;
    //return false 则不更新组件
}

componentWillUpdate

这个方法和componentWillMount 类似,在组件接收到了新的 props 或者 state 即将进行重新渲染前,componentWillUpdate(object nextProps, object nextState)会被调用,注意不要在此方面里再去更新 props 或者 state。

componentDidUpdate

这个方法和 componentDidMount 类似,在组件重新被渲染之后,componentDidUpdate(object prevProps, object prevState)会被调用。可以在这里访问并修改 DOM。

销毁时

componentWillUnmount

每当React使用完一个组件,这个组件必须从 DOM 中卸载后被销毁,此时 componentWillUnmout 会被执行,完成所有的清理和销毁工作,在 conponentDidMount 中添加的任务都需要再该方法中撤销,如创建的定时器或事件监听器。

当再次装载组件时,以下方法会被依次调用:

  • getInitialState
  • componentWillMount
  • render
  • componentDidMount
单向数据流动

react的单向数据流动主要体现在props数据的传递,主要是指父元素向子元素传递,它的这种思想和redux结合起来使用会很酷,稍后再总结

React拥抱ES6

在React中尝试编写ES6是个非常不错的开始,React并不是一开始就支持ES6的, 而是从 v0.13.0 开始支持的。你会经常用到的ES6特性包括类、箭头函数、consts 和模块。例如,我们会经常从继承 React.Component 类开始编写我们的组件。

缺点

React是数据驱动式的UI component体系,是一个开放的数据依赖式,非自闭OO对象。它会有以下挑战

  • render方法可能很大,component显示逻辑是一次性在render里实现的。这里往往有两部分逻辑实现,初次数据绑定,与用户交互之后的算法。
  • render出一个ReactElement树,但这个树中的一些组件、UI元素因为计算被前移了,这会导致这个树看起来不太完整,进而不太直观。
  • 虽然可以分解成小的component,构建大的Component可能是个挑战,因为所有逻辑都依赖于外部数据,而外部数据相对不稳定,组件失去了自我边界的保护,非自闭。
  • 当交互复杂,组件的state也会越来越大,render全局算法会越来越难写。
  • 把子组件的行为传上来也是一件不显化的事,往往需要把父组件的一个函数作为回调传给子组件。
  • 大组件往往有多个Page,这几个Page如何交换数据是个很大的挑战

总结:react有很多很好的特性,具体总结请移步这里

转载于:https://my.oschina.net/sunshinewyf/blog/751374

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值