前端知识梳理-浏览器篇
一、什么是进程,什么是线程
进程是cpu资源分配最小单位,线程是进程最小调度单位。 在我们的操作系统中,有许多许多的进程,操作系统每干一件事儿就会交给一个进程去做,比如打开音乐就是一个进程。而每个进程里又包含了许多的线程,例如打开了音乐播放器,放一首歌,就是一个新的线程,切换下一首,可能又是另外一个线程。总之他俩之间的关系就是一个进程包含多个线程。
二、浏览器是多进程还是单进程
浏览器一般都是多进程,每开一个tab页,就是一个新的进程。这也就是为什么一个tab页崩溃了不会影响到整个浏览器崩溃。他主要分为以下几个进程:
1、主进程:只有一个,负责调度主控整个浏览器
2、插件进程:每个插件都有一个进程,只在插件被调用的时候创建
3、GPU进程:只有一个,负责3d绘制
4、渲染进程:每个tab页一个,负责网页的渲染,脚本的执行和事件的处理等。这个进程也是前端最需要了解的进程。
三、渲染进程
要了解渲染进程,那就得从他有几个线程开始。
渲染进程主要有以下几个线程:
1、GUI线程:负责页面的构建和渲染,当页面需要被绘制的时候就会启动这个线程,要注意的是,他和js引擎线程是互斥的,不能并行执行
2、js引擎线程:负责解析和执行js脚本,因为他和GUI线程的互斥性,所以js代码是会导致页面渲染不连贯的,也就是常说的阻塞页面渲染
3、事件触发线程:归属于浏览器,而不是js引擎,他主要就是控制事件循环,将一系列的任务加入一个队列等js引擎空闲下来后去执行
4、定时器线程:管理定时器的计时,等时间到了就把事件推入任务队列,等待js引擎执行
5、异步http请求线程:每发送一个请求就会开启一个新的线程,等待响应后把回调函数推入任务队列,等js引擎执行。
以上几个就是渲染进程的主要线程。其实通过上述的了解我们就可以大概明白了,js的代码执行顺序为什么是那样,可以简单的认为只有同步代码是第一时间就在js引擎线程里去执行的,其他的例如宏任务,微任务,等都是由别的线程管理,当满足条件后才将代码交给js引擎去执行。
那接下来就聊一聊老生常谈的浏览器渲染流程:
1、首先浏览器会去解析html文件,构建DOM树,然后解析css,生成css树。
2、等DOM树和css树都构建好了之后,会将两者合并,生成最终的render树
3、接着去布局render树,计算位置 尺寸。然后再绘制render树。
4、最后将各层的信息发送给GPU,GPU合成渲染层最终呈现出页面。
在浏览器的渲染过程中还有两个钩子会被触发,他们就是load事件和DOMcontentloaded事件
那二者有什么区别呢?
DOMcontentloaded事件会在dom树构建完成之后(不包括样式表,图片等)就执行,而load事件会在渲染完毕后被执行。所有前者是先于后者执行的
下一个问题,css到底会不会阻塞dom渲染呢?
首先看一下浏览器的渲染流程图
image.png
可以看到,DOM的加载和css的加载时并行执行的,所以理论上来说css是不会阻塞dom渲染的,但是最终的渲染是需要dom树和css树共同生成一个render树,就算dom树构建好了,但是css树没构建好,那也没办法渲染页面。所以css的加载不会阻塞但是css的解析又是会阻塞页面渲染的。
浏览器的回流和重绘:
回流指的时当元素发生大小位置上的变化,引起整个结构也变化了这个时候浏览器就会触发回流,重绘则是元素的颜色背景色等不影响布局变化的地方发生了变化,就会触发重绘。
回流一定触发重绘,但重绘不一定触发回流。
那平时应该怎么尽量避免这两个问题呢?
1、尽量把操作dom的过程统一执行,以减少回流重绘的次数
2、把需要多次操作的dom先display:none,操作完在展示出来
3、使带有动画的dom脱离文档流
4、尽量使用偏移量transform去代替left top等位移属性
5、使用一些能触发GPU加速的样式代替原来的写法
script标签
说完了html,css,当然还要聊一聊最重要的js环节了。
看完上面这些内容我们大概已经知道了,js为什么会阻塞页面渲染,那有没有什么办法避免呢。
首先了解以下script标签上的async和defer两个属性
async:异步加载js文件,他会在异步加载完成之后立即执行js文件,所以还是会阻塞后续的dom渲染。
defer:也是异步加载,他和async不同的是,他加载完成之后不会立即执行,而是会等到DOM树构建完毕DOMcontentloaded执行之前再去执行脚本。
并且因为async加载就执行的特点,导致async加载js文件时无序的,这可能会出现当某个js需要依赖上一个js的时候,上一个js还没加载过来的问题。而defer则时有序的执行js,会按照你书写的script标签顺序去执行。
好啦,了解完这么多,应该就明白了为什么通常js要放在页面的底部引入。只要记住GUI渲染线程和js线程是互斥的就能回答大部分阻塞的问题了