头图

⭐⭐浏览器原理 之 页面形成的原理和性能优化篇

原本地址

掘金

github

大家好,我是林一一,这是一篇关于浏览器是如何渲染一个页面形成的原理,话不多说,直接开始阅读吧👍

001 浏览器的底层渲染页面篇

浏览器中的5个进程
浏览器进程.jpg

浏览器在获取服务器的资源后将 html 解析成 DOM 树,CSS 计算成 CSSOM 树,将两者合成 render tree。具体如下浏览器根据 render tree 布局生成一个页面。需要理解的是浏览器从服务器获取回来的资源是一个个的字节码3C 6F 6E 62 ....等,浏览器会按照一套规范W3C将字节码最后解析一个个的代码字符串才成为我们看到的代码

浏览器加载资源的机制

  • 浏览器会开辟一个 GUI 渲染线程,自上而下执行代码,专门用于渲染渲染页面的线程。

遇到 CSS 资源

  • 遇到 <style> 内联标签会交给 GUI 渲染线程解析,但是遇到 <link> 标签会异步处理,浏览器会开辟一个 HTTP 请求处理的线程,GUI 渲染线程继续往下执行
  • 如果遇到@import 时,也会开辟一个新的 HTTP 请求线程处理,由于 @import 是同步的 GUI 渲染线程会阻塞等待请求的结果。

    需要注意 chrome 中,同一个源下,最多同时开辟 6-7 和 HTTP 请求线程。

遇到 JS 资源

GUI渲染遇到script.jpg

最底部的线表示 GUI 线程的过程,渲染线程遇到不同情况下的script资源,有不同的处理。
  • 遇到 <script></script> 资源,默认是同步的。 此时 GUI 渲染线程会阻塞。等待 JS 渲染线程渲染结束后,GUI 线程才会继续渲染。
  • 如果遇到 <script async></script> 那么资源是异步的 async,浏览器也会开辟一个 HTTP 请求线程加载资源,这时 GUI 渲染线程会继续向下渲染,请求的资源回来后 JS 渲染线程开始执行,GUI 线程再次被阻塞。
  • 如果遇到 <script defer></script> 和 async 类似都会开辟一个新的HTTP线程,GUI 继续渲染。和 async 不一样的地方在于,defer 请求回来的资源需要等待 GUI 同步的代码执行结束后才执行 defer 请求回来的代码。

    async 不存在资源的依赖关系先请求回来的先执行。defer 需要等待所有的资源请求回来以后,按照导入的顺序/依赖关系依次执行。

图片或音频

  • 遇到 <img/> 异步,也会开辟一个新的 HTTP 线程请求资源。GUI 继续渲染,当 GUI 渲染结束后,才会处理请求的资源。

需要注意的是:假设某些资源加载很慢,浏览器会忽略这些资源接着渲染后面的代码,在chrome浏览器中会先使用预加载器html-repload-scanner先扫描节点中的 src,link等先进行预加载,避免了资源加载的时间

浏览解析资源的机制

  • 浏览器是怎样解析加载回来的资源文件的? 页面自上而下渲染时会确定一个 DOM树CSSOM树,最后 DOM树CSSOM树 会被合并成 render 树,这些所谓的树其实都是js对象,用js对象来表示节点,样式,节点和样式之间的关系。

DOM 树

所谓的 DOM 树是确定好节点之间的父子、兄弟关系。这是 GUI 渲染线程自上而下渲染结束后生成的,等到 CSS 资源请求回来以后会生成 CSSOM 样式树。

DOM树.jpg

CSSOM 树

CSSOM(CSS Object Model), CSS 资源加载回来以后会被 GUI 渲染成 样式树

样式树.jpg

Render tree 渲染树

浏览器根据 Render tree 渲染页面需要经历下面几个步骤。注意 display:node 的节点不会被渲染到 render tree 中

renderTree.jpg

  • layout 布局,根据渲染树 计算出节点在设备中的位置和大小
  • 分层处理。按照层级定位分层处理
  • painting 绘制页面

