浏览器渲染原理

浏览器渲染原理

  1. 为什么JavaScript是单线程的
  2. 为什么js阻塞页面加载
  3. css加载会造成阻塞
  4. DOMContentLoaded和load的区别
  5. 什么是CRP,即关键渲染路径?
  6. defer和async的区别
  7. 浏览器的回流和重绘
  8. 什么是渲染成合并
    进程和线程是操作系统的基本概念。进程是cpu资源分配的最小单元,是能具有资源和独立运行的最小单位。线程是cpu调度的最小调度,是建立在进程基础的一次程序运行单位。
    chrome是多进程组成的,每个进程都有自己的核心职责,相互配合完成浏览器的整体功能。每个进程之间包含多个线程,一个进程之内的多个线程也会协同工作。新开一个tab页面,新建一个进程,单个tab页面崩溃不会影响到整个浏览器。第三方插件崩溃不会影响整个浏览器。系统会为浏览器新开的进程分配内存,cpu等资源,内存和cpu资源的消耗也会变大。
浏览器 --- 浏览器进程 --- 主进程
                     --- 第三方插件进程
                     --- GPU进程
                     --- 渲染进程 --- js引擎线程
                                 --- 事件触发线程
                                 --- 定时触发器线程
                                 --- 异步http请求线程
                                 --- GUI渲染线程

主线程: 负责浏览器界面的显示和交互,各个页面的管理,创建和销毁其他进程。网络资源管理,下载等
第三方插件进程: 各种类型的插件对应着一个进程,仅当使用该插件时才创建。
GPU进程: 最多只有一个,用于3D绘制
渲染进程: 称为浏览器渲染进程或浏览器内核,内部是多线程的。主要负责页面渲染,脚本执行,事件处理等。
GUI渲染线程: 负责渲染浏览器界面,解析 HTML,CSS,构建 DOM 树和 RenderObject 树,布局和绘制等。当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行。GUI 渲染线程与 JS 引擎线程是互斥的,当 JS 引擎执行时 GUI 线程会被挂起(相当于被冻结了),GUI 更新会被保存在一个队列中等到 JS 引擎空闲时立即被执行。
js引擎线程: javascript引擎,也成为js内核,负责处理JavaScript脚本程序。JS 引擎一直等待着任务队列中任务的到来,然后加以处理,一个 Tab 页(renderer 进程)中无论什么时候都只有一个 JS 线程在运行 JS 程序。GUI 渲染线程与 JS 引擎线程是互斥的,所以如果 JS 执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞。
事件触发线程: 归属于浏览器而不是 JS 引擎,用来控制事件循环。当 JS 引擎执行代码块如 setTimeOut 时(也可来自浏览器内核的其他线程,如鼠标点击、AJAX 异步请求等),会将对应任务添加到事件线程中。当对应的事件符合触发条件被触发时,该线程会把事件添加到待处理队列的队尾,等待 JS 引擎的处理。由于 JS 的单线程关系,所以这些待处理队列中的事件都得排队等待 JS 引擎处理(当 JS 引擎空闲时才会去执行)。这些处理队列中的事件都得等js引擎处理
定时触发器线程: setInterval和setTimeout所在的线程。通过单独线程来计时并触发定时。
异步http请求线程: 在XMLHttpRequest在连接后是通过浏览器新开一个线程请求。在检测到状态变化的时候,如果有设置回调函数,异步线程就产生状态变事件,将这个回调事件队列中。在由JavaScript引擎中执行。

浏览器渲染流程在这里插入图片描述

  1. 解析html代码,生成一个dom树
  2. 解析css代码,生成CSSOM树
  3. 将dom树和CSSOM树结合,去除不可见的元素,生成Render Tree
  4. 计算布局,(回流 | 重排)根据Render Tree进行布局计算,得到每一个节点的几何信息
  5. 绘制页面,(重绘)GPU根据布局信息绘制
    细致点
    1. 解析HTML
    1. 样式计算
    1. 布局
    1. 分层
    1. 绘制: 为每一层生成如何绘制的指令
    1. 分块
    1. 光栅化

为什么js会阻塞页面加载

由于JavaScript是可以操纵dom的时候,如果在修改元素属性的时候,同时渲染页面,就会使得JavaScript线程和ui线程同时运行,渲染线程前后获得到元素数据就可能不一致。所以GUI渲染线程和JavaScript引擎应该是互斥关系。当 JavaScript 引擎执行时 GUI 线程会被挂起,GUI 更新会被保存在一个队列中等到引擎线程空闲时立即被执行。

css加载是否会造成阻塞

DOM和CSSOM通常是并行构建的,所以CSS加载不会阻塞DOM的解析。render tree是依赖于DOM Tree和CSSOM Tree的,必须等到CSSOM tree构建完成,也就是CSS资源加载完成,或者是css资源加载失败后,才能开始渲染。这样说来, CSS加载会阻塞DOM的阻塞。JavaScript是可以操纵DOM和css样式,如果在修改这些元素属性同时渲染界面,渲染线程前后获得的元素数据就可能不一致。为了防止出现渲染不可预期的结果,浏览器设置GUI渲染线程与JavaScript引擎为互斥关系。样式表会在后面的js执行前加载完毕,所以css会阻塞后面js的执行

DOMContentLoaded与load的区别

