当使用React编写应用程序时,可以使用JSX语法来描述用户界面的结构。JSX是一种类似于HTML的语法,但实际上它是一种JavaScript的扩展,用于定义React元素。React元素描述了我们想要在界面上看到的内容和结构。
在运行React应用程序时,JSX会被转换成真实的DOM元素,这个过程主要涉及以下几个步骤:
JSX代码编写: 你使用JSX语法编写React组件的界面结构。JSX允许你使用类似HTML的标记来描述UI层次结构,例如:
const element = Hello, React!;
Babel转换: JSX语法并不是浏览器原生支持的,所以需要使用工具将JSX代码转换为浏览器可识别的JavaScript代码。通常会使用Babel这样的工具来进行这种转换。
在上述示例中,JSX代码会被转换成类似以下的形式:
const element = React.createElement("div", null, "Hello, React!");
React.createElement 是一个用于创建React元素的函数,它接受标签名、属性和子元素作为参数。
创建虚拟DOM: 在转换后的JavaScript代码中,React.createElement 函数会创建一个称为虚拟DOM的JavaScript对象。虚拟DOM是React内部用来表示界面结构的一种轻量级表示形式。
虚拟DOM对比: 当状态发生变化导致界面需要更新时,React会使用虚拟DOM来比较前后两次状态的差异。这个过程被称为"协调。
生成真实DOM更新: 通过协调过程,React能够计算出哪些部分的DOM需要被更新。然后,它将针对实际需要更新的部分,生成一系列DOM操作,然后将其应用于浏览器的实际DOM上。这个过程是优化的,React会尽量最小化实际DOM操作的次数,以提高性能。
真实DOM: 真实DOM是浏览器实际生成的文档对象模型,它代表了网页的实际结构,由HTML元素组成,每个元素都有对应的属性和方法。当应用程序的状态发生变化时,直接操作真实DOM会导致浏览器进行重绘和重排,这是一种消耗性能的操作。更新真实DOM的频繁操作可能会导致性能问题,因为DOM操作是相对昂贵的操作,特别是在有大量元素需要更新时。
优点: 真实DOM是浏览器的基础,它可以完全呈现应用程序的状态,对于一些复杂的应用场景,真实DOM可能更能够准确地反映UI状态。
虚拟DOM: 虚拟DOM是一个轻量级的JavaScript对象,它是对真实DOM的抽象表示。它类似于真实DOM,但不是实际的网页结构,而是一个虚拟的副本。 当应用程序的状态发生变化时,React等库会首先在虚拟DOM上进行操作,然后通过比较前后两个虚拟DOM的差异,找出需要更新的部分,最终只会对真实DOM进行最小化的操作。
优点: 虚拟DOM的更新机制减少了对真实DOM的频繁操作,从而提高了性能。通过批量更新、合并操作等方式,可以有效减少重绘和重排的次数。
优缺点对比:
真实DOM优点:
真实DOM 缺点:
虚拟DOM 优点:
虚拟DOM 缺点:
相信在平时的开发中遇到过的警告warning: Each child in a list should have react.development.js:315 a unique "key" prop 。这就是在循环渲染的时候,如果忘记给元素加上key,浏览器给出警告。那么key到底有什么作用呢?
可以使用index作为key吗
绝大部分情况下使用 index 作为 key值,对性能没有优化。因为它可能导致性能问题和不稳定的渲染结果。
性能问题: 使用索引作为 key 可能导致不必要的重绘和性能问题。当列表中的元素发生变化时,React可能会错误地认为相同索引处的元素没有变化,从而无法正确更新。
列表重排序问题: 当列表中的元素发生重新排序时,如果仅仅依赖于索引作为 key,可能会导致元素混乱,不符合预期。
动态添加和删除问题: 在列表中动态添加或删除元素时,使用索引作为 key 可能导致React无法准确识别新增和删除的元素,从而导致错误的渲染。
不稳定的渲染结果: 使用索引作为 key 可能导致不稳定的渲染结果。不同的渲染顺序可能会导致相同的数据在不同索引处渲染,从而引起错误。
使用合适的组件: 将应用划分为合适的组件,每个组件关注特定功能。避免过大的组件,因为较小的组件更新更快。
使用PureComponent和React.memo: 使用PureComponent和React.memo来避免不必要的组件重新渲染,它们会在props或state没有变化时阻止不必要的渲染。
避免不必要的渲染: 通过使用shouldComponentUpdate生命周期方法或者函数组件的React.memo来控制组件是否需要重新渲染。
列表性能优化: 对于大型列表,使用列表虚拟化库(如react-virtualized、react-window、react-infinite-scroll-component)来仅渲染可见部分的数据,减少渲染开销。
使用key属性: 在渲染列表时,为每个子元素提供唯一的key属性,以帮助React准确地识别变化。
异步渲染: 使用React.lazy和Suspense实现组件的异步加载,从而减少初始加载时的资源消耗。
使用Memoization: 在函数组件中,使用useMemo和useCallback来避免不必要的函数重新计算,减少渲染开销。
使用Immutability: 使用不可变数据结构来更新状态,以帮助React检测状态变化。这可以通过展开运算符、Object.assign、Array.concat等方式实现。
组件拆分和懒加载: 将大型组件拆分成更小的子组件,并使用动态导入和代码分割来实现按需加载。
使用Profiler组件: React提供的组件可以帮助你识别渲染性能问题,并分析组件的渲染时间和调用次数。