要不要来看看浏览器视图更新的底层规则?

简介: 大家在操作原生的 DOM 的时候,有没有遇到过这样一个问题,就是我修改了 DOM 的属性,但是浏览器并没有立即更新视图,而我们开发者为了获取这次更新的结果,通常会使用 setTimeout 这样的方法

大家在操作原生的 DOM 的时候,有没有遇到过这样一个问题,就是我修改了 DOM 的属性,但是浏览器并没有立即更新视图,而我们开发者为了获取这次更新的结果,通常会使用 setTimeout 这样的方法来延迟一段时间,然后再去获取 DOM 的属性,那么大家有没有思考过为什么会有这个问题?

1. 浏览器的渲染机制

说到上面的问题,我们首先要了解一下浏览器的渲染机制,这个问题的答案就在这里。

先来看一张图,这个是浏览器调试工具中的性能卡,收集到性能分析数据后,可以在下边找到自下而上的 tab 选项,点击后可以看到浏览器渲染的过程。

image.png

这个就是一个白板页面,这里就是浏览器的整个渲染过程,从倒数第四个开始看,也就是解析完HTML后,就开始布局,然后重新计算样式,最后绘制页面,这个过程就是浏览器的渲染机制。

这里重要的就是布局和重新计算样式,布局和重新计算样式就是我们常说的回流和重绘,回流就是重新计算元素的位置和大小,重绘就是重新绘制元素的样式。

2. 布局

什么是布局,布局就是决定元素的位置和大小,这个过程是由浏览器自动完成的,我们只需要设置元素的样式,浏览器就会自动计算出元素的位置和大小,这个过程就是布局。

能影响到布局的属性有很多,我不一一列举,只要明白一点,就是影响到元素的位置和大小的属性,都会触发布局。

3. 重新计算样式

什么是重绘,重绘就是决定元素的样式,这个过程也是由浏览器自动完成的,我们只需要设置元素的样式,浏览器就会自动计算出元素的样式,这个过程就是重绘。

能影响到重绘的属性也有很多,就是影响到元素的样式的属性,都会触发重绘。

4. 实际问题

上面说了这么多,其实都是为了解决我们的问题的,上面两个操作都很影响性能,但是通常情况下,我们并不会遇到很多这种性能上的问题,所以这点性能上的消耗我们通常无视就好了。

虽然不会遇到性能上的问题,但是我们能遇到的问题就是,我们的页面会出现闪烁的情况,还有就是页面的渲染会出现卡顿的情况。

5. 闪烁

闪烁其实很常见,但是通常会被我们无视,因为一般只会出现一两次,而且会很快的恢复正常,所以我们会选择性的忽略它。

直接看代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>闪烁</title>
    <style>
        .box {
    
    
            width: 100px;
            height: 100px;
            background-color: red;
        }
    </style>
</head>
<body>
<div class="box"></div>
<script>
    setTimeout(function () {
    
    
        document.querySelector('.box').style.width = '100vw';
    }, 0);
</script>
</body>
</html>

就上面这个示例代码,你疯狂刷新页面,你就会发现页面会出现闪烁的情况,这个问题严重吗?不严重,但是我们要知道,这个问题是存在的。

如果你把代码中的setTimeout去掉,你就会发现,页面就不会出现闪烁的情况了,这个问题就不存在了。

这个问题等会再去解释。

6. 卡顿

卡顿是我们经常会遇到的问题,因为我们的页面中,有很多的动画,而且动画的执行时间是不确定的,所以我们会遇到卡顿的情况。

卡顿通常是由于js执行的时间过长,导致页面的渲染被阻塞了,所以页面就会出现卡顿的情况。

这个就不用我举例子了,直接在页面中写一个长一点的循环,你就会发现页面会出现卡顿的情况。

7. 问题出现的原因

为什么会出现这个问题呢?我们先来看一下代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>位置计算错误</title>
    <style>
        .box {
    
    
            background-color: red;
        }
    </style>
</head>
<body>
<span class="box"></span>
<script>
    var box = document.querySelector('.box');
    box.innerText = box.offsetWidth;
    const now = Date.now();
    while (Date.now() - now < 1000) {
    
    
    }
    box.innerText = 'hello';
</script>
</body>
</html>

这里你刷新页面会发现页面的内容直接是hello0就没有出现过,当然你这个时候去获取box.offsetWidth的值还是会发生变化的,因为这个时候box里面已经有内容了,所以宽度就变了。

如果你去打个debugger,你就会发现,然后逐行执行,你会神奇的发现,页面可以正常显示0,然后执行完成while循环,页面就会出现hello了,但是去掉debugger,页面就会出现hello0就没有出现过。

感觉很神奇吧,我们再次去掉debugger,然后在box.innerText = box.offsetWidth后面加上console.log(box.offsetWidth),你就会发现,控制台可以正确的输出值,但是页面就是不正确。

这个问题的原因就是因为绘制的任务是异步的,并不是你修改完某个属性之后,页面就会立即更新,而是等到下一次绘制的时候,才会去更新页面。

8. 问题的解决

上面的这些问题我们能解决吗?当然可以,我们可以通过requestAnimationFrame来解决。

requestAnimationFrame是一个浏览器提供的一个方法,它的作用是在下一次浏览器重绘之前执行回调函数,这样我们就可以在回调函数中去修改页面的内容,这样就不会出现上面的问题了。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>位置计算错误</title>
    <style>
        .box {
    
    
            background-color: red;
        }
    </style>
