React踩坑笔记 —— 差分算法(一)

概要

  • 《React踩坑笔记 —— 差分算法(二)》

React提供了声明式的API,以致于咱们不须要担忧每次更新具体发生了什么更改。这使得咱们开发应用变得很容易,但始终没法清楚React内部是如何实现的。本文解释了在 “差分算法” 中如何作出选择,使得组件更新,在足够快的高性能应用中仍然能够预测。html

在理解 “差分算法” 以前,首先咱们须要去理解:node

  • document与Dom nodes的关系;
  • document与react组件的关系;
  • react组件与react元素树的关系;
  • render()函数的做用与生命周期;
  • ReactDOM.render()函数的做用;

Document与React元素树

JSX代码

import React from "react";
import ReactDOM from 'react-dom';

const First = () => (<div> <h1>第一棵树</h1> <div>附加信息</div> </div>);

const Second = () => (<div> <h1>第二棵树</h1> <Third /> </div>);

const Third = () => (<div> <h1>第三棵树</h1> <div>附加信息</div> </div>);

const App = () => (<div> <First /> <Second /> </div>);

ReactDOM.render(<App />, document.getElementById('root')); 复制代码

图示

在这里插入图片描述

说明

万变不离其宗,对于一个HTML页面,documentNode永远会是它的基础组成部分(实际上document也只是一个特别的Node),若是把document看作一张纸,那么Node能够当作点缀在纸上的文字、图片或符号。react

在React中,React组件是由真实的Dom Node组成,最终会被转化为Dom Node并在document中渲染出来(事件\样式\选择器,只属于真实的DOM Node)。算法

当你使用React的时候,你能够认为:浏览器

  • 每个React组件都对应一棵React元素树
  • 或者说,在某一时间点上每个render()函数都建立了一棵React元素树
  • 这里的render()函数,见下文。
  • 每个React组件能够由一个或多个React组件以及HTMLElement组成,便是 —— 每一棵树均可以做为另外一棵树的子树,正如上图所示:<Third /><Second />的子树,<Second /><First />又都是<App />的子树。
  • 在上图中,<App />组件所对应的React元素树,是最大的一棵树。经过API ——ReactDOM.render(element, container[, callback])将这棵树挂载到容器(container)中。
  • 所谓容器(container),不过是document中某个指定的Div或其它块级元素
  • 因此,不管经过JavaScript选择器仍是dom操做插件仍是React提供的Refs,对Dom node的操做都会被保留在这个document中,并在组件stateprops发生改变的时候参与 “差分算法”
  • 因此,若是当前页面被刷新,那么原来的document也不复存在:
    • React组件会被从新挂载
    • Dom node会被从新插入
    • 若是你想保留某些状态,你须要高于页面的存储方式,例如sessionStorage( 标签页 localStorage( 浏览器 cookie( 浏览器 Web SQL( 浏览器 服务器数据( 任何地方
  • 路由(Router),只会引发某些组件的卸载或挂载,而不会颠覆整个document。

render()

对于Function组件(无状态组件)就是自身,对于Class组件(有状态组件)就是从React.Component继承的render()方法。服务器

返回值类型

render()被调用时,能够依赖propsstate来构造返回值,其返回值类型包括以下:cookie

  • React elements. 典型地经过JSX语法建立,例如<div /><MyComponent />
  • Arrays and fragments. 因为 “差分算法” ,React要求render()必须返回单元素(single),React提供了<></><React.Fragment>容许开发者从render()中返回多元素
  • Portals. 有时咱们须要将子组件渲染到父组件外的某个Dom node上,例如对话框、悬浮卡、提示信息。Portals 为咱们都提供了首选方式。
  • String and numbers. 字符串和数字被以文本节点(Text nodes)的形式渲染到Dom中
  • Booleans or null. 返回Null表示什么都不作。 (大多数状况是返回 test && <Child /> ,这里test 是布尔值(boolean)

纯函数

render()应该是一个纯函数,返回值只依赖于stateprops,而且不会产生反作用,反作用包括:网络

  • 修改stateprops
  • 调用非纯函数。如 Date.now()Math.random()
  • 网络请求,路由跳转
  • 浏览器交互。setTimeout()console.log()

若是你须要浏览器交互网络请求,能够选择在componentDidMount()其它合适的声明周期函数中执行。有些条件没有作硬性要求,可是保证render()做为纯函数,可以提升组件更新的性能。session

生命周期

组件在挂载(Mounting)和更新(Updating)时,render()都会被执行,并产生一棵新的React元素树。声明周期执行顺序以下。详情可见官方文档《component-lifecycle》dom

Mounting

render()会产生一棵新的树,这颗新树会被直接挂载到父组件对应的那棵树上。

  • constructor()
  • static getDerivedStateFromProps()
  • UNSAFE_componentWillMount()
  • render()
  • componentDidMount()

Updating

render()会产生一棵新的树,而后与先前的那棵旧树,执行 “ 差分算法” 更新UI(用户界面)。

  • static getDerivedStateFromProps()
  • shouldComponentUpdate()
  • UNSAFE_componentWillUpdate()
  • render()
  • getSnapshotBeforeUpdate()
  • componentDidUpdate()

ReactDOM.render()

ReactDOM.render(element, container[, callback])
复制代码

该方法将整个React元素树渲染到document中指定的<Div />容器中,并返回根组件(对应最大元素树)的引用(Function 组件 —— 无状态组件,没有实例(Instance)因此返回null)。

若是这个React元素(根组件)先前已经被渲染到容器中,那么将对它执行更新,按照 “差分算法” 去呈现它。

若是这个可选的参数—— 回调函数,被提供。那么会在组件被渲染更新后调用。

Note

  • ReactDOM.render()控制着容器(container)节点的内容,第一次调用时,任何存在的Dom元素都会被替换,以后再调用会使用React Dom diffing algorithm(差分算法)进行高效更新。
  • ReactDOM.render()不修改容器节点(只修改容器的子节点)。
  • ReactDOM.render() 当前返回根组件(root ReactComponent )实例的引用。然而,返回值的使用已经被遗留了,应该避免去使用它,由于在将来的React版本中,在某些状况下React会采用异步的方式去渲染组件。若是你真的须要引用根组件实例,首选解决方案是为你的根组件添加《callback ref》
  • 另外,使用ReactDOM.render()去融合服务端渲染容器(server-rendered container)已经被遗弃了,并将在React 17被移除,代替使用hydrate()
相关文章
相关标签/搜索