layout2.jpg

上面的图形就是浏览器分成处理后的显示效果

002 浏览器的性能优化篇

前端浏览器的性能优化,可以从CRP: 关键渲染路径入手

DOM Tree

  • 减少 DOM 的层级嵌套
  • 不要使用被标准标签

CSSOM

  • 尽量不要使用 @import,会阻碍GUI渲染线程。
  • CSS 代码量少可以使用内嵌式的style标签,减少请求。
  • 减少使用link,可以减少 HTTP 的请求数量。
  • CSS 选择器链尽可能短,因为CSS选择器的渲染时从右到左的。
  • 将写入的 link 请求放入到<head></head> 内部,一开始就可以请求资源,GUI同时渲染。

其他资源

  • <script></script> 中的同步 js 资源尽可能的放入到页面的末尾,防止阻碍GUI的渲染。如果遇到 <script async/defer></script> 的异步资源,GUI 渲染不会中断,但是JS资源请求回来以后会中断 GUI 的渲染。
  • <img /> 资源使用懒加载,懒加载:第一次加载页面时不要加载图片,因为图片也会占据 HTTP 的数量。还可以使用图片 base64,代表图片。

003 回流和重绘篇

layout 阶段就是页面的回流期,painting 就是重绘阶段。第一次加载页面时必有一次回流和重绘。
  • 浏览器渲染页面的流程

    浏览器会先把 HTML 解析成 DOM树 计算 DOM 结构;然后加载 CSS 解析成 CSSOM;最后将 DOM 和 CSSOM 合并生成渲染树 Render Tree,浏览器根据页面计算 layout(重排阶段);最后浏览器按照 render tree 绘制(painting,重绘阶段)页面。

重排(DOM 回流)

重排是指 render tree 某些 DOM 大小和位置发生了变化(页面的布局和几何信息发生了变化),浏览器重新渲染 DOM 的这个过程就是重排(DOM 回流),重排会消耗页面很大的性能,这也是虚拟 DOM 被引入的原因。
发生重排的情况
  • 第一次页面计算 layout 的阶段
  • 添加或删除DOM节点,改变了 render tree
  • 元素的位置,元素的字体大小等也会导致 DOM 的回流
  • 节点的几何属性改变,比如width, height, border, padding,margin等被改变
  • 查找盒子属性的 offsetWidth、offsetHeight、client、scroll等,浏览器为了得到这些属性会重排操作。
  • 框架中 v-if 操作也会导致回流的发生。
  • 等等
一道小题,问下面的代码浏览器重排了几次(chrome新版浏览器为主)?
box.style.width = "100px";
box.style.width = "100px";
box.style.position = "relative";
你可能会觉得是3次,但是在当代浏览器中,浏览器会为上面的样式代码开辟一个渲染队列,将所有的渲染代码放入到队列里面,最后一次更新,所以重排的次数是1次。 问下面的代码会导致几次重排
box.style.width = "100px";
box.style.width = "100px";
box.offsetWidth;
box.style.position = "relative";
答案是2次,因为 offsetWidth 会导致渲染队列的刷新,才可以获取准确的 offsetWidth 值。最后 position 导致元素的位子发生改变也会触发一次回流。所以总共有2次。

重绘

重绘是指 页面的样式发生了改变但是 DOM 结构/布局没有发生改变。比如颜色发生了变化,浏览器就会对需要的颜色进行重新绘制。
发生重绘的情况
  • 第一次页面 painting 绘制的阶段
  • 元素颜色的 color 发生改变

直接合成

如果我们更改了一个不影响布局和绘制的属性,浏览器的渲染引擎会跳过重排和重绘的阶段,直接合成
  • 比如我们使用了CSS 的 transform 属性,浏览器的可以师姐合成动画效果。

重排一定会引发重绘,但是重绘不一定会导致重排