</head>
<body>
<span class="box"></span>
<script>
    requestAnimationFrame(() => {
    
    
        box.innerText = box.offsetWidth;
        requestAnimationFrame(() => {
    
    
            box.innerText = 'hello';
        });
    });
</script>
</body>
</html>

上的代码中,我们刷新页面,你会发现页面会出现闪烁,这就是因为0被正确渲染出来了,然后又被hello覆盖了。

这个只是演示,请不要在requestAnimationFrame中去做一些耗时的操作,因为这个方法是在下一次浏览器重绘之前执行,如果你在这个方法中做了一些耗时的操作,那么浏览器就会一直等待,直到这个方法执行完成,才会去重绘页面,这样就会导致页面出现卡顿的情况。

9. 总结

本文主要讲了一些关于浏览器渲染的一些问题,今天的分享就这么多,可能有点难以消化,这边建议多敲敲代码,多看看执行过程,多思考,多总结,这样才能更好的理解。

目录
相关文章
|
5月前
|
XML 编解码 JavaScript
从浏览器的解析规则认识XSS防御
从浏览器的解析规则认识XSS防御
66 2
|
3月前
|
JavaScript 前端开发 数据处理
模板字符串和普通字符串在浏览器和 Node.js 中的性能表现是否一致?
综上所述,模板字符串和普通字符串在浏览器和 Node.js 中的性能表现既有相似之处,也有不同之处。在实际应用中,需要根据具体的场景和性能需求来选择使用哪种字符串处理方式,以达到最佳的性能和开发效率。
126 63
|
3月前
|
JSON 移动开发 JavaScript
在浏览器执行js脚本的两种方式
【10月更文挑战第20天】本文介绍了在浏览器中执行HTTP请求的两种方式:`fetch`和`XMLHttpRequest`。`fetch`支持GET和POST请求,返回Promise对象,可以方便地处理异步操作。`XMLHttpRequest`则通过回调函数处理请求结果,适用于需要兼容旧浏览器的场景。文中还提供了具体的代码示例。
在浏览器执行js脚本的两种方式
|
3月前
|
算法 开发者
Moment.js库是如何处理不同浏览器的时间戳格式差异的?
总的来说,Moment.js 通过一系列的技术手段和策略,有效地处理了不同浏览器的时间戳格式差异,为开发者提供了一个稳定、可靠且易于使用的时间处理工具。
75 1
|
3月前
|
机器学习/深度学习 自然语言处理 前端开发
前端神经网络入门:Brain.js - 详细介绍和对比不同的实现 - CNN、RNN、DNN、FFNN -无需准备环境打开浏览器即可测试运行-支持WebGPU加速
本文介绍了如何使用 JavaScript 神经网络库 **Brain.js** 实现不同类型的神经网络,包括前馈神经网络(FFNN)、深度神经网络(DNN)和循环神经网络(RNN)。通过简单的示例和代码,帮助前端开发者快速入门并理解神经网络的基本概念。文章还对比了各类神经网络的特点和适用场景,并简要介绍了卷积神经网络(CNN)的替代方案。
675 1
|
4月前
|
机器学习/深度学习 自然语言处理 前端开发
前端大模型入门:Transformer.js 和 Xenova-引领浏览器端的机器学习变革
除了调用API接口使用Transformer技术,你是否想过在浏览器中运行大模型?Xenova团队推出的Transformer.js,基于JavaScript,让开发者能在浏览器中本地加载和执行预训练模型,无需依赖服务器。该库利用WebAssembly和WebGPU技术,大幅提升性能,尤其适合隐私保护、离线应用和低延迟交互场景。无论是NLP任务还是实时文本生成,Transformer.js都提供了强大支持,成为构建浏览器AI应用的核心工具。
993 1
|
5月前
|
JavaScript 前端开发
js之浏览器对象|28
js之浏览器对象|28
|
4月前
|
JavaScript API
深入解析JS中的visibilitychange事件:监听浏览器标签间切换的利器
深入解析JS中的visibilitychange事件:监听浏览器标签间切换的利器
254 0
|
6月前
|
机器学习/深度学习 人工智能 前端开发
【人工智能】利用TensorFlow.js在浏览器中实现一个基本的情感分析系统
使用TensorFlow.js在浏览器中进行情感分析是一个非常实用的应用场景。TensorFlow.js 是一个用于在JavaScript环境中训练和部署机器学习模型的库,使得开发者能够在客户端直接运行复杂的机器学习任务。对于情感分析,我们可以使用预先训练好的模型来识别文本中的积极、消极或中性情感。
173 4
【人工智能】利用TensorFlow.js在浏览器中实现一个基本的情感分析系统
|
6月前
|
机器学习/深度学习 存储 前端开发
实战揭秘:如何借助TensorFlow.js的强大力量,轻松将高效能的机器学习模型无缝集成到Web浏览器中,从而打造智能化的前端应用并优化用户体验
【8月更文挑战第31天】将机器学习模型集成到Web应用中,可让用户在浏览器内体验智能化功能。TensorFlow.js作为在客户端浏览器中运行的库,提供了强大支持。本文通过问答形式详细介绍如何使用TensorFlow.js将机器学习模型带入Web浏览器,并通过具体示例代码展示最佳实践。首先,需在HTML文件中引入TensorFlow.js库;接着,可通过加载预训练模型如MobileNet实现图像分类;然后,编写代码处理图像识别并显示结果;此外,还介绍了如何训练自定义模型及优化模型性能的方法,包括模型量化、剪枝和压缩等。
180 1

热门文章

最新文章