浅析 react

 

  1. JSX
    1. 注释:在一个组件的子元素位置使用注释要用 {} 包起来
      1. const App = ( 
         <Nav> 
         {/* 节点注释 */} 
         <Person 
         /* 多行 
         注释 */ 
         name={window.isLoggedIn ? window.name : ''} 
         /> 
         </Nav> 
        ); 

          但 HTML 中有一类特殊的注释——条件注释,它经常使用于判断浏览器的版本:html

      2. <!--[if IE]> 
         <p>Work in IE browser</p> 
        <![endif]-->

          上述方法能够经过使用 JavaScript 判断浏览器版原本替代: react

      3. { 
         (!!window.ActiveXObject || 'ActiveXObject' in window) ? 
         <p>Work in IE browser</p> : '' 
        } 
    2. 元素属性  算法

      1. class 属性改成 className    浏览器

      2. for 属性改成 htmlFor  缓存

  2. React 组件的构建less

    1. React 组件即为组件元素
    2. React 组件基本上由 3 个部分组成——属性(props)、状态(state)以及生命周期方法
    3. React 组件能够接收参数,也可能有自身状态。一旦接收到的参数或自身状态有所改变,React 组件就会执行相应的生命周期方法,最后渲染。整个过程彻底符合传统组件所定义的组件职责。
    4. React 组件的构建方法
      1. React.createClassdom

          1. const Button = React.createClass({ 
             getDefaultProps() { 
             return { 
             color: 'blue', 
             text: 'Confirm', 
             }; 
             }, 
             
             render() { 
             const { color, text } = this.props; 
             
             return ( 
             <button className={`btn btn-${color}`}> 
             <em>{text}</em> 
             </button> 
             ); 
             } 
            });
            1. 在 0.14 版本发布以前,这一直都是 React 官方惟一指定的组件写法
            2. 只用写成 <Button />,就能够被解析成 React.createElement(Button) 方法来建立 Button
              实例,这意味着在一个应用中调用几回 Button,就会建立几回 Button 实例异步

      2. ES6 classes函数

          1. import React, { Component } from 'react'; 
             
            class Button extends Component { 
             constructor(props) { 
             super(props); 
             } 
             
             static defaultProps = { 
             color: 'blue', 
             text: 'Confirm', 
             }; 
             
             render() { 
             const { color, text } = this.props; 
             
             return ( 
             <button className={`btn btn-${color}`}> 
             <em>{text}</em> 
             </button> 
             ); 
             } 
            }

            React 的全部组件都继承自顶层类 React.Component。它的定义很是简洁,只是初始化了
            React.Component 方法,声明了 props、context、refs 等,并在原型上定义了 setState 和
            forceUpdate 方法。内部初始化的生命周期方法与 createClass 方式使用的是同一个方法
            建立的。优化

      3. 无状态函数(stateless function)

        1. function Button({ color = 'blue', text = 'Confirm' }) { 
           return ( 
           <button className={`btn btn-${color}`}> 
           <em>{text}</em> 
           </button> 
           ); 
          } 
          1. 无状态组件只传入 props 和 context 两个参数;也就是说,它不存在 state,也没有生命周
            期方法,组件自己即上面两种 React 组件构建方法中的 render 方法。不过,像 propTypes 和
            defaultProps 仍是能够经过向方法设置静态属性来实现的。

          2. 在适合的状况下,咱们都应该且必须使用无状态组件。无状态组件不像上述两种方法在调用
            时会建立新实例,它建立时始终保持了一个实例,避免了没必要要的检查和内存分配,作到了内部
            优化。

    5. React 数据流

      1. 在 React 中,数据是自顶向下单向流动的,即从父组件到子组件
      2. state 与 props 是 React 组件中最重要的概念
      3. 若是顶层组件初始化 props,那么 React 会向下遍历整棵组件树,从新尝试渲染全部相关的子组件。

      4. 而 state 只关心每一个组件本身内部的状态,这些状态只能在组件内改变。

      5. 把组件当作一个函数,那么它接受了 props 做为参数,内部由 state 做为函数的内部参数,返回一个 Virtual DOM 的实现

    6. state: setState 
      1. 是异步操做函数;
      2. 组件在尚未渲染以前, this.setState 尚未被调用;
      3. 批量执行 State 转变时让 DOM 渲染更快(相对比一个一个的setState的来的快)。
    7. props 自己是不可变的:组件的 props 必定来自于默认属性或经过父组件传递而来
      1. 若是说要渲染一个对 props 加工后的值,最简单的方法就是使用局部变量或直接在 JSX 中计算结果

      2. React 为 props 一样提供了默认配置,经过 defaultProps 静态变量的方式来定义
      3. React.Children 是 React 官方提供的一系列操做 children 的方法。它提供诸如 map、forEach、count 等实用函数,能够为咱们处理子组件提供便利

    8. 组件 props

      1. 如今咱们发现对于 state 来讲,它的通讯集中在组件内部;对于 props 来讲,它的通讯是父组

        件向子组件的传播。

    9. propTypes:用于规范 props 的类型与必需的状态
    10. React 生命周期

      1. 挂载、渲染和卸载这
      2. import React, { Component, PropTypes } from 'react'; 
         
        class App extends Component { 
         static propTypes = { 
         // ... 
         }; 
         
         static defaultProps = { 
         // ... 
         }; 
         
         constructor(props) { 
         super(props); 
         
         this.state = { 
         // ... 
         }; 
         } 
         
         componentWillMount() { 
         // ... 
         } 
        componentDidMount() { 
         // ... 
         } 
         
         render() { 
         return <div>This is a demo.</div>; 
         } 
        } 
      3. componentWillMount 方法会在 render 方法以前执行,而 componentDidMount 方法会在 render 方法以后执行,这些都只会在组件初始化时运行一次

      4. 组件卸载很是简单,只有 componentWillUnmount 这一个卸载前状态
      5. import React, { Component, PropTypes } from 'react'; 
         
        class App extends Component { 
         componentWillUnmount() { 
         // ... 
         } 
         
         render() { 
         return <div>This is a demo.</div>; 
         }

        在 componentWillUnmount 方法中,咱们经常会执行一些清理方法,如事件回收或是清除定
        时器。

    11. 数据更新过程
      1. 更新过程指的是父组件向下传递 props 或组件自身执行 setState 方法时发生的一系列更新动做

      2. import React, { Component, PropTypes } from 'react'; 
         
        class App extends Component { 
         componentWillReceiveProps(nextProps) { 
         // this.setState({}) 
         } 
         
         shouldComponentUpdate(nextProps, nextState) { 
         // return true; 
         } 
         
         componentWillUpdate(nextProps, nextState) { 
         // ... 
         } 
         
         componentDidUpdate(prevProps, prevState) { 
         // ... 
         } 
         
         render() { 
         return <div>This is a demo.</div>; 
         } 
        } 

        若是组件自身的 state 更新了,那么会依次执行 shouldComponentUpdate、componentWillUpdate 、
        render 和 componentDidUpdate。

      3. 须要注意的是,你不能在 componentWillUpdate 中执行 setState
      4. 若是组件是由父组件更新 props 而更新的,那么在 shouldComponentUpdate 以前会先执行 

        componentWillReceiveProps 方法。此方法能够做为 React 在 props 传入后,渲染以前 setState 的
        机会。在此方法中调用 setState 是不会二次渲染的。

  3.  React 与 DOM

    1.  ReactDOM
      1. findDOMNode

        1. DOM 真正被添加到 HTML 中的生命周期方法是 componentDidMount 和 componentDidUpdate 方法

        2. 假设要在当前组件加载完时获取当前 DOM,则可使用 findDOMNode:

          import React, { Component } from 'react'; 
          import ReactDOM from 'react-dom'; 
           
          class App extends Component { 
           componentDidMount() { 
           // this 为当前组件的实例 
           const dom = ReactDOM.findDOMNode(this); 
           } 
           
           render() {} 
          } 
        3. findDOMNode 只对已经挂载的组件有效。

      2. render

        1. 为何说只有在顶层组件咱们才不得不使用 ReactDOM 呢?这是由于要把 React 渲染的 Virtual DOM 渲染到浏览器的 DOM 当中,就要使用 render 方法了:
          1.  
            ReactComponent render( 
             ReactElement element, 
             DOMElement container, 
             [function callback] 
            ) 

             

          2. 该方法把元素挂载到 container 中,而且返回 element 的实例(即 refs 引用)。固然,若是是无状态组件,render 会返回 null。当组件装载完毕时,callback 就会被调用。

          3. 当组件在初次渲染以后再次更新时,React 不会把整个组件从新渲染一次,而会用它高效的 DOM diff 算法作局部的更新。这也是 React 最大的亮点之一!

      3. refs

  4. 事件系统
    1. 在 JSX 中,咱们必须使用驼峰的形式来书写事件的属性名(好比onClick),而 HTML 事件则须要使用所有小写的属性名(好比 onclick)。

    2. HTML 的属性值只能是 JavaScript 代码字符串,而在 JSX 中,props 的值则能够是任意类型,这里是一个函数指针。

    3. 在 React 底层,主要对合成事件作了两件事:事件委ี和自动绑定。

      1. 事件委派
        1. React并不会把事件处理函数直接绑定到真实的节点上,而是把全部事件绑定到结构的最外层,使用一个统一的事件监听器,这个事件监听器上维持了一个映射来保存全部组件内部的事件ᄢ听和处理函数。
        2. 当组件挂载或卸载时,只是在这个统一的事件ᄢ听器上插入或删除一些对象;当事件发生时,首先被这个统一的事件ᄢ听器处理,而后在映射里找到真正的事件处理函数并调用。这样作简化了事件处理和回收机制,效率也有很大提高。

      2. 自动绑定

        1. 在 React 组件中,每一个方法的上下文都会指向该组件的实例,即自动绑定 this 为当前组件
        2. 并且 React 还会对这种引用进行缓存,以达到 CPU 和内存的最优化
        3. 在使用 ES6 classes 或者纯函数时,这种自动绑定就不复存在了,咱们须要手动实现 this 的绑定。

          1. bind 方法

            1. import React, { Component } from 'react'; 
                 
              class App extends Component { 
               handleClick(e, arg) { 
               console.log(e, arg); 
               } 
               
               render() { 
               // 经过bind方法实现,能够传递参数 
               return <button onClick={this.handleClick.bind(this, 'test')}>Test</button>; 
               } 
              }
          2. 若是方法只绑定,不传参,那 stage 0 ᕘ案中提供了一个便૸的方案①——Ԥ冒号语法,其做用与 this.handleClick.bind(this) 一致,而且 Babel 已经实现了该提案。

            1. import React, { Component } from 'react';
              class App extends Component { 
               handleClick(e) { 
               console.log(e); 
               } 
               
               render() { 
               return <button onClick={::this.handleClick}>Test</button>; 
               } 
              }
          3. 构造器内声明
            1. 在组件的构造器内完成了 this 的绑定,这种绑定方式的好处在于仅须要进行一次绑定,而不须要每次调用事件ᄢ听器时去执行绑定操做:
              1. import React, { Component } from 'react'; 
                 
                class App extends Component { 
                 constructor(props) { 
                 super(props); 
                 
                 this.handleClick = this.handleClick.bind(this); 
                 } 
                 
                 handleClick(e) { 
                 console.log(e); 
                 } 
                 
                 render() { 
                 return <button onClick={this.handleClick}>Test</button>; 
                 } 
                } 
          4. 箭头函数
            1. 箭头函数不只是函数的“语法糖”,它还自动绑定了定义此函数做用域的 this,所以咱们不须要再对它使用 bind 方法。

              1. import React, { Component } from 'react'; 
                 
                class App extends Component { 
                 const handleClick = (e) => { 
                   console.log(e); 
                 }; 
                 
                 render() { 
                 return <button onClick={this.handleClick}>Test</button>; 
                 } 
                } 
                或 
                import React, { Component } from 'react'; 
                 
                class App extends Component { 
                 handleClick(e) { 
                 console.log(e); 
                 } 
                 
                 render() { 
                return <button onClick={() => this.handleClick()}>Test</button> 
                 } 
                }
          5. 在 React中使用原生事件

            1. componentDidMount 会在组件已经完成安装而且在浏览器中存在真实的 DOM 后调用,此时咱们就能够完成原生事件的绑定。

            2. 值得注意的是,在 React 中使用 DOM 原生事件时,必定要在组件卸载时手动移除,不然极可能出现内存泄漏的问题。而使用合成事件系统时则不须要,由于 React 内部已经帮你妥ؒ地处理了。

          6. 合成事件与原生事件混用

            1. 不要将合成事件与原生事件混用,如:
              1. componentDidMount() { 
                   document.body.addEventListener('click', e => { 
                        this.setState({ 
                            active: false, 
                         }); 
                    }); 
                 
                    document.querySelector('.code').addEventListener('click', e => { 
                       e.stopPropagation(); 
                     }) 
                } 
                 
                componentWillUnmount() { 
                 document.body.removeEventListener('click'); 
                 document.querySelector('.code').removeEventListener('click'); 
                }
            2. 经过 e.target判断来避免

              1. componentDidMount() { 
                 document.body.addEventListener('click', e => { 
                 if (e.target && e.target.matches('div.code')) { 
                 return; 
                 } 
                 
                 this.setState({ 
                 active: false, 
                 }); 
                 }); 
                }
          7. 对比React合成事件与JavaScript原生事件

            1. 事件传播与阻止事件传播

      3. 非受控组件
        1. 若是一个表单组件没有value props(单选按钮和复选框对应的是checked prop)时,就能够称为非受控组件。
        2. 非受控组件是一种反模式,它的值不受组件自身的state或props控制。一般要经过为其添加ref prop来访问渲染后的底层DOM元素
      4. 对比受控组件和非受控组件
        1. 受控组件
          1. <input   value={this.state.value}  
                 onChange={e => {     
                    this.setState({ value:           e.target.value.toUpperCase() 
                })   
                }} />
        2. 非受控组件

          1. <input   
                defaultValue={this.state.value}   
                onChange={e => {     
                this.setState({ value:     e.target.value.toUpperCase() })   }} />

              

        3. 若是不对受控组件绑定change事件,咱们在文本框中输入任何值都不会起做用。多数状况下,对于非受控组件,咱们并不须要提供change事件。
        4. 最大的区别:非受控组件的状态并不会受应用状态的控制,应用中也多了局部组件状态,而受控组件的值来自于组件的state.
    4. mixin:
      1. import React, { Component } from 'React'; 
        import { mixin } from 'core-decorators';
        const PureRender = { shouldComponentUpdate() {} }; const Theme = { setTheme() {} }; @mixin(PureRender, Theme) class MyComponent extends Component { render() {} }
      2. 这个mixin与createClass中的mixin的区别:
        1. 以前直接给对象的prototype属性赋值,但这里用了getOwnPropertyDescriptor 和defineProperty 这两个方法
        2. 这样实现的好处在于 defineProperty 这个方法,也就是定义与复制的区别,定义是对已有的定义,赋值则是覆盖已有的定义。
  5. 初探React 生命周期  
    1. 当首次挂载组件时,按顺序执行 getDefaultProps、getInitialState、componentWillMount、 render 和 componentDidMount。
    2. 当卸载组件时,执行 componentWillUnmount。
    3. 当从新挂载组件时,此时按顺序执行 getInitialState、componentWillMount、render 和 componentDidMount,但并不执行 getDefaultProps。
    4. 当再次渲染组件时,组件接受到更新状态,此时按顺序执行 componentWillReceiveProps、 shouldComponentUpdate、componentWillUpdate、render 和 componentDidUpdate。 
  6. 生命周期原理
    1.   
    2. 经过 mountComponent 挂载组件,初始化序号、标记等参数,判断是否为无状态组件,并进行对应的组件初始化工做,好比初始化 props、context 等参数。利用 getInitialState 获取初始化state、初始化更新对列和更新状态。
    3. 若存在 componentWillMount,则执行。若是此时在 componentWillMount 中调用 setState 方法,是不会触发 re-render的,而是会进行 state 合并,且 inst.state = this._processPendingState
      (inst.props, inst.context) 是在 componentWillMount 以后执行的,所以 componentWillMount 中的 this.state 并非最新的,在 render 中才能够获取更新后的 this.state。
    4. mountComponent 本质上是经过递归渲染内容的,因为递归的特性,父组件的componentWillMount 在其子组件的 componentWillMount 以前调用,而父组件的 componentDidMount在其子组件的 componentDidMount 以后调用。

    5. 禁止在 shouldComponentUpdate 和 componentWillUpdate 中调用 setState,这会形成循环调用,直至耗光浏览器内存后崩溃。
    6. 若是存在 componentWillUnmount,则执行并重置全部相关参数、更新队列以及更新状态,若是此时在 componentWillUnmount 中调用 setState,是不会触发 re-render 的,这是由于全部更新队列和更新状态都被重置为 null,并清除了公共类,完成了组件卸载操做。

  7. React diff 算法的 3 个策略
    1.  策略一: Web UI DOM 节点跨层级的移动操做特别少,能够忽略不计
    2.  策略二:拥有相同类的两个组件将会生成类似的树形结构,拥有不一样类的两个组件将会
      生成不一样的树形结构。 
    3.    策略三:对于同一层级的一组子节点,它们能够经过惟一 id 进行区分。
  8.  基于以上策略, React 分别对 tree diffcomponent diff 以及 element diff 进行算法优化。
    1.  tree diff
      1.  基于策略一, React 对树的算法进行了简洁明了的优化,即对树进行分层比较,两棵树只会
        对同一层次的节点进行比较
      2. 既然 DOM 节点跨层级的移动操做少到能够忽略不计,针对这一现象,React 经过 updateDepth
        Virtual DOM 树进行层级控制,只会对相同层级的 DOM 节点进行比较,即同一个父节点下的
        全部子节点。当发现节点已经不存在时,则该节点及其子节点会被彻底删除掉,不会用于进一步
        的比较。这样只须要对树进行一次遍历,便能完成整个 DOM 树的比较。
    2.  component diff
      1. 若是是同一类型的组件,按照原策略继续比较 Virtual DOM 树便可。
      2. 若是不是,则将该组件判断为 dirty component,从而替换整个组件下的全部子节点。
      3. 对于同一类型的组件,有可能其 Virtual DOM 没有任何变化,若是可以确切知道这点,那
        么就能够节省大量的 diff 运算时间。所以, React 允许用户经过 shouldComponentUpdate()
        来判断该组件是否须要进行 diff 算法分析。
    3.  element diff
      1.  当节点处于同一层级时, diff 提供了 3 种节点操做,分别为 INSERT_MARKUP(插入)、 MOVE_
        EXISTING(移动)和 REMOVE_NODE(删除)。
        1.    INSERT_MARKUP: 新的组件类型不在旧集合里,即全新的节点,须要对新节点执行插入操做。
        2.   MOVE_EXISTING:旧集合中有新组件类型,且 element 是可更新的类型, generateComponentChildren 已调用 receiveComponent,这种状况下 prevChild=nextChild,就须要作移动操做,能够复用之前的 DOM 节点。
        3.   REMOVE_NODE:旧组件类型,在新集合里也有,但对应的 element 不一样则不能直接复用和更
          新,须要执行删除操做,或者ே组件不在新集合里的,也须要执行删除操做。
  9.  React Virtual DOM 树转换成 actual DOM 树的最少操做的过程称为调和
相关文章
相关标签/搜索