浏览器页面的加载过程?
- 浏览器在加载页面的时候会用到 GUI 渲染线程(渲染浏览器界面的HTML元素)和 JavaScript 引擎线程(负责处理JavaScript脚本程序);
由于JavaScript 引擎线程
在处理过程中会改变界面结构和样式,所以在JavaScript 引擎线程
执行时,GUI 渲染线程
会被挂起。
举个🌰:
1)当我们打开一个网页的时候,浏览器会从服务器中获取HTML内容。
2)当浏览器获取到HTML内容,会从上到下解析HTML的元素。
3)<head>
中元素首先会被解析,此时浏览器还没有渲染页面。<head>
中有用于描述页面数据的元素,也有用于引用外部资源的<link>
元素(图片、css等)以及指向外部资源的<script>
元素
4)浏览器解析到<head>
元素,会暂停解析并下载JavaScript脚本。
5)当JavaScript脚本下载完成之后,浏览器的控制权会交给JavaScript 引擎线程
,当脚本执行完成之后,控制权会交给GUI 渲染引擎
,渲染引擎会向下解析HTML页面。
6)浏览器会解析<body>
元素,页面开始渲染
造成的问题:
如果外部脚本加载时间很长(比如一直无法完成下载),就会造成网页长时间失去响应,浏览器就会呈现“假死”状态,用户体验会变得很糟糕。
解决问题:
可以将JavaScript脚本放在 <body>
最后面。这样可以避免资源阻塞,页面得以迅速展示。也可以使用defer/async/preload
等属性来标记<script>
,从而控制JavaScript的加载顺序。
虚拟DOM
- 浏览器在加载页面的时候会用到 GUI 渲染线程(渲染浏览器界面的HTML元素)和 JavaScript 引擎线程(负责处理JavaScript脚本程序);
由于JavaScript 引擎线程
在处理过程中会改变界面结构和样式,所以在JavaScript 引擎线程
执行时,GUI 渲染线程
会被挂起。
举个🌰:
1)当我们打开一个网页的时候,浏览器会从服务器中获取HTML内容。
2)当浏览器获取到HTML内容,会从上到下解析HTML的元素。
3)<head>
中元素首先会被解析,此时浏览器还没有渲染页面。<head>
中有用于描述页面数据的元素,也有用于引用外部资源的<link>
元素(图片、css等)以及指向外部资源的<script>
元素
4)浏览器解析到<head>
元素,会暂停解析并下载JavaScript脚本。
5)当JavaScript脚本下载完成之后,浏览器的控制权会交给JavaScript 引擎线程
,当脚本执行完成之后,控制权会交给GUI 渲染引擎
,渲染引擎会向下解析HTML页面。
6)浏览器会解析<body>
元素,页面开始渲染
造成的问题:
如果外部脚本加载时间很长(比如一直无法完成下载),就会造成网页长时间失去响应,浏览器就会呈现“假死”状态,用户体验会变得很糟糕。
解决问题:
可以将JavaScript脚本放在 <body>
最后面。这样可以避免资源阻塞,页面得以迅速展示。也可以使用defer/async/preload
等属性来标记<script>
,从而控制JavaScript的加载顺序。
虚拟DOM产生和设计思想?
- 随着应用程序越来越复杂,需要监听事件和在事件回调中更新DOM的操作越来越多,频繁的操作DOM会导致页面频繁的计算和渲染,导致不小的开销,于是虚拟DOM的想法被提出
虚拟DOM的设计思想:
1)用JavaScript对象模拟DOM树,得到一颗虚拟的DOM树;
2)当页面数据变更时,生成一颗新的DOM树,比较新旧虚拟DOM的差异;
3)把差异应用到新的DOM树上。
事件委托?
- 浏览器从各个页面中接收事件的顺序包括
事件捕获阶段
、事件目标阶段
、事件冒泡阶段
。事件委托
利用事件冒泡机制,将子元素的事件委托给父元素进行处理。解决的问题:
1)绑定子元素会绑定很多次事件,绑定父元素只需要一次。
2)将事件委托给父元素,这样对子元素的增加、删除等操作,都不要重新绑定。
优点
1) 使用事件委托,大量减少浏览器对元素的监听,前端性能优化最基础的一个办法。⚠️注意!
在document.body
上进行事件委托,会带来额外的问题。
1)由于浏览器在进行页面渲染的时候,会有合成的步骤,合成的过程会将页面分成不同的合成层,而用户与浏览器进行交互的时候需要接收事件,此时,浏览器会将具有事件处理的区域进行标记,被标记的区域与主线程进行通信,而将事件绑定在document.body
上,则整个页面都会进行标记,即使我们的页面不关心某些部分的用户交互,合成器线程也必须与主线程进行通信,并在每次事件发生时进行等待。这种情况,我们可以使用passive: true
选项来解决。passive: true
不会对浏览器的默认行为说no,它可以在两个线程里同时执行监听器中的 JavaScript 代码和浏览器的默认行为。
结束语
总结:大功告成✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️