搬运:细说浏览器输入URL后发生了什么 - 掘金 (juejin.cn)
[1]地址解析:检验输入url的合法性(正则检验*),看输入的是url还是搜索内容,然后通过浏览器默认搜索引擎的服务器查找进行地址转换url查询。
[2]DNS域名解析,涉及到了计网的相关知识点。
在客户端输入 URL 后,会有一个递归查找的过程,从浏览器缓存中查找->本地的hosts文件查找->找本地DNS解析器缓存查找->本地DNS服务器查找,这个过程中任何一步找到了都会结束查找流程。
若本地域名服务器的 DNS 缓存没有命中,则本地域名服务器向上级域名服务器进行迭代查询
首先本地域名服务器向根域名服务器发起请求,根域名服务器返回顶级域名服务器的地址给本地服务器
本地域名服务器拿到这个顶级域名服务器的地址后,就向其发起请求,获取权限域名服务器的地址
本地域名服务器根据权限域名服务器的地址向其发起请求,最终得到该域名对应的 IP 地址本地域名服务器将得到的 IP 地址返回给操作系统,同时自己将 IP 地址缓存起来
操作系统将 IP 地址返回给浏览器,同时自己也将 IP 地址缓存起
至此,浏览器就得到了域名对应的 IP 地址,并将 IP 地址缓存起
迭代查询会使得服务器的事件负载增加,所以在本地DNS服务器未命中后会采取迭代查找(自行配置DNS服务器)–多级缓存就不再多说了,计网总结笔记都有。
[3]等待 TCP 队列:Chrome 有个机制,同一个域名同时最多只能建立 6 个 TCP 连接,如果在同一个域名下同时有 10 个请求发生,那么其中 4 个请求会进入排队等待状态.(网络请求进程)
这一点比较重要,引入一些新知识(杂):
TCP连接会存在TCP队列,那加载大量图片或者其他资源的时候,该怎么解决卡顿呢?(待解决)
多次HTTP报文的发送,存在有多次报文断触的情况产生,那需要采用一些什么策略减少这些额外的消耗呢?
报文头设置connection:keep-alive,便可以保持端到端的tcp链接,避免了tcp链接建立和ssl的消耗。
[4]建立TCP连接:三次握手以及对应解决了哪些问题.https是由HTTP + SSL / TLS 两部分组成,待三次握手TCP完成以后,https会进行SSL握手。
SSL握手引入:隔离在TCP层与HTTP层的加密层,握手的流程为
需要经历双密匙交流的加密后–借加密信息对报文进行处理。接下来我们做抓包解析(使用Fiddler做抓包解析*)
[5]进行HTTP报文的发送,这其中涉及到了多种缓存验证机制。
[6]四次挥手,报文发送完毕,终止态是如何进入的(*)
TCP连接在一段时间内是不会断的,因为建立新连接太耗资源,等待时间久,多个HTTP请求会复用TCP通道,过了最大允许的时间就会自动断开,和页面渲染是分开的,没有等待关系。
这里我想引入一个之前一直思考的问题,服务器只开启一个端口,该如何为多用户提供服务。
1.端口与端口之间通过套接字建立通信,TCP的套接字唯一标识是四元组形式(源IP地址,源端口号,目的IP地址,目的端口号)。
2.服务器在端口收到请求后,用四种组合形成唯一标识,分配特定套接字。多个套接字可以拥有相同的目的端口80.
3.通过任务队列等机制吧,实现信息处理(*隐藏实现),然后通过套接字回返报文(*隐藏实现)
[7]重定向:(基于此的状态码301 和 302)根据服务器解析报文回返的location字段访问新定向的导航。
[8]浏览器进行渲染页面–>第二大点
细说浏览器输入URL后发生了什么 - 掘金 (juejin.cn)
【前端工程师面试宝典】学习说明_互联网校招面试真题面经汇总_牛客网 (nowcoder.com)
根据上图的流程做一个简单的介绍
浏览器解析器关注点一:
当解析器发现非阻塞资源,例如一张图片,浏览器会请求这些资源并且继续解析。当遇到一个 CSS 文件时,解析也可以继续进行,但是对于
标签(特别是没有
async
或者defer
属性的)会阻塞渲染并停止 HTML 的解析。尽管浏览器的预加载扫描器加速了这个过程,但过多的脚本仍然是一个重要的瓶颈。
预加载扫描器:
浏览器构建 DOM 树时,这个过程占用了主线程。当这种情况发生时,预加载扫描仪将解析可用的内容并请求高优先级资源,如 CSS、JavaScript 和 web 字体。多亏了预加载扫描器,我们不必等到解析器找到对外部资源的引用来请求它。它将在后台检索资源,以便在主 HTML 解析器到达请求的资源时,它们可能已经在运行,或者已经被下载。预加载扫描仪提供的优化减少了阻塞。
当 JavaScript 解析和执行顺序不重要时,可以添加 async 属性或 defer 属性。(重点)
(1)构建DOM树
HTML是一种描述性质的标志性文本,通过渲染引擎解析成为内存中的DOM树结构(DOM的树结构)
[1]译码规则解析文本流,生成初始解析文本(Bytes -> Characters)
[2]标准解析规则,参照HTML标准实现Tokens化,解析HTML生成详细的规则信息
[3]构建DOM的Node,对node添加特定属性底层指针标识,确定位置与关联内存指向,建立联系(浏览器底层一般是c++编写)
[4]构建DOM树(按Node属性信息生成)
(2)样式计算(构建CSSOM树)
CSS 对象模型和 DOM 是相似的。DOM 和 CSSOM 是两棵树. 它们是独立的数据结构
转换CSS样式表为styleSheets,计算DOM节点的样式,将一些CSS语义规范转换成标准的styleSheets形式,譬如rem,blue的rgba转换,字符标识。(更高层的样式抽象,less,样式变量等属于编译器解析范畴不是浏览器引擎执行的)
属性值标准化。处理完成后再处理样式的继承和层叠,有些文章将这个过程称为CSSOM的构建过程
(3)布局阶段
创建布局树,计算元素的布局信息
我们可以简单的预见,标识属性为display:none的元素不会被展现。再排除一些头部报文节点,功能性的节点。就可以构建一个包含位置信息的元素布局树。
[1]回流:对DOM的修改引起了DOM中标识的集合尺寸性的变化,需要重新计算几何属性(重生成布局树?)
[2]重绘:不影响计算属性的几何属性,也是回流后的操作,然后会对元素的样式进行重绘。
(4)分层
对布局树进行分层,生成分层树(图层排布,z轴引入)
如果一个节点没有对应的层,那么这个节点就从属于父节点的图层
拥有层叠上下文属性的元素会被提升成单独的一层:
根元素 (HTML),
z-index 值不为 "auto"的 绝对/相对定位元素,
position,固定(fixed) / 沾滞(sticky)定位(沾滞定位适配所有移动设备上的浏览器,但老的桌面浏览器不支持)
z-index值不为 "auto"的 flex 子项 (flex item),即:父元素 display: flex|inline-flex,
z-index值不为"auto"的grid子项,即:父元素display:grid
opacity 属性值小于 1 的元素(参考 the specification for opacity),
transform 属性值不为 "none"的元素,
mix-blend-mode 属性值不为 "normal"的元素,
filter值不为"none"的元素,
perspective值不为"none"的元素,
clip-path值不为"none"的元素
mask / mask-image / mask-border不为"none"的元素
isolation 属性被设置为 "isolate"的元素
在 will-change 中指定了任意CSS属性(参考 这篇文章)
-webkit-overflow-scrolling 属性被设置 "touch"的元素
contain属性值为"layout",“paint”,或者综合值比如"strict",“content”
作者:winty
链接:https://juejin.cn/post/6844903969693646862
来源:稀土掘金
还有一些引发回流的操作:
- DOM操作(对元素的增删改,顺序变化等);
- 内容变化,包括表单区域内的文本改变;
- CSS属性的更改或重新计算;
- 增删样式表内容;
- 修改class属性;
- 浏览器窗口变化(滚动或缩放);
- 伪类样式激活(:hover等)。
- 常规的一些当前属性引入也会带来回流
可以看出上述的大部分属性都是参与了一些视觉运算的。
(5)栅格化和显示
为每个图层生成绘制列表,并将其提交到合成线程。合成线程将图层分图块,并栅格化将图块转换成位图,合成线程发送绘制图块命令给浏览器进程。浏览器进程根据指令生成页面,并显示到显示器上。
(6)在DOM和CSSOM的基础上,渲染树创建
(这在Webkit内核中被称为renderer或者渲染对象render object,在Gecko内核中被称为框架frame)。渲染树映射除了不可见元素(例如**或者含有display:none;**的标签)外的所有DOM结构。每一段文本字符串都将划分在不同的渲染对象中,每一个渲染对象都包含了它相应的DOM对象以及计算后的样式。换句话讲,渲染树是DOM的直观表示。
(本质就是减少重绘与回流的次数)
(1)浏览器本身会尽可能减少次数
通过队列的缓存出队一次pass绘制多次改变
部分浏览器缓存了一个 flush 队列,把我们触发的回流与重绘任务都塞进去,待到队列里的任务多起来、或者达到了一定的时间间隔,或者“不得已”的时候,再将这些任务一口气出队。但是当我们访问一些即使属性时,浏览器会为了获得此时此刻的、最准确的属性值,而提前将 flush 队列的任务出队。
如果需要知道详细的动画变动过程可以在js语句的执行期间进行即时属性读取强制回流,损失一点性能带来更好视觉体验。
参考:前端开发者应知必会:浏览器是如何渲染网页的 - 知乎 (zhihu.com)
(2)操作优化:
[1]脚本代码中优化避免多次调用DOM执行操作没应当缓存后统一加设,如果不是想看到分段效果,建议如此。
[2]DOM的值以及即时量的值不应当出现在循环中
[3]布局信息改变时候查询,渲染队列强制刷新(浏览器保证即时性)
[4]避免使用table布局元件减少关联等(尽量只对 position 为 absolute/fixed 的元素设置动画)
[5]在页面滚动的时候禁用:hover的样式
参考:浏览器层合成与页面渲染优化 - 掘金 (juejin.cn)
引入一个问题:平常页面出现的白屏丢帧表征特像的问题一般是什么原因引起(渲染进程被占用)
在 DOM 树中每个节点都会对应一个渲染对象(RenderObject),当它们的渲染对象处于相同的坐标空间(z 轴空间)时,就会形成一个 RenderLayers,也就是渲染层。渲染层将保证页面元素以正确的顺序堆叠,这时候就会出现层合成(composite),从而正确处理透明元素和重叠元素的显示。
作者:WecTeam
链接:https://juejin.cn/post/6844903966573068301
[1]渲染对象:渲染对象依然维持着 DOM 树的树形结构。一个渲染对象知道如何绘制一个 DOM 节点的内容,它通过向一个绘图上下文(GraphicsContext)发出必要的绘制调用来绘制 DOM 节点。
[2]渲染层:这是浏览器渲染期间构建的第一个层模型,处于相同坐标空间(z轴空间)的渲染对象,都将归并到同一个渲染层中
[3]图形层:GraphicsLayer 其实是一个负责生成最终准备呈现的内容图形的层模型,它拥有一个图形上下文(GraphicsContext),GraphicsContext 会负责输出该层的位图。存储在共享内存中的位图将作为纹理上传到 GPU,最后由 GPU 将多个位图进行合成,然后绘制到屏幕上
[4]合成层:满足某些特殊条件的渲染层,会被浏览器自动提升为合成层。合成层拥有单独的 GraphicsLayer,而其他不是合成层的渲染层,则和其第一个拥有 GraphicsLayer 的父层共用一个。(分离出多个context上下文进行渲染实现)
条件我并不是很懂就不引入了,可以去作者页面看看
合成层的转换有隐式合成和显示合成两种,合成层新的出现会使得渲染层覆盖同样提升–保留相对的顺序(感觉到了一种复杂情况下的包渲染速度亏损)
引入–>[5]层爆炸:合成层达到一定的量级,严重损耗GPU和内存以及页面的性能,消除它的方法就是建立一定的交叠高度
[6]层压缩:浏览器的层压缩机制,会将隐式合成的多个渲染层压缩到同一个 GraphicsLayer 中进行渲染(浏览器自身避免)
[7]合成层为什么会出现:
/1:合成层的位图,会交给GPU合成绘制,提高绘制效率。
/2:重绘只会重绘repaint本身,不会跨层影响。
/3:元素提升为合成层后,transform 和 opacity 才不会触发 repaint,如果不是合成层,则其依然会触发 repaint。
弊端:合成层的内存消耗以及(分任务类型?或者是内存标识*)并且合成层的传递需要到GPU种执行。
Size:合成层的大小,其实也就是对应元素的尺寸;
Compositing Reasons:形成复合层原因,这是最关键的,也是我们分析问题的突破口,比如图中的合成层产生的原因就是交叠问题;
Memory estimate:内存占用估算;
Paint count:绘制次数;
Slow scroll regions:缓慢滚动区域。
作者:WecTeam
链接:https://juejin.cn/post/6844903966573068301
1.动画采用transform实现:(重绘回流带来的不必要消耗以及视觉闪烁)
对于一些体验要求较高的关键动画,比如一些交互复杂的玩法页面,存在持续变化位置的 animation 元素,我们最好是使用 transform 来实现而不是通过改变 left/top 的方式。这样做的原因是,如果使用 left/top 来实现位置变化,animation 节点和 Document 将被放到了同一个 GraphicsLayer 中进行渲染,持续的动画效果将导致整个 Document 不断地执行重绘,而使用 transform 的话,能够让 animation 节点被放置到一个独立合成层中进行渲染绘制,动画发生时不会影响到其它层。并且另一方面,动画会完全运行在 GPU 上,相比起 CPU 处理图层后再发送给显卡进行显示绘制来说,这样的动画往往更加流畅。
作者:WecTeam
链接:https://juejin.cn/post/6844903966573068301
来源:稀土掘金
2.减少隐式合成生成的合成层(调节页面排布顺序或者z-index高度避免)
3.减小合成层的尺寸(这也行)
JavaScript引擎是一个专门处理JavaScript脚本的虚拟机,一般会附带在网页浏览器之中,比如最出名的就是Chrome浏览器的V8引擎(下图)跟操作系统类似的运行结构,但是可以调用web浏览器与操作系统提供的web API实现一些其他效果
执行栈用于函数变量的暂时存储,在调用函数时会生成栈帧,一些内存结构被存储在堆内存中,等待调用。
(一个tab页内进程间的异步线程协同处理,分别调度处理几个宏任务队列,将其回调实现放置在事件循环队列task 和micro中根据调用api判断)
Event Loop是由javascript宿主环境(像浏览器)来实现的;
WebAPIs是由C++实现的浏览器创建的线程,处理诸如DOM事件、http请求、定时器等异步事件;
JavaScript 的并发模型基于"事件循环";
Callback Queue(Event Queue 或者 Message Queue) 任务队列,存放异步任务的回调函数
作者:追风筝的人er
链接:https://juejin.cn/post/6844903606466904078
来源:稀土掘金。
通过轮询判断执行栈为空 调入回调队列任务到执行栈中
浏览器的js引擎中,把任务区从逻辑上分为了宏任务和微任务(外部任务和内部任务)
产生宏任务的外部事件源:–>(event loop中的task中)
1.dom操作引起的重绘和重排缓冲队列。
2.用户的交互调用事件。
3.网络请求处理(xhr/fetch)
4.historyAPI调用
5.定时器,需要外部变量环境。
于是:每一个事件源存在一个调度队列,一个事件循环(eventloop)可以存在多个外部队列(task),不同事件源享用不同优先级(调度算法)
注:一个 event loop里只有一个microtask 队列
产生微任务的外部事件源:
- process.nextTick
- promises
- Object.observe
- MutationObserver
执行规则:
1、首先先执行主程序中的代码 将script压栈进入一个task中
2、执行栈中的函数执行时 产生的异步操作or需要调用Webapi的操作会被(异步线程接到通信处理后,放入event loop中)按照创建的线程实现分配。
3、然后在执行事件循环中宏任务队列的代码之前,要先看看微任务队列中是否为空,如果不为空,则将微任务队列中的任务取出执行。
4、在执行宏任务之前,保证微任务队列中的任务为空。
5、执行界面渲染(非必须)
浏览器的 Javascript 具有自动垃圾回收机制(GC:Garbage Collecation),也就是说,执行环境会负责管理代码执行过程中使用的内存。其原理是:垃圾收集器会定期(周期性)找出那些不在继续使用的变量,然后释放其内存。但是这个过程不是实时的,因为其开销比较大并且GC时停止响应其他操作,所以垃圾回收器会按照固定的时间间隔周期性的执行。
考虑到两个地方的内存占用(在js虚拟引擎中)一个是堆,一个是执行栈。
执行函数创建的变量在执行函数结束后可能通过返回值传递了内存指针,所以不一定可以直接释放(还有一些闭包问题)
要释放无用变量的内存,首先要判断无用变量,那如何判断定义的变量是否无用了呢?
GC追踪变量实现释放:标记清除和引用计数(非常用)
步骤:进入执行环境的变量会被打上标记,在进入时进入环境标记,在离开时离开环境标记。(详)
标记每个值被引用的次数(字面意)当引用为0时,内存指向被回收
存在问题循环引用:属性的属性指向对象主体
GC时阻塞响应其他操作,垃圾回收器会按照固定的时间间隔周期性的执行,所以什么时候触发垃圾回收呢?
思路1:固定事件定时清理 思路2:达到临界条件时候清理(动态化清理区间–防止回收程序固定内存)
1.采用的时标记清除的GC方案
2.问题:但是这样GC的缺陷是,GC时间过长,可能会影响一些需要流程运行的任务。
3.方案(优化策略)
(1)分代回收(Generation GC),目的是通过区分“临时”与“持久”对象;多回收“临时对象”区(young generation),少回收“持久对象”区(tenured generation),减少每次需遍历的对象,从而减少每次GC的耗时。
(2)增量GC方案,这个方案的思想很简单,就是“每次处理一点,下次再处理一点。分段中断处理。
内存泄漏常见
1.意外的全局变量(函数定义中的全局)
2.被遗忘的计时器或回调函数(计时器没释放)
3.闭包
4.没有清理的DOM元素引用(属性引用指向)
(33条消息) 深入理解浏览器垃圾回收机制_CUG-GZ的博客-CSDN博客_浏览器垃圾回收机制
在闭包中使用了大量的变量以及内存,在外部保留了闭包的引用,就会导致其内存保存在堆中无法释放,当外界函数不释放这些引用,或则循环调用时候就会产生内存泄漏。
var funs = [];
function fun0(){
funs.push(getVar()); //外部变量持有闭包的引用
}
setInterval(fun0, 1000);
function getVar(){
var arr = new Array(1000000);
return function(){
console.log(arr);
}
}
V8 把堆分成两个区域——新生代和老生代,并分别使用两个不同的垃圾回收器。
浏览器的垃圾回收机制 - 掘金 (juejin.cn)
因为其采用的是分代回收的方法:
小的内存对象会被分配到新生区,scavenge算法(均分新生代空间使其对半分为两个区域,一半为对象区域,一半为空闲区域)
新建的对象在新生区的对象区域中,在一次函数调用结束后,进行垃圾回收,腾出对象空间到空闲区,进行了空间整理,同时为了使算法的时间复杂度不会很高,降低空间的总体容量是比较不错的选择(待整理完成后,翻转两个区域)
—》老生区为何存在,经过两次回收后还存活的对象,移动到老生区。
主垃圾回收器主要负责老生区中的垃圾回收。除了新生区中晋升的对象,一些大的对象会直接被分配到老生区。因此老生区中的对象有两个特点,一个是对象占用空间大,另一个是对象存活时间长。
遍历标记活动对象(标记阶段就是从一组根元素开始,递归遍历这组根元素,在这个遍历过程中,能到达的元素称为活动对象,没有到达的元素就可以判断为垃圾数据。)
执行垃圾回收算法,便将正在执行的 JavaScript 脚本暂停下来,待垃圾回收完毕后再恢复脚本执行。
让垃圾回收标记和 JavaScript 应用逻辑交替进行,直到标记阶段完成
当解析器发现非阻塞资源,例如一张图片,浏览器会请求这些资源并且继续解析。当遇到一个 CSS 文件时,解析也可以继续进行,但是对于
标签(特别是没有
async
或者defer
属性的)会阻塞渲染并停止 HTML 的解析。尽管浏览器的预加载扫描器加速了这个过程,但过多的脚本仍然是一个重要的瓶颈。
(1)单进程浏览器时代:网络、插件、JavaScript 运行环境、渲染引擎和页面等所有模块都运行。
(2)早期的多进程架构:只是将渲染进程封装进入了沙箱,使其不能与操作系统相联通,同时设立了插件独立的作用进程,与浏览器主进程协同管理整个应用。
最后我们再来看看上面的两个安全问题是怎么解决的: 用多进程架构的额外好处是可以使用安全沙箱,你可以把沙箱看成是操作系统给进程上了一把锁,沙箱里面的程序可以运行,但是不能在你的硬盘上写入任何数据,也不能在敏感位置读取任何数据,例如你的文档和桌面。Chrome 把插件进程和渲染进程锁在沙箱里面,这样即使在渲染进程或者插件进程里面执行了恶意程序,恶意程序也无法突破沙箱去获取系统权限。
一些系统调度层的api可以在沙箱之外实现,通过IPC通讯进行信息传递
(3)现在的多进程架构:
最新的 Chrome 浏览器包括:1 个浏览器(Browser)主进程、1 个 GPU 进程、1 个网络(NetWork)进程、多个渲染进程和多个插件进程。
JS虽然是单线程的,但是通过底层语言的实现,获取了多进程执行的机会。
浏览器进程。主要负责界面显示、用户交互、子进程管理,同时提供存储等功能。
渲染进程。核心任务是将 HTML、CSS 和 JavaScript 转换为用户可以与之交互的网页,排版引擎 Blink 和 JavaScript 引擎 V8 都是运行在该进程中,默认情况下,Chrome 会为每个 Tab 标签创建一个渲染进程。出于安全考虑,渲染进程都是运行在沙箱模式下。
GPU 进程。其实,Chrome 刚开始发布的时候是没有 GPU 进程的。而 GPU 的使用初衷是为了实现 3D CSS 的效果,只是随后网页、Chrome 的 UI 界面都选择采用 GPU 来绘制,这使得 GPU 成为浏览器普遍的需求。最后,Chrome 在其多进程架构上也引入了 GPU 进程。
图形处理器(英语:graphics processing unit,缩写:GPU),又称显示核心、视觉处理器、显示芯片,是一种专门在个人电脑、工作站、游戏机和一些移动设备(如平板电脑、智能手机等)上做图像和图形相关运算工作的微处理器。
网络进程。主要负责页面的网络资源加载,之前是作为一个模块运行在浏览器进程里面的,直至最近才独立出来,成为一个单独的进程。
插件进程。主要是负责插件的运行,因插件易崩溃,所以需要通过插件进程来隔离,以保证插件进程崩溃不会对浏览器和页面造成影响。(缺少专业作用环境)
在edge中管理查看程序。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZEmguP1o-1652963838703)(https://gitee.com/nwesnn/image/raw/master/images/image-20220508162023010.png)]
从一个页面打开的新页面,而新页面和当前页面属于同一站点时,,新页面会复用父页面的渲染进程。官方把这个默认策略叫process-per-site-instance。(同享的资源减耗策略)
对应于页面的一些知识,我们再做一些深层的介绍,虽然上面已经做了渲染的介绍。
GUI渲染线程
- 负责渲染浏览器界面,解析HTML,CSS,构建DOM树和RenderObject树,布局和绘制等。
- 当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行
- 注意,GUI渲染线程与JS引擎线程是互斥的,当JS引擎执行时GUI线程会被挂起(相当于被冻结了),GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。
JS引擎线程
- 也称为JS内核,负责处理Javascript脚本程序。(例如V8引擎)
- JS引擎线程负责解析Javascript脚本,运行代码。
- JS引擎一直等待着任务队列中任务的到来,然后加以处理,一个Tab页(renderer进程)中无论什么时候都只有一个JS线程在运行JS程序
- 同样注意,GUI渲染线程与JS引擎线程是互斥的,所以如果JS执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞。
事件触发线程
- 归属于浏览器而不是JS引擎,用来控制事件循环(可以理解,JS引擎自己都忙不过来,需要浏览器另开线程协助)
- 当JS引擎执行代码块如setTimeOut时(也可来自浏览器内核的其他线程,如鼠标点击、AJAX异步请求等),会将对应任务添加到事件线程中
- 当对应的事件符合触发条件被触发时,该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理
- 注意,由于JS的单线程关系,所以这些待处理队列中的事件都得排队等待JS引擎处理(当JS引擎空闲时才会去执行)
定时触发器线程
- 传说中的
setInterval
与setTimeout
所在线程- 浏览器定时计数器并不是由JavaScript引擎计数的,(因为JavaScript引擎是单线程的, 如果处于阻塞线程状态就会影响记计时的准确)
- 因此通过单独线程来计时并触发定时(计时完毕后,添加到事件队列中,等待JS引擎空闲后执行)
- 注意,W3C在HTML标准中规定,规定要求setTimeout中低于4ms的时间间隔算为4ms。
异步http请求线程
- 在XMLHttpRequest在连接后是通过浏览器新开一个线程请求
- 将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将这个回调再放入事件队列中。再由JavaScript引擎执行。
上面是渲染进程的多个线程
注:*为验证性推断和未解决的猜想
谷歌浏览器自带的调试工具:
- Elements:可查看网页页面代码(修改只是当前使用有效),也可实时调试修改页面ccs代码样式。
- console:记录开发者开发过程中的日志信息,也可在里面写js代码。一般页面运行时js报错都是可以在这里看到反馈和定位bug原因及其位置。
- Sources:断点调试JS,可以查看程序代码执行的过程,断点调试对于每一个程序员来说可是很重要。
- Network:从发起网页页面请求开始,分析HTTP请求后得到的各个请求资源信息(“小编有时候就利用这下载一些网站不给下载的在线视频,比如教学视频”)。
- Timeline:记录并分析网站的生命周期所发生的各类事件,分析渲染js执行的每一个阶段。
- Application:记录网站加载的各个资源信息。
- Security:判断网页是否安全。
- Audits:对当前网页的网络利用及网页性能进行检测,并给出一些优化建议。
BrowserStack
地址:https://www.browserstack.com/
现在拥有各自内核的浏览器越来越多,各自的特性也千差万别。如果作为一个前端攻城师想要检测网站在不同的操作系统和移动平台下的各种浏览器的兼容性,那是相当痛苦不堪的。看到有在自己电脑上装虚拟机配置各种环境,有自己的电脑上组建好这样的环境,然后一一测试,可是人的精力毕竟有限,我们没法在同一台电脑上装那么多系统,那么多浏览器的。幸好出了个 BrowserStack 是前端的福音呀。
BrowserStack 是一款提供网站浏览器兼容性测试的在线云端测试工具,从而开发测试人员不必再准备很多虚拟机或者手机模拟器。