likes
comments
collection
share

浏览器的底层逻辑:代码是如何变成浏览器上的页面的?

作者站长头像
站长
· 阅读数 24

前言

大家都很熟练地掌握了浏览器页面的编写,能轻松的编写出想要的页面,实现想要的功能,为页面添加各种样式,但是大家知道我们编写的代码是如何变成浏览器上的页面的吗?

今天我就带大家认识一下浏览器的底层逻辑!!


浏览器中的引擎

浏览器之所以能够解析代码并显示成网页,是因为其中有两大引擎的协同运作:渲染引擎JavaScript引擎

渲染引擎

渲染引擎主要负责解析HTML、CSS和图片等资源,然后生成用户看到的可视化页面。

我们常见的渲染引擎有webkit(苹果公司发明,现用于Safari浏览器),Blink(相当于webkit升级版,主要用于chrome浏览器)和Gecko(用于Firefox浏览器)等。

工作流程

1.解析HTML: 渲染引擎会使用HTML解析器将HTML先解析为多个DOM节点,并通过栈结构将DOM节点添加到DOM树中

2.解析CSS: CSS解析器会解析CSS,并构建CSSOM树,上面包含着页面中各元素的样式信息。

3.构建渲染树: 渲染引擎将DOM树与CSSOM树进行匹配,为DOM树中的每个元素添加样式信息,并最终构建成渲染树(Render Tree)。这样渲染树上就包含了所有可视化元素和对应的样式信息。(不可见的元素不会出现在渲染树上)

4.计算页面布局: 渲染引擎通过渲染树计算出每个元素的位置和尺寸,其输出是一个盒模型。这个过程也被称为重排(或回流)

5.绘制页面: 布局计算完成后,渲染引擎就会遍历渲染树上的所有节点,并将其进行像素级别的绘制

6.合成页面: 有许多页面可能会有不同的图层(比如背景等),所以绘制结束后,这些图层会被合成为一张图像,并最终呈现在页面上。

JavaScript引擎

JavaScript引擎负责执行网页中的js代码。这包括解析、编译和运行JavaScript程序,以实现各种网页交互效果和复杂的功能。

我们常见的js引擎就是chrome和Node.js中的V8引擎,除此之外还有SpiderMonkey引擎(用于 Firefox)等。

工作流程

1.解析js代码: 当遇到 <script> 标签时,浏览器会暂停 HTML 的解析,JavaScript引擎开始介入解析 js 代码。

2.预编译: 在正式执行js代码之前,会进行一些预处理操作,包括变量的声明提升等。

3.编译: JavaScript引擎开始编译代码。

4.创建执行上下文: JavaScript引擎会创建全局执行上下文对象和函数执行上下文对象,每个执行上下文都有自己的变量环境。

5.出入调用栈: 调用栈用于管理执行上下文的执行顺序。当函数被调用时,该函数的执行上下文会被推入调用栈,当函数执行完毕后,该上下文从调用栈弹出。

6.内存管理: JavaScript引擎会使用垃圾回收机制识别并释放不再使用的内存,自动管理内存,包括分配和回收。


浏览器中的性能优化

了解完代码是如何变成浏览器中的页面之后,我们还要再了解一下如何对页面的显示进行性能优化。今天我们再讲一讲性能优化中较为关键的两个点:重绘(repain)回流(reflow)

重绘

什么是重绘?

在浏览器中,重绘(repaint)是指当页面元素的css属性(如颜色、背景、边框等)发生改变,但不影响布局时,浏览器重新绘制这些元素的过程。也就是说,只要元素的css属性发生改变,即使这些改变不会影响元素的尺寸和位置,也会触发重绘操作。

为什么重绘会影响性能?

其实单次甚至几次重绘不会消耗太多的资源,但是频繁多次的重绘就会造成大的性能开销(尤其是在比较复杂的页面),可能会使得页面变得不流畅,降低用户体验。并且重绘还有可能引发回流(马上我们会讲到),而回流对浏览器性能的影响更大。

如何避免重绘?

1.批量修改DOM元素: 当你需要修改多个DOM元素的样式时,尽量将这些操作放在一个函数调用中,而不是逐个修改。这样可以减少重绘次数。以下是代码示例:

function updateStyles(elements) {
    elements.forEach(element => {
        element.style.color = 'bule';
        element.style.backgroundColor = 'green';
    });
}

const elements = document.querySelectorAll('.my-elements');
updateStyles(elements);

2.使用CSS类: 将常用的样式组合定义为 CSS 类,通过添加或移除类来更改样式,而不是直接修改单个样式属性。以下是代码示例:

<style>
    .show{
        width: 100px;
        height: 200px;
        background-color: green;
    }
</style>
 <script>
    let container = document.getElementById('container');
    container.classList.add('show')
</script>

3.减少不必要的样式更改: 只有在真正需要时才更新元素的样式,以此避免在循环或高频率事件处理中频繁的修改样式。


回流

什么是回流?

在浏览器中,回流(reflow)是指浏览器重新计算和布局页面元素的过程。这个过程发生在浏览器首次渲染页面或在后续的任何时刻当页面的布局信息需要更新时,比如元素的尺寸、位置或布局属性发生变化时,元素被添加到或从DOM中删除时等场景,就会触发回流操作。

为什么回流会影响性能?

回流可能非常耗费性能,因为它涉及到对渲染树的重新构建和布局计算,如果页面中有大量的回流,尤其是在用户交互过程中,可能会导致页面渲染的延迟,降低用户体验。

如何避免回流?

1.缓存 DOM 访问: 由于DOM操作相对耗时,每次通过getElementById(), getElementsByClassName(), querySelector()等方法访问DOM元素时,浏览器都需要遍历DOM树来查找匹配的元素,这会消耗一定的CPU资源和时间。如果在代码中多次访问同一DOM元素,这种开销会累积起来,影响性能。所以在需要多次访问 DOM 元素时,我们可以将其引用缓存起来,避免重复的查找操作。以下是代码示例:

var cacheElement;

function cache() {
    cacheElement = document.getElementById('counter');
}

function updateCounter() {
    cacheElement.innerHTML = 'New value';
}

cache函数在初始化时查询DOM并缓存counter元素,之后updateCounter函数直接使用缓存的cacheElement变量,避免了重复查询DOM。

2.使用文档碎片批量添加或修改DOM:

  • 文档碎片(Document Fragment) 是一种特殊的节点,它可以像真实的DOM节点一样添加DOM属性,但只能用于暂时存储。当你将一些元素添加到文档碎片中,这些元素会在被暂时保存,之后你可以将整个文档碎片一次性添加到DOM树中。这样可以大大减少重绘和回流的次数,对浏览器的性能有很大提升。
//HTML代码
<ul id="list"></ul>

//JavaScript代码
const fragment = document.createDocumentFragment();
2for (let i = 0; i < 10; i++) {
3    const li = document.createElement('li');
4    li.textContent = `Item ${i + 1}`;
5    fragment.appendChild(li);
6}
7
8const list = document.getElementById('list');
9list.appendChild(fragment);

我们首先创建了一个文档碎片fragment,然后在循环中创建了10个<li>元素,并将它们逐个添加到文档碎片中。最后,我们将整个文档碎片添加到了<ul>元素中,这样就一次性完成了10个列表项的添加,而不需要进行10次DOM操作。

3.避免使用 table 布局: 因为 table 及其内部元素可能需要多次计算布局,对性能有一定影响。

4.使用绝对定位脱离文档流:

  • 对一些复杂的元素可以使用绝对定位,让其脱离文档流,避免引起父元素及后续元素的频繁回流。
  • 因为正常文档流是指元素按照它们在HTML代码中的先后顺序从上到下、从左到右排列的布局方式。当一个元素脱离了文档流,它就不再影响其他元素的位置,也不受其他文档流中元素的影响。这样即使后续要对某个复杂元素进行修改等操作,也不会影响到其他元素的位置,可以最大程度的减少重绘与重排的次数,从而提升性能。

总结

  • 渲染引擎JS引擎是浏览器中的两个核心引擎,这两个引擎共同协作,使得浏览器能够解析和展示网页内容,并执行其中的交互脚本。
  • 对浏览器显示的页面的性能进行优化是很有必要的,可以提高网页加载速度降低内存消耗增强用户体验
  • 性能优化最主要的就是避免重绘和回流

希望以上内容能对各位有帮助!!

转载自:https://juejin.cn/post/7388091090321375258
评论
请登录