当domcontentloaded事件触发的时候,仅当dom解析完成后,不包括样式表,图片。若文档中没有脚本,浏览器解析完文档便能触发domcontentloaded事件。如果文档中包含脚本,则脚本会阻塞文档的解析,则脚本需要等cssom构建完成才能执行。在任何情况下,DOMContentLoaded的触发不需要等待图片等其他资源加载完成。
当onload事件触发的时候,页面上所有的DOM, 样式表,脚本,图片等资源已经加载完成
DOMContentLoaded -> load

什么是CRP,即关键渲染路径

关键渲染路劲是浏览器将html css JavaScript转换为在屏幕上呈现的像素内容所经历的一系列不走。

  • 关键资源的数量: 可能阻止网页首次渲染的资源
  • 关键路径长度: 获取所有关键资源所需要的往返次数和总时间
  • 关键字节: 实现网页首次渲染所需要的总字节数,等同于所有关键资源传送文件大小的总和

优化DOM

  • 删除不需要的代码和注释包括空格,做到最小化文件
  • 使用GZIP压缩文件
  • 结合HTTP缓存文件

优化CSSOM

缩小,压缩以及缓存同样重要,CSSOM会阻止页面呈现。

  • 减少关键CSS元素数量
  • 声明样式表的时候,密切关注媒体查询的类型。

优化JavaScript

当浏览器遇到script的标记的时候,会阻止解析器继续操作,直到cssom构建完毕,JavaScript才会运行并继续完成DOM构建过程。

  • async: 当我们在script标记上添加async属性之后,浏览器遇到这个script标签的时候,会继续解析dom,同时脚本也不会被cssom阻止,不会阻止CRP
  • defer: 脚本文件需要等文档解析后(DOMContrentLoaded事件前)执行。async和他的主要区别就是执行。
  • 脚本不会修改DOM或者CSSOM的时候,使用async
  • 预加载: preload和prefetch
  • DNS预解析: dns-prefetch
    分析并用关键资源数,关键字节数,关键路径长度来描述crp
    最小化资源树: 消除内联,推迟下载defer,使用异步解析async
    优化关键字节数(缩小,压缩)来减少下载时间
    优化加载剩余关键资源的顺序: 让关键资源css尽早下载以减少crp长度

defer和async的区别

浏览器的回流和重绘

回流 重排

当renderer tree中部分或者全部元素的尺寸,结构,或者某些属性发生改变的时候,浏览器重新渲染部分或者全部文档的过程称为回流。

  • 页面首次渲染
  • 浏览器窗口大小发生改变
  • 元素尺寸或位置发生改变导致内容变化
  • 元素字体大小变化
  • 添加或者删除可见的dom元素
  • 激活css伪类
  • 查询某些属性或调用某些方法 offsetWidth offset
  • width height
  • fontsize
  • display position
  • float
  • table layout

重绘

某些元素的外观被改变所触发的浏览器行为(重新计算节点在屏幕中的绝对位置并渲染的过程)

性能影响

回流比重绘的代价更高。浏览器会维护一个队列,把所有引起回流和重绘的操作放入队列中,如果队列中的任务数量或者时间间隔达到一个阈值的,浏览器就会将队列清空,进行一次批处理,这样可以把多次回流和重绘变成一次。
css

  • 避免使用table布局
  • 尽可能在DOM树的最末端改变class
  • 避免设置多层内联样式
  • 将动画效果应用到position属性为absolute或fixed的元素上
  • 避免使用css表达式
    JavaScript
  • 避免频繁操作样式,最好一次性重写 style 属性,或者将样式列表定义为 class 并一次性更改 class 属性。
  • 避免频繁操作 DOM,创建一个 documentFragment,在它上面应用所有 DOM 操作,最后再把它添加到文档中。
  • 也可以先为元素设置 display: none,操作结束后再把它显示出来。因为在 display 属性为 none 的元素上进行的 DOM 操作不会引发回流和重绘。
    // javascript
    const renderEle = document.getElementById('demo');
    // 第一次操作修改 color、background、padding
    renderEle.style.display = 'none'; // 导致重排(重排会引起渲)
    renderEle.style.color = 'red'; // DOM不存在渲染树上不会引起重排、重绘
    renderEle.style.background= '#ccc';// DOM不存在渲染树上不会引起重排、重绘
    renderEle.style.padding = '15px 20px';// DOM不存在渲染树上不会引起重排、重绘
    // ...
    // 第二次操作修改 marginLeft、marginTop
    renderEle.style.marginLeft = '15px';// DOM不存在渲染树上不会引起重排、重绘
    renderEle.style.marginTop = '15px';// DOM不存在渲染树上不会引起重排、重绘
    // ...
    // 第三次操作修改 border
    renderEle.style.border = '2px solid #ccc';// DOM不存在渲染树上不会引起重排、重绘
    renderEle.style.display = 'block';// 导致重排(重排会引起渲)
    
  • 避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来。
  • 对具有复杂动画的元素使用绝对定位,使它脱离文档流,否则会引起父元素及后续元素频繁回流

什么是渲染层合并

渲染层合并,对于页面中 DOM 元素的绘制(Paint)是在多个层上进行的。在每个层上完成绘制过程之后,浏览器会将绘制的位图发送给 GPU 绘制到屏幕上,将所有层按照合理的顺序合并成一个图层,然后在屏幕上呈现。

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值