← 返回目录

VDom一定更快吗

第 153 / 154 卷日期:

3、同一层级子节点,通过key 进行列表对比

标记key,React 可以直接移动 DOM节点,降低消耗

VDom一定更快吗

VDOM是JS对象,模拟DOM节点,是通过特定算法计算出一次操作所带来的DOM变化。react和vue中都使用了 VDOM

react中涉及到VDOM的代码主要分为三部分,第二步domDiff是核心:

把render中的JSX(或者createElement这个API)转化成VDOM 状态或属性改变后重新计算VDOM并生成补丁对象(domDiff)

通过补丁对象更新视图中的DOM节点

不一定更快

DOM操作是性能杀手,因为操作DOM会引起页面回流或重绘。相比,通过预先计算减少DOM的操作更划算

但是,“使用VDOM会更快”不一定适用所有场景。例如:一个页面就有一个按钮,点击一下,数字加一,肯定是直接操作DOM更快。使用VDOM无非白白增加计算量和代码量。即使是复杂情况,浏览器也会对我们的DOM操作进

行优化,大部分浏览器会根据我们操作的时间和次数进行批量处理,所以直接操作DOM也未必很慢

那为什么现在的框架都使用VDOM?因为VDOM可以提高代码的性能下限,并极大优化大量操作DOM时产生的性能损耗。同时也保证了,即使在少数VDOM不太给力的场景下,性能也在我们接受的范围内

Diff算法

react diff算法

React性能优化

思路

减少render执行次数减少渲染的节点

降低渲染计算量 VDOM

使用工具分析性能使用不可突变数据结构,数组使用concat,对象使用Object.assign()

组件尽可能拆分列表类组件优化

bind函数优化不滥用props

reactDOMServer服务端渲染组件

React.memo

做浅比较

第二个参数可传入自定义比较函数

areEqual方法和shouldComponentUpdate返回值相反

const App = React.memo(

function myApp(props) { //使用props渲染

}

function areEqual(prevProps, nextProps) { //如果把prevProps传入render方法的返回结果和将nextProps传入render的返回结果一样,则返回

true,否则返回false });

React.useMemo

缓存大量计算

useMemo 的第一个参数就是一个函数,这个函数返回的值会被缓存起来,同时这个值会作为 useMemo 的返回值,第二个参数是一个数组依赖,如果数组里面的值有变化,那么就会重新去执行第一个参数里面的函数,并将函

数返回的值缓存起来并作为 useMemo 的返回值。

// 避免这样做 function Component(props) {

const someProp = heavyCalculation(props.item);

return< AnotherComponent someProp = { someProp

} />

}

// 只有 `[props.item](http://props.item)` 改变时someProp的值才会被重新计算 function Component(props) {

const someProp = useMemo(() => heavyCalculation(props.item), [props.item]);

return< AnotherComponent someProp = { someProp

} />

}

State 的方式来实现了该函数。

shouldComponentUpdate函数中做的是“浅层比较”。若是“深层比较”,那时某个特定组件的行为,需要我们自己编写。

父组件状态的每次更新,都会导致子组件的重新渲染,即使是传入相同props。但是这里的重新渲染不是说会更新 DOM,而是每次都会调用diif算法来判断是否需要更新DOM。这对于大型组件例如组件树来说是非常消耗性能的。

shouldComponentUpdate生命周期来确保只有当组件props状态改变时才会重新渲染.

PureComponent会进行浅比较来判断组件是否应该重新渲染,对于传入的基本类型props,只要值相同,浅比较就会认为相同,对于传入的引用类型props,浅比较只会认为传入的props是不是同一个引用,如果不是,哪怕这

两个对象中的内容完全一样,也会被认为是不同的props。

PureComponent,因为进行浅比较也会花费时间,这种优化更适用于大型的展示组件上。大型组件也可以拆分成多个小组件,并使用memo来包裹小组件,也可以提升性能。

确保数据类型是值类型如果是引用类型,不应当有深层次的数据变化(解构)

避免使用内联对象

使用内联对象时,react会在每次渲染时重新创建对此对象的引用,这会导致接收此对象的组件将其视为不同的对象,因此,该组件对于prop的浅层比较始终返回false,导致组件一直重新渲染。

