React是一种流行的构建用户界面的JavaScript库。它让用户定义想要的应用程序的外观和行为,都是用JavaScript实现的。
将应用定义为可组合组件的树型结构,这些组件是React的输入。树型结构定义了应用程序的结构,而外观由应用于这些树组件的各种属性控制,行为由各种回调函数控制,这些函数在用户输入或组件生命周期的某个阶段触发。树型结构的叶子表示要显示的实际HTML元素。
这棵树本质上是
React的createElement函数的一个巨大的嵌套调用
。此函数的第一个参数是要使用的节点的类型,这可以是我们自己定义的组件,也可以是具有特定HTML元素的叶子节点。第二个参数是要发送到此组件的属性,第三个参数指定树中组件节点的子级。
这个接口让组件非常容易组合,因为可以非常动态地传递数据,回调甚至其他组件(树中的父组件到子组件)。这使得React可以表达许多有用的设计模式。
为了节省手动输入并使语法更符合实际的HTML,许多人使用称为JSX的语法糖,这使得函数调用更加紧凑。
React 围绕浏览器文档对象模型或DOM进行包装,DOM是一种所有Web浏览器中都存在的重要的API。
此API允许我们以编程的方式更改页面的内容。
它将HTML页表示为具有属性、参数、事件处理程序的节点树,并提供用于查询、创建新元素、在树中追加新子级等的方法。React的组件树也有类似的结构。
React作为预期状态系统
让我们通过期望状态的镜头来看看React。当页面加载时,一些初始预期状态作为组件树的形式给到React。在内部,React在内存中保留此树的表示形式,每当预期状态发生变更时——基于用户输入或其他触发器——它都会将旧状态与新状态进行比较。这种内部表示形式过去被称为虚拟DOM,现在这个名称已经用的不多了。
状态的比较生成了一系列需要在实际DOM上执行的操作。用于生成将一个树转换为另一个树所需的最小操作数的通用算法具有O(n^3)级的复杂性,其中n是树中的组件数。然而,React必须非常快速地做到这一点,因此
它使用一系列启发式方法来计算所需的最少操作数
。
这将时间复杂度降低到O(n)。这些启发式方法在很大程度上依赖于两个假设:首先,两个不同类型的元素将产生不同的树;其次,开发人员可以使用称为key的特殊属性来提示哪些子元素在渲染中可能无需更改。对于列表和其他顺序很重要的地方,这是必需的。
React还依赖于传递给子级的属性是不可变的事实
——它假设当对象的内容发生变化时,对该对象的引用也会发生变化。然后,React可以执行简单的引用比较,而无需执行深度比较,并在组件属性更改时重新渲染组件。这让UI保持可响应。
我之所以强调这一点,是因为状态的结构以及比较它的两个版本时如何改变它的难度,可能是构建理想的预期状态系统的关键部分之一。
React计算所需最少操作的原因是,DOM上的操作非常慢。但是,整个比较机制是实现的细节,如果它是高性能的,React可以选择每次都从头开始重建整个树。
现在我想说,根据我们正在使用的模型,React是一个简化的预期状态系统。让我离题介绍一下控制理论的基础知识。
控制理论:闭环系统 vs 开环系统
闭环系统是在一个循环中相互连接的系统。如果系统1向系统2发送信号,则系统2的输出在某种程度上又是系统1输入的一部分。这称为反馈。
反馈的一个关键特征是为不确定性提供了鲁棒性。
闭环系统通过将所需的输出条件与实际条件进行比较,自动实现并保持预期的输出条件。
虽然反馈有很多优点,但它也带来了一系列缺点。如果设计不当,系统可能会表现不稳定。这可能是正反馈的形式,例如当麦克风的放大器在房间中调得太高时。此外,反馈本质上也耦合了系统的不同部分[4]。
另一方面,在开环系统中,这种互连被切断。
React是开环系统
虽然React是模型中的预期状态系统,但它实际上是一个开环系统。React不会不断重新检查浏览器DOM的当前状态,以确定它是否正确。
首先,这可能会非常慢。它其实也不需要这么做。与许多其他预期状态系统不同,React的运行假设是,只有它能够操作目标域。它通常假设没有其他库函数或人绕过它去修改页面。
你可以使用浏览器中的开发工具自行测试。如果修改了由React控制的HTML元素,则库函数不会尝试覆盖你的修改,除非发生变更的元素的父元素被重新渲染并替换了整个子树。
React的一个好处是它是以模块化的方式构建的
。
与DOM通信的部分是一个名为ReactDOM的单独模块,可以替换为不同的渲染目标,React称之为主机(host)。例如,在移动应用程序框架React Native中使用了不同的主机。
在编写React Native时,你仍然将应用的用户界面指定为组件树。然而,React不是与DOM对话,而是与移动操作系统的原生API交互。使用自定义的渲染目标来扩展 React 也相对容易。
React的API最近添加了Hooks。这是一种有趣的设计模式,允许开发人员以声明性的方式管理组件中的状态和副作用。建议大家阅读Dan Abramov的博客文章《使用 React Hooks 让setInterval变成声明式[5]》,他在其中用声明式的hook包装了一个固有的命令式API,即浏览器的setInterval方法。
让我们进一步研究些别的。