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

版权声明:本文为博主原创文章,可以转载不可复制。 https://blog.csdn.net/qq_32331073/article/details/85108015


概要

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

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

在理解 “差分算法” 之前,首先我们需要去理解:

  • 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组件是由真实的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来构造返回值,其返回值类型包括如下:

  • 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()作为纯函数,能够提高组件更新的性能。

生命周期

组件在挂载(Mounting)和更新(Updating)时,render()都会被执行,并产生一棵新的React元素树,然后执行 “ 差分算法” 更新UI(用户界面)。声明周期执行顺序如下。详情可见官方文档《component-lifecycle》

Mounting

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

Updating

  • 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()

猜你喜欢

转载自blog.csdn.net/qq_32331073/article/details/85108015
今日推荐