浏览器的渲染过程以及性能优化

前言:网页的加载速度是用户对一个网站的首先的用户体验,反正个人认为,如果在网好的情况下,打开一个网页超过3s,我就不不想再进这个网页了,除非我特别想进这个网站

首先从问题到根源,再到解决方案
  1. 什么是进程,什么是线程?
  2. 为什么js是单线程的?
  3. js和GUI的执行顺序,相互会阻塞吗?
  4. defer 和 async 是什么,以及它们的区别
  5. 什么是CRP,即关键渲染路径(Critical Rendering Path)?如何优化?
  6. 什么是浏览器的回流与重绘?
  7. 为什么js是单线程的
进程(process)和线程(thread)

进程(process)和线程(thread)是操作系统的基本概念。

进程是CPU资源分配的最小单位(是能拥有资源和独立运行的最小单位)
线程是CPU调度的最小单位(是建立再进程基础之上的一次程序运行单位)

线程又分为单线程(至始至终都只有一个线程工作)和多线程(可能有多个线程共同工作)

进程是CPU发给操作系统的任务,而线程主要是执行任务,一个任务由至少由一个线程,或者多个线程协作完成

由于每个进程至少要做一件事,所以一个进程至少由一个线程,系统会给每个进程分配独立的内存,因此进程有独立的资源。同一进程内的各个线程之间共享该进程的分配空间(包括代码段,数据集,堆)。这也就是为什么js线程和事件处理线程不是一个线程,但是却可以操纵相同的数据

当我们启动一个应用,(或者打开一个浏览器的标签页)计算机会会创建一个进程并且分配一块空间,应用的状态都会保存到这块内存中。如果应用关闭进程就会结束,然后还会释放相应的内存

浏览器的多进程架构

一个好的进程常常被划分为几个相互独立又彼此配合的模块,浏览器也是如此。

以Chrome为例,他由多个进程组成,每个进程都有自己的核心的职责,他们相互配合完成浏览器的整体功能

每个进程中又有多个线程(一个进程最少有一个线程)也会协同工作,配合完成所在的进程的任务的工作

Chrome 采用对进程的架构,其顶层存在一个Browser process 用于协调浏览器的其他进程工作

多进程的优点

以Chrome 为例,每打开一个标签页都是开启一个新的进程,单个标签页崩溃并不会引起浏览器的奔溃,除非单个进程所占用的内存太高,会引起程序的崩溃。

多进程的缺点

由于每开启一个进程都会占用的一定的内存和CPU,如果进程太多额你存占用太多,也会引起程序的崩溃。Chrome浏览器自带内存回收机制很良性。

浏览器的主要进程和职责
主要进程
  • 主进程
  • 第三方插件进程(浏览器的第三方插件)
  • GPU进程(图像处理)
  • 渲染进程(本文主要描述进程)等
主进程 Browser process

负责浏览器界面的显示与交互。各个页面的(标签页)的管理、创建和销毁其他进程。网络资源的管理、下载等

第三方插件进程 Plugin process

每种类型的插件对应一个进程,仅当使用该插件时才创建。

GPU 进程 GPU process

最多只有一个,用于3D绘制

渲染进程 Renderer process

称为浏览器渲染进程或浏览器内核,内部时多线程的。只要负责页面渲染(GUI线程),脚本执行(js线程),事件处理(事件处理进程)等

渲染进程的主要线程
GUI渲染线程

负责构建DOM树和CSSOM树,然后合并DOM树和CSSOM树成 渲染树,然后进行页面的布局渲染等。当页面发生重排或者回流的时候,该线程就会重新执行

注意GUI线程和js线程正常情况下时互斥的

一般情况下等GUI线程执行完成以后才执行js线程,试想一下,js进程主要是围绕着DOM写交互,如果先执行js线程,js获取某个DOM的时候,这个DOM在浏览器中中还没有

js引擎线程

JavaScript引擎(v8引擎),也称为js内核,负责处理js脚本程序

js引擎一直等着任务队列的到来,然后加以处理,一个渲染进程无论什么时候都只有一个js线程运行js,js线程和GUI线程互斥