// Don't do this!

function Component(props) {

const aProp = { someProp: 'someValue' }

return <AnotherComponent style={{ margin: 0 }} aProp={aProp} />

}

// Do this instead :)

const styles = { margin: 0 };

function Component(props) {

const aProp = { someProp: 'someValue' }

return <AnotherComponent style={styles} {...aProp} />

}

// 避免这样做

function Component(props) {

return <AnotherComponent onChange={() => props.callback(props.id)} />

}

// 优化方法一

function Component(props) {

const handleChange = useCallback(() => props.callback(props.id), [props.id]);

return <AnotherComponent onChange={handleChange} />

}

// 优化方法二

class Component extends React.Component {

handleChange = () => {

this.props.callback(this.props.id)

}

render() {

return <AnotherComponent onChange={this.handleChange} />

}

}

组件懒加载

可以使用新的React.Lazy和React.Suspense轻松完成。

React.lazy

定义一个动态加载的组件,这可以直接缩减打包后 bundle 的体积,延迟加载在初次渲染时不需要渲染的组件

React.Suspense

悬挂终止暂停

const SettingDrawer = React.lazy(() => import('@/components/SettingDrawer'));

用 CSS

而不是强制加/卸载组件

渲染成本很高,尤其是在需要更改DOM时。此操作可能非常消耗性能并可能导致延迟

// 避免对大型的组件频繁对加载和卸载

function Component(props) {

const [view, setView] = useState('view1');

return view === 'view1' ? <SomeComponent /> : <AnotherComponent />

}

// 使用该方式提升性能和速度

const visibleStyles = { opacity: 1 };

const hiddenStyles = { opacity: 0 };

function Component(props) {

const [view, setView] = useState('view1');

return (

<React.Fragment>

<SomeComponent style={view === 'view1' ? visibleStyles : hiddenStyles}>

<AnotherComponent style={view !== 'view1' ? visibleStyles : hiddenStyles}>

</React.Fragment>

)

}

React.Fragment

聚合子元素列表,不在DOM中添加额外标签

<React.Fragment>

<h1>Hello world!</h1>

<h1>Hello there!</h1>

<h1>Hello there again!</h1>

</React.Fragment>

)

}

key

保证key具有唯一性 DIff算法根据key判断元素时新创建还是被移动的元素,从而减少不必要渲染

虽然key是一个prop,但是接受key的组件并不能读取到key的值,因为key和ref是React保留的两个特殊prop

合理使用Context

无需为每层组件手动添加 Props,通过provider接口在组件树间进行数据传递

原则

Context 中只定义被大多数组件所共用的属性

使用createContext创建一个上下文设置provider并通过value接口传递state数据

局部组件从value接口中传递的数据对象中获取读写接口

toggle——切换

import { createContext } from 'react';

const LoginContext = createContext();

export default LoginContext;

虚拟列表

合理设计组件

简化Props

简化State