重排 (DOM回流)和重绘吗?说一下区别
思路:先讲述浏览器的渲染机制->重排和重绘的概念->怎么减少重排和重绘。。。
区别
重排会导致 DOM结构 发生改变,浏览器需要重新渲染布局生成页面,但是重绘不会引发 DOM 的改变只是样式上的改变,前者的会消耗很大的性能。
如何减少重排和重绘
    1. 避免使用 table 布局,因为 table 布局计算的时间比较长耗性能;
    1. 样式集中改变,避免频繁使用 style,而是采用修改 class 的方式。
    1. 避免频繁操作 DOM,使用vue/react。
    1. 样式的分离读写。设置样式style和读取样式的offset等分离开,也可以减少回流次数。
    1. 将动画效果设计在文档流之上即 position 属性的 absolutefixed 上。使用 GPU 加速合成。

参考

《浏览器工作原理与实践》

Render Tree页面渲染

结束

感谢阅读到这里,如果这篇文章能对你有点启示或帮助欢迎给👍👍,我是林一一,下次见。

浏览器原理篇:本地存储和浏览器缓存

Vue 原理篇:Vue高频原理详细解答

webpack原理篇: 编写loader和plugin

掘金 合集

github 合集

154 声望
5 粉丝
0 条评论
推荐阅读
2021 年林一一同学的字节,百度,京东秋招记录
大家好,我是林一一。有一段时间没有写博客了,因为最近几个月的时间都放在秋招上面了,想了好久,秋招的经历不知道写还是不写。最近思绪很乱,心情也比较浮躁,想躺平一段时间,但总感觉有很多事情要做。下面的...

林一一1阅读 1.1k

封面图
从零搭建 Node.js 企业级 Web 服务器(零):静态服务
过去 5 年,我前后在菜鸟网络和蚂蚁金服做开发工作,一方面支撑业务团队开发各类业务系统,另一方面在自己的技术团队做基础技术建设。期间借着 Node.js 的锋芒做了不少 Web 系统,有的至今生气蓬勃、有的早已夭折...

乌柏木150阅读 12.3k评论 10

正则表达式实例
收集在业务中经常使用的正则表达式实例,方便以后进行查找,减少工作量。常用正则表达式实例1. 校验基本日期格式 {代码...} {代码...} 2. 校验密码强度密码的强度必须是包含大小写字母和数字的组合,不能使用特殊...

寒青56阅读 7.9k评论 11

JavaScript有用的代码片段和trick
平时工作过程中可以用到的实用代码集棉。判断对象否为空 {代码...} 浮点数取整 {代码...} 注意:前三种方法只适用于32个位整数,对于负数的处理上和Math.floor是不同的。 {代码...} 生成6位数字验证码 {代码...} ...

jenemy46阅读 6k评论 12

从零搭建 Node.js 企业级 Web 服务器(十五):总结与展望
总结截止到本章 “从零搭建 Node.js 企业级 Web 服务器” 主题共计 16 章内容就更新完毕了,回顾第零章曾写道:搭建一个 Node.js 企业级 Web 服务器并非难事,只是必须做好几个关键事项这几件必须做好的关键事项就...

乌柏木66阅读 6.2k评论 16

再也不学AJAX了!(二)使用AJAX ① XMLHttpRequest
「再也不学 AJAX 了」是一个以 AJAX 为主题的系列文章,希望读者通过阅读本系列文章,能够对 AJAX 技术有更加深入的认识和理解,从此能够再也不用专门学习 AJAX。本篇文章为该系列的第二篇,最近更新于 2023 年 1...

libinfs39阅读 6.3k评论 12

封面图
从零搭建 Node.js 企业级 Web 服务器(一):接口与分层
分层规范从本章起,正式进入企业级 Web 服务器核心内容。通常,一块完整的业务逻辑是由视图层、控制层、服务层、模型层共同定义与实现的,如下图:从上至下,抽象层次逐渐加深。从下至上,业务细节逐渐清晰。视图...

乌柏木44阅读 7.4k评论 6

154 声望
5 粉丝
宣传栏