事件触发线程

归属于浏览器内核(渲染进程)而不是js引擎,用来控制异步队列事件循环(可以理解为这是一个辅助性的线程,辅助js引擎),另外辅助线程还有异步http线程,定时器线程等,这些辅助线程只是帮助处理,最后还是得js执行(js空闲得情况下)

定时器触发线程

setInterval、setTimeout

浏览器定时器并不是由js记数的(因为js引擎是单线程的,如果定时器处于阻塞进程,会影响记数的准确性)而是通过单独的线程计时触发(计时完毕后添加到事件队列中等待js引擎孔宪后执行)

异步的http请求线程

在 XMLHttpRequest 在连接后是通过浏览器新开的一个线程请求,将检测到状态变更时,如果设置后回调函数,异步线程就产生状态变更事件,将这个回调放入事件队列中,再由js引擎执行

浏览器渲染的流程以及个线程的工作
  1. 渲染进程简析字符串
  2. 碰到html文件构建DOM树
  3. 碰到link css构建CSSDOM树(碰到script标签先检查有没有async或者defer属性(有的情况下这个后面说)如果没有,行进入队列,在整个GUI线程没有执行完毕不会执行js线程,除非将script标签放在前面)
  4. 将两颗树合并成renderer树
  5. GUI线程完成页面的绘制
  6. 开始执行队列中的js,开启js线程
  7. js同步执行代码
  8. 碰到定时器,定时器线程执行,将处理好的回调放入事件队列中等待js空闲的时候执行
  9. 碰到异步请求或者异步回调,异步线程和事件触发线程执行,将改状态的回调放入事件队列中(等待js空闲的时候执行)
  10. 当js空闲下来的时候(也就是同步任务执行完毕的时候)开始执行事件队列中的任务
  11. 而在执行任务的时侯,任务又分为宏任务和微任务,进入这样一个事件循环
为什么js线程和GUI线程只能执行一个

如果同时执行的情况下,试想一个场面,GUI正在构建DOM树或者CSSDOM树,如果这时候js线程操纵一个DOM并改变了样式,很容易发生不可估量的错误(抛开在js线程执行的时候,这个DOM存不存在的问题),反之亦然。

css加载会阻塞html加载吗

会,在构建tree时,编译时两个进程,并且时并发的,两个相互不影响,但是别忘了,单靠一个DOM或者一个CSSDOM树还不够,别忘了,GUI线程最终会将这两个树给结合,只要有一颗树没有渲染完成,就一直等着

js为什么是单线程的

其实这个和js的作用还是有关系的,js组要作用是给浏览器添加一些交互效果,操作DOM,试想一下,如果一个线程在创建一个DOM,而另一个线程删除这个DOM,两个线程并发执行,可以想想后果

什么是关键渲染路径(CRP)

关键渲染路径是浏览器将HTML CSS js转化为屏幕上呈现的像素内容所经历的一系列步骤。也就是合并两棵树之后的渲染流程

为尽快完成首次渲染,我们需要最大限度减小以下三种可变因素:

  • 关键资源的数量: 可能阻止网页首次渲染的资源。

  • 关键路径长度: 获取所有关键资源所需的往返次数或总时间。

  • 关键字节: 实现网页首次渲染所需的总字节数,等同于所有关键资源传送文件大小的总和。

优化方案

  • 删除不必要的代码和注释包括空格,尽量做到最小化文件
  • 利用gzip压缩文件
  • 静态文件的缓存
  • 减少css选择器的过多嵌套
优化js加载方案

上文也提到,由于js线程和GUI渲染线程彼此会相互阻塞,优化方案就在script的两个属性上

  • async :当渲染进程碰到script标签,并且检查到有async属性的情况下,两个线程将不会阻塞,会同时并发

需要注意的情况:js没有操作DOM的情况下

  • defer :当渲染进程碰到script标签,并且检查到有defer属性的情况下,两个线程将不会阻塞,但js线程只会加载js而并不会执行js
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值