# 1. 高阶组件与 mixin

高阶组件属于函数式编程(functional programming)思想,对于被包裹的组件时不会感知到高阶组件的存在,而高阶组件返回的组件会在原来的组件之上具有功能增强的效果。而 Mixin 这种混入的模式,会给组件不断增加新的方法和属性,组件本身不仅可以感知,甚至需要做相关的处理(例如命名冲突、状态维护),一旦混入的模块变多时,整个组件就变的难以维护,也就是为什么如此多的 React 库都采用高阶组件的方式进行开发。

  • 属性代理: 操作 props,父级处理完 props,传给包裹组件
  • 反向继承:渲染劫持:返回的高阶组件继承原组件,再高阶组件中根据条件,去反向执行继承的原组件的 render 方法。

# 2. jsx

  • 0 还是会被渲染,必须是布尔类型
    <div>
      {props.messages.length > 0 &&
        <MessageList messages={props.messages} />
      }
    </div>
    
  • 反之,如果你想渲染 false、true、null、undefined 等值,你需要先将它们转换为字符串:
    <div>
      My JavaScript variable is {String(myVariable)}.
    </div>
    
  • 实现原理: jsx---> 经过 babel 编译和 react 构造得到(js 对象结构)----> React.dom 得到(dom 元素)----->最后插入到页面 jsx实现原理 **好处:**当数据变化,需要更新组件的时候,就可以用比较快的算法操作这个 JavaScript 对象,而不用直接操作页面上的 DOM,这样可以尽量少的减少浏览器重排,极大地优化性能

# 3. diff 算法和虚拟 dom

  • 虚拟 dom 虚拟dom1 虚拟dom2

  • 为什么要有虚拟 dom

    • 不操作 dom
    • 跨平台
  • 虚拟 dom 步骤

    • 用 js 对象结构表示 dom 数的结构,然后用这个树构建一个真正的 dom 树,插入到文档当中
    • 当状态变更的时候,重新构造一颗新的对象树,然后用新的树与旧的树进行比较,记录两棵树差异
    • 把 2 步所记录的差异应用到步骤 1 所构建的真正的 dom 树,试图就更新了
  • diff 算法

    • Tree Diff: 将新旧两颗虚拟 DOM 树,按照层级对应的关系,从头到尾的遍历一遍,,就能找到那些元素是需要更新的,这种方式: Tree Diff diff值tree 执行过程:create A -> create B -> create C -> delete A
    • Component Diff: 对比每一个层级的时候,会有自己的组件,这种组件的对比方式就叫: Component Diff ;
      • 如果类型相同,暂时不更新,
      • 如果类型不相同,就需要更新; ( 删除旧的组件,再创建一个新的组件,插入到删除组件的那个位置) diff值component 执行过程:delete D -> create G
  • Element Diff: 在类型相同的组件内, 再继续对比组件内部的元素,查看内部元素是否相同,如果需要修改,找到需要修改的元素,进行针对性的修改!

    • 不加 key 时 diff值element 执行过程:B != A,则创建并插入 B,删除 A;以此类推,创建并插入 A、D、C,删除 B、C、D
    • 加 key 时 diff值element 执行过程:B、D 不做任何操作,A、C 进行移动操作

# 4. ref

  • 创建 ref Refs 是使用 React.createRef() 创建的,并通过 ref 属性附加到 React 元素。在构造组件时,通常将 Refs 分配给实例属性,以便可以在整个组件中引用它们。

    class MyComponent extends React.Component {
      constructor(props) {
        super(props);
        this.myRef = React.createRef();
      }
      componentDidMount() {
        //组建中通过this.myRef.current获取实例
        this.myRef.current.focusTextInput();
      }
      render() {
        return <div ref={this.myRef} />;
      }
    }
    
  • ref 的值根据节点的类型而有所不同:

    • 当 ref 属性用于 HTML 元素时,构造函数中使用 React.createRef() 创建的 ref 接收底层 DOM 元素作为其 current 属性。
    • 当 ref 属性用于自定义 class 组件时,ref 对象接收组件的挂载实例作为其 current 属性。
    • 你不能在函数组件上使用 ref 属性,因为他们没有实例
  • 将 DOM Refs 暴露给父组件 Ref 转发是一个可选特性,其允许某些组件接收 ref,并将其向下传递(换句话说,“转发”它)给子组件。

    //子组件
    const FancyButton = React.forwardRef((props, ref) => (
      <button ref={ref} className="FancyButton">
        {props.children}
      </button>
    ));
    
    // 父组件,你可以直接获取 DOM button 的 ref:
    const ref = React.createRef();
    <FancyButton ref={ref}>Click me!</FancyButton>;
    
  • string 的 ref 的弊端

    • 当 ref 定义为 string 时,需要 React 追踪当前正在渲染的组件
    • string ref 无法被组合,例如一个第三方库的父组件已经给子组件传递了 ref,那么我们就无法再在子组件上添加 ref 了,而 callback ref 可完美解决此问题。

# 5. Component 和 PureComponent

一、React.Component 和 React.PureComponent 很相似,两则的区别在于,

  1. PureComponent 类帮我们以浅比较的方式对比 props 和 state,实现了 shouldComponentUpdate()函数,进行浅比较值没变(对象和数组引用没变)的情况下,组件是不会更新的。
  2. 在某些情况下,使用 PureComponent 可以减少 render 函数的执行,提升性能。

# 6. setState 原理

  1. setState 批量更新的过程

    TIP

    在 react 生命周期和合成事件执行前后都有相应的钩子,分别是 pre 钩子和 post 钩子,pre 钩子会调用 batchedUpdate 方法将 isBatchingUpdates 变量置为 true,开启批量更新,而 post 钩子会将 isBatchingUpdates 置为 false

    TIP

    enqueueUpdate 包含了 React 避免重复 render 的逻辑。mountComponent 和 updateComponent 方法在执行的最开始,会调用到 batchedUpdates 进行批处理更新,此时会将 isBatchingUpdates 设置为 true,也就是将状态标记为现在正处于更新阶段了。 isBatchingUpdates 为 true,则把当前组件(即调用了 setState 的组件)放入 dirtyComponents 数组中;否则 batchUpdate 所有队列中的更新

# 参考

diff 虚拟 dom 高阶 setState