马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
浏览器渲染原理与重排重绘机制
浏览器将HTML和CSS转换为用户可见页面的过程是前端开发的底子知识,也是明白DOM性能优化的关键。这个渲染过程大抵可分为以下几个步骤:
渲染过程的核心步骤
- 解析HTML构建DOM树:浏览器解析HTML标记,转换为DOM树(Document Object Model),表示文档的内容结构。
- 解析CSS构建CSSOM树:浏览器解析CSS标记,转换为CSSOM树(CSS Object Model),表示文档的样式规则。
- 归并DOM和CSSOM形成渲染树:浏览器将DOM树与CSSOM树结合,创建渲染树,只包含需要显示的节点及其样式。
- 布局(Layout/Reflow):浏览器计算渲染树中所有节点的几何信息(位置、大小)。
- 绘制(Paint):浏览器将每个节点绘制到屏幕上,应用视觉属性。
- 合成(Composite):将绘制的元素分层,再将各层合成为终极图像的过程。
重排(Reflow)与重绘(Repaint)
**重排(Reflow)**是计算页面布局的过程,当DOM元素的几何属性(如大小、位置)发生变革时触发。重排是一个计算密集型操作,会影响性能。
常见触发重排的操作:
- 添加/删除DOM元素
- 修改元素尺寸或位置(width, height, margin等)
- 浏览器窗口大小改变
- 获取特定的元素属性(offsetWidth, clientHeight等)
**重绘(Repaint)**是重新应用元素的视觉属性的过程,例如颜色、透明度等变革。重绘不涉及布局变革,性能消耗通常小于重排。
常见触发重绘的操作:
- 修改元素的颜色(color, background-color)
- 修改元素的可见性(visibility)
- 修改元素的透明度(opacity)
性能影响与优化原则
重排和重绘都会消耗体系资源,尤其是重排。一次重排通常会导致后续的重绘,因此重排对性能的影响更为明显。
关键优化原则:
- 尽量淘汰重排和重绘的次数
- 尽可能选择影响范围较小的操作
- 批量处理DOM操作
- 利用CSS硬件加速(transform, opacity等)
DocumentFragment批量DOM更新实践
DocumentFragment是一个轻量级的文档对象,它不是DOM树的一部分,因此对它的操作不会触发DOM树的重排和重绘,只有当它被添加到DOM树时才会触发一次更新。
DocumentFragment的优势
- 淘汰重排重绘次数:将多次DOM操作归并为一次
- 进步内存服从:不持有对DOM的引用,淘汰内存占用
- 改善性能:特别是在大量DOM节点操作时
实践案例:列表渲染优化
未优化版本:直接操作DOM,每次添加一个元素都会触发重排
- // 低效方式:直接操作DOM
- function renderListInefficient(data) {
-
- const container = document.getElementById('list-container');
-
- // 每次操作都会触发重排
- data.forEach(item => {
-
- const li = document.createElement('li');
- li.textContent = item.name;
- li.className = 'list-item';
- container.appendChild(li); // 每次都触发DOM更新
- });
- }
复制代码 优化版本:利用DocumentFragment批量处理
- // 高效方式:使用DocumentFragment
- function renderListEfficient(data) {
-
- const container = document.getElementById('list-container');
- const fragment = document.createDocumentFragment();
-
- // 在DocumentFragment中构建DOM结构
- data.forEach(item => {
-
- const li = document.createElement('li');
- li.textContent = item.name;
- li.className = 'list-item';
- fragment.appendChild(li); // 在内存中操作,不触发DOM更新
- });
-
- // 一次性将所有更改应用到DOM
- container.appendChild(fragment); // 只触发一次DOM更新
- }
复制代码 性能对比测试
在包含1000个列表项的页面上举行测试,利用DocumentFragment的版本比直接操作DOM的版本快约40-60%,具体数据如下:
- 直接操作DOM:约300-350ms
- 利用DocumentFragment:约120-180ms
实际应用场景
- 数据表格渲染:大型数据表格构建时
- 动态创建表单位素:批量创建多个表单字段
- HTML解析器:从字符串构建DOM结构
- 模板引擎:基于模板天生HTML内容
假造列表实现无穷滚动的高性能方案
对于包含大量数据的列表,一次性渲染所有条目会导致严肃性能题目。假造列表(Virtual List)技能只渲染可视区域内的条目,在滚动时动态更换内容,大幅提升性能。
假造列表的核心原理
- 只渲染可见项:只在DOM中创建可见区域内的元素
- 滚动时动态更换内容:监听滚动事件,计算可见区域,更新DOM
- 利用占位符维持滚动条:通过设置容器高度模仿完备列表
实现假造列表的基本步骤
- 计算可见区域内能显示的条目数量
- 监听滚动事件,计算应显示的数据片段
- 只渲染计算出的可见数据
- 设置内部容器高度,保持正确的滚动条比例
高性能假造列表实现代码
- class VirtualList {
-
- constructor(options) {
-
- this.container = options.container;
- this.data = options.data || [];
- this.itemHeight = options.itemHeight || 50;
- this.visibleItems = 0;
- this.startIndex = 0;
- this.endIndex = 0;
- this.scrollTop = 0;
-
- // 创建必要的DOM结构
- this.createDOMStructure();
-
- // 初始化和绑定事件
- this.init();
- this.bindEvents();
- }
-
- createDOMStructure() {
-
- // 设置容器样式
- this.container.style.position = 'relative';
- this.container.style.overflow = 'auto';
-
- // 创建内部容器,用于设置总高度
- this.innerContainer = document.createElement('div');
- this.innerContainer.style.position = 'relative';
-
- // 创建实际渲染可见项的容器
- this.itemsContainer = document.createElement('div');
- this.itemsContainer.style.position = 'absolute';
- this.itemsContainer.style.top = '0';
- this.itemsContainer.style.left = '0';
- this.itemsContainer.style.width = '100%';
-
- this.innerContainer.appendChild(this.itemsContainer);
- this.container.appendChild(this.innerContainer);
- }
-
- init() {
-
- // 计算可见区域能容纳的项目数
- this.visibleItems = Math.ceil(this.container.clientHeight / this.itemHeight) + 2; // 额外渲染2个做缓冲
-
- // 设置内部容器总高度
- this.innerContainer.style.height = `
复制代码 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |