浏览器的事件循环

参考:https://www.cnblogs.com/caiyy/p/10406934.html

GUI和Web软件

1、GUI是通过 代码打包下载 安装到如手机上进行访问的过程

2、Web端的软件是通过 发布到server或cdn上,网页通过增量式访问获取服务的

浏览器多进程

包含一个主进程(包含网络渲染,页面管理),插件进程,GPU(3d)渲染进程(可选),页面渲染进程(js处理,页面渲染,事件处理)

渲染进程:

1、broswer主进程 进行浏览器的页面资源管理 创建和销毁其他进程,页面交互,将位图合并(布局渲染树)绘制到页面上

2、GPU进程,硬件加速 只有一个

3、插件进程 每个类型的插件对应一个进程

4、渲染进程 每个tab页一个进程,负责页面渲染 脚本执行 事件触发,任务队列轮询等

——JS引擎线程(V8引擎):负责js脚本解析,代码运行 与GUI互斥

——GUI渲染线程: 解析html和css,负责布局和绘制,与js引擎线程互斥,这是由于js可以操作dom,防止渲染前后不一致

——事件触发线程:归属于浏览器,用于控制事件循环,当事件被触发该线程会将对应的回调函数放到任务队列当中

——定时触发器线程:setTimeout setTnterval W3C中规定setTimeout低于4ms算4ms 回调函数放入任务队列

——异步http请求线程:监测状态变更时产生状态变更事件,放入任务队列

——任务队列轮询线程:轮询监听任务队列是否为空

webWorker和shareWorker

js是单线程的 当有大量运算会造成页面渲染卡顿,为了避免可以申请一个web worker。 js引擎向浏览器申请开一个子线程(不可造作dom),js引擎线程和worker线程通过特定的方式通信,计算出结果后通信给js引擎主线程。

webWorker是某个页面render下的一个线程,不会和其他页面的render进程共享

shareWorder是浏览器所有页面共享的,是一个单独的进程

GUI渲染

网页从白屏到内容显示的时间就是HTML 文档加载和解析的时间。也就是DOMContentLoaded 事件触发之前所经历的时间。

js脚本参数设置: https://blog.csdn.net/zyj0209/article/details/79698430

由js引擎来解析html生成dom树,css解析生成cssdom树,js脚本执行可以设置参数默认是

1、同步sync,当执行html的过程中遇到js脚本,会停止解析html,先去加载执行js脚本完毕后继续解析html

2、异步async,当执行html遇到js,会同时下载js脚本,如果js先加载完就停止解析html先执行js脚本,然后解析html或者是这个时候html已经加载完毕,那么直接执行js,不管是哪一种,DOMContentLoaded都会在html解析完触发

3、延后dsync,遇到js脚本会进行下载,脚本需要等待html解析完才会执行,DOMContentLoaded会在js脚本执行完了被触发

dom树和cssdom树结合为渲染树render-tree,从根节点递归调用计算每一个元素的大小,位置等,给出每个节点在屏幕上精确的坐标,这就是基于渲染树的布局渲染树,之后交给主进程进行渲染树的绘制。

资源阻塞机制

DOMContentLoaded和load分别标识着dom加载完成和dom&&css&&js加载完成

GUI线程中html和css解析是并行的,css不会阻塞html的解析但是会阻塞页面渲染,所有放在head中尽量早解析

js根据需要不同可以放在不同的位置,初始化放在head,操作dom放在body末尾或者使用load事件

回流和重绘

render树中的部分或全部因为元素的尺寸、布局、隐藏等改变需要重新构建称为回流,如

1、页面初始化、调整窗口,改变字体,内容变化、操作dom、操作css、激活css伪类、操作class属性、设置style属性、增加或者移除样式表

2、如何防止回流:减少逐项修改style最好一次定义在class中更新、避免循环操作dom、到要频繁的获取offset属性时,不重复读取,将复杂的元素绝对定位或者固定定位,使用GPU硬件加速创建一个新的复合图层

图层:dom中的每个节点对应一个简单图层,复合图层是对简单图层的合并,absolute,fixed布局会脱离文档流,但是还在当前的复合图层中,会影响重绘,但是回流不影响。

当使用硬件加速的时候会生成新的复合图层,互不影响,设置transform: translate3d(0,0,0) 或 translateZ(0),更多内容参考://www.greatytc.com/p/f8b1d6e598db

proload和prefetch

详情:https://www.cnblogs.com/xiaohuochai/p/9183874.html

proload提升资源的优先级,提升到跟设置了as属性的同一优先级,as属性设置资源的优先级,不设置会默认为异步请求的优先级,很低。不管资源是否需要都会被加载,并且有回调函数。设置方式<link rel="preload" href="..." as="..." onload="preloadFinished()">

prefetch是预先加载下一页可能用到的资源,不可混用。

事件循环机制

更多:https://juejin.im/post/5ec73026f265da76da29cb25

  • 事件循环机制的核心是事件触发线程,js执行栈的过程中触发异步任务,或者是微任务,将异步任务交给相关的线程,将微任务放到微任务队列;
  • 当执行栈执行完毕后去查看微任务,执行当次产生的微任务,这个时候如果有微任务添加进来会继续执行微任务直到js执行栈为空才去渲染;
  • 同时异步任务完成后会将回调函数放到任务队列(宏任务)当中,当执行栈结束&&微任务结束,进入渲染阶段
    1、判断是否需要重渲染,一般涉及到屏幕刷新率、页面性能、程序是否在后台执行,一般屏幕刷新率为60Hz,,如果页面性能过低,为了保证稳定的刷新率会选择30Hz,当在一次刷新帧内发生多次动画行为,并不会真实的渲染到页面,而会被浏览器收集起来一次执行;如果程序在后台执行会降低刷新率到4hz甚至更低;如果浏览器认为渲染不会引起页面视觉上的变化;如果帧动画回调函数为空(可以通过requestAnimationFrame函数触发);满足以上条件不进行渲染
    2、如果判断需要渲染则进行渲染否则直接开始进行后续代码的执行,也是在这里进行resize scroll的触发,这里浏览器会保存一个,目标对象,等到这里派发事件到目标上的时候驱动目标对象的回调函数。触发帧动画回调(requAnimationFrame);执行IntersectionObserver回调;绘制页面;判断宏任务和微任务队列为空,执行空闲周期算法判断是否要执行requestIdleCallback的回调函数
  • 开启下一轮的事件循环,从宏任务队列获取新的任务放到执行栈执行

宏任务 macro-task:script整体代码、setTimeout、setInterval、setImmediate、I/O、UIrendering

微任务 micro-task:process、nextTick、Promises、Object.observe、MutationObserver

注:

  • async await 最终的结果是返回了promise,在await后跟随的语句是同步的,下一行开始的语句是异步的 等同于then后面的微任务回调
  • 对于任务队列中的任务,并不是只有一个,对于用户输入的任务如鼠标键盘事件将会优先于其他的task,浏览器在保证顺序的前提下将会分配给用户事件4/3的优先权
  • requestAnimationFrame函数是在页面重新渲染之前的最后一步调用,很可能在宏任务之后不进行调用
  • requestIdleCallback(fn, deadline)函数是浏览器提供的空闲调度算法,将一些计算量大而且又不紧急的任务放到空闲时间去执行,每次使用的时候要去调用timeRemaining()函数获取deadline来判断是否有空余时间可以使用,如果有更高优先级的任务出现则将在没有渲染任务的时候则会动态的将剩余时间设置为0;需要注意的是每次当浏览器是空闲的也会有deadline为50ms这是为了应对用户的交互操作发生时,确保用户在无感知的延迟下得到回应

例题:https://blog.csdn.net/weixin_34176694/article/details/91400057

浏览器渲染之前做了什么

更多:https://juejin.im/post/5e6394b4e51d4526e32c3cef

需要注意的是,每次渲染的的条件是js的执行栈为空,也就是触发了requestAnimationFrame()方法,所以在执行代码的时候,如


document.addElementListener('click', function(){console.log(1, promise.resolve().then(() =>{console.log(2)}))})

document.addElementListener('click', function(){console.log(3, promise.resolve().then(() =>{console.log(4)}))})

在代码中调用,会在js执行栈中加载一个脚本文件,第一个监听器触发后,脚本文件scripts还在所以这个时候不能去执行微任务,还是继续执行宏任务,执行完后退出js堆栈,这时才去清空微任务,结果:1=>3=>2=>4

如果是在页面上点击触发,没有初始的js脚本,则会在第一个监听触发后判断堆栈为空去执行微任务,结果为1=>2=>3=>4


image.png

这里有一个图,三个环,中间是事件循环,左边是其他的事件(宏任务、微任务),右边是浏览器渲染,在左边完成后,会按照顺序推到中间的js执行栈中,在js执行栈为空的时候触发requestAnimationFrame()去进行页面渲染,如下代码,微任务执行后继续添加了新的任务到js栈中,会循环执行

function loop(){promise.resolve().then(loop)}
loop()
setTimeout(() => {
  console.log("sto")
  requestAnimationFrame(() => console.log("rAF"))
})
setTimeout(() => {
  console.log("sto")
  requestAnimationFrame(() => console.log("rAF"))
})

如果用setTimeout循环来进行动画的循环处理,会比requestAnimationFrame()执行的次数多,这是因为浏览器一般为60Hz也就是一秒最多渲染60次,这里是由于人眼的感知所决定,60Hz已经是较为流畅的实现,当然与硬件显卡之类的设备也有关系,硬件决定上限,性能平衡决定结果;所以setTimeout最多可以达到浏览器的阈值,当然多余的调用就被浪费了,同时可能会因为在每一个动画帧调用次数不同而出现漂移,也可能因为在没有匹配到动画帧而丢失渲染,不可控,如上面的代码第二段,浏览器期待在定时器之间不穿插渲染,所以会将两次渲染合并到一起,结果是:sto sto rAF rAF
而requestAnimationFrame是在每一次渲染之前调用,更加科学的用于动画的处理,有些人会把requestAnimationFrame方法归类为和setTimeout一样都属于宏任务队列,但是实际上来说与宏任务和微任务无关它只和浏览器渲染相关,在浏览器刷新前执行

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354