全站分卷索引 · 154 卷
  1. 1代码随想录知识星球精华(最强八股文)第五版(前端篇))
  2. 2html语义化
  3. 3src 和 href 的区别
  4. 4link 与 @import 的区别
  5. 5iframe
  6. 6CSS
  7. 7伪元素和伪类
  8. 8block,inline,inline-block
  9. 9重排、重绘和合成
  10. 10盒子模型
  11. 11margin 合并
  12. 12margin和padding 的值为百分比时
  13. 13浮动
  14. 14定位
  15. 15CSS选择器
  16. 16CSS选择器优先级
  17. 17选择器权重Specificity
  18. 18CSS属性继承
  19. 19不可/可继承属性
  20. 20display: none与 visibility: hidden的区别
  21. 21background-size
  22. 22client、offset&scroll
  23. 23CSS3新特性
  24. 24flex布局
  25. 25常见布局
  26. 26三栏布局
  27. 27垂直居中
  28. 28水平垂直居中
  29. 29CSS画三角形
  30. 30使chrome支持12px以下文字
  31. 31响应式布局
  32. 32媒体查询
  33. 33过渡与动画
  34. 34JavaScript 动画 和 CSS动画
  35. 35SCSS
  36. 36CSS性能优化
  37. 37有哪些数据类型
  38. 38判断数据类型
  39. 39类型转换
  40. 40parseInt&parseFloat
  41. 41浮点数求和步骤
  42. 42十进制小数转二进制
  43. 430.1+0.2 为什么不等于 0.3
  44. 44undefined 和 undeclared 的区别
  45. 45JavaScript NaN 属性
  46. 46isFinite()&isNaN()
  47. 47数组
  48. 48对原数组有影响
  49. 49对原数组无影响
  50. 50数组去重
  51. 51数组拍平
  52. 52ES6新增的数组方法
  53. 53判断是否存在某个值
  54. 54arguments为啥不是数组
  55. 55对象
  56. 56遍历对象的方法
  57. 57判断对象是否具有属性
  58. 58for in 和 for of 的区别
  59. 59继承方式
  60. 60instanceof
  61. 61深浅拷贝
  62. 62freeze属性
  63. 63函数
  64. 64函数柯里化
  65. 65函数的 length
  66. 66箭头函数和普通函数区别
  67. 67构造函数
  68. 68执行上下文
  69. 69作用域
  70. 70作用域和作用域链
  71. 71闭包和内存泄漏
  72. 72this
  73. 73关于 this 的指向问题
  74. 74改变this指向的方法(bind / call / apply)
  75. 75字符串
  76. 76DOM和事件
  77. 77哪些事件支持冒泡
  78. 78阻止冒泡
  79. 79HTMLCollection、NodeList
  80. 80dom.onclick 和 dom.addEventListener的区别
  81. 81e.target和e.currentTarget
  82. 82页面生命周期
  83. 83异步
  84. 84内存管理GC
  85. 85其它
  86. 86JavaScript 中日期时间格式转换
  87. 87input和object实现双向绑定
  88. 88JSON.stringify()
  89. 89escape、encodeURI&encodeURIComponent
  90. 90移动端滚动穿透和溢出
  91. 91ES6
  92. 92Map字典
  93. 93... 与rest
  94. 94proxy
  95. 95模块化
  96. 96Ajax、Fetch和Axios
  97. 97Webpack
  98. 98浏览器:事件循环
  99. 99浏览器内核
  100. 100浏览器同源策略
  101. 101输入URL回车后发生了什么
  102. 102HTTP
  103. 103HTTP 2.0
  104. 104浏览器存储
  105. 105鉴权
  106. 106Webstorage
  107. 107浏览器执行机制
  108. 108AST 抽象语法树
  109. 109性能优化
  110. 110CSS
  111. 111网络相关
  112. 112使用HTTP2.0
  113. 113图片
  114. 114CDN图片
  115. 115其他文件优化
  116. 116降低APP首页开屏渲染时间
  117. 117页面白屏
  118. 118HTTPS中间人攻击
  119. 119Web Worker
  120. 120Service Worker
  121. 121微前端
  122. 122Vue
  123. 123v-show 和 v-if 的区别
  124. 124双向数据绑定的原理
  125. 125Vue中的插槽使用
  126. 126Vue中的key
  127. 127Vue过滤器
  128. 128Vue中Scoped原理
  129. 129Vue中$nextTick的使用
  130. 130Vue中实现组件通信的方式
  131. 131Vue-router
  132. 132Vue路由传参
  133. 133前端路由
  134. 134Vuex
  135. 135虚拟DOM
  136. 136DIFF 算法
  137. 137Vue3和Vue2有哪些区别
  138. 138说一说你对React的理解
  139. 139React事件机制
  140. 140说一说你对react refs的理解
  141. 141react中key的作用
  142. 142组件开发
  143. 143有状态组件和无状态组件
  144. 144函数式组件和类组件的区别
  145. 145PureComponent&Component
  146. 146常见 Hooks
  147. 147状态管理
  148. 148redux toolkit
  149. 149React18新特性
  150. 150client中的createRoot取代以前的ReactDOM.render
  151. 151Suspense
  152. 152新的Hooks
  153. 153VDom一定更快吗
  154. 154Fiber