浏览器页面的加载过程
-
浏览器在加载页面的时候会用到GUI渲染引擎和JavaScript引擎线程,其中GUI渲染引擎负责渲染浏览器界面HTML元素;JavaScript引擎线程负责处理JavaScript脚本程序
-
网页加载流程如下:
1.1当我们打开网页的时候,浏览器会从服务器中获取HTML内容
1.2浏览器获取到HTML内容后,开始从上到下解析HTML元素
1.3head元素内容会先被解析,此时浏览器还没有开始渲染页面(head元素中有包含描述页面元数据的meta元素,还有一些link元素涉及到外部资源,如图片和CSS样式,此时浏览器会向服务器
并行
请求CSS样式、图片或视频。继续渲染后面的代码;当服务器返回图片文件或者视频文件的时候,浏览器会重新渲染这部分代码。head元素中还包含不少的script元素,通过src属性指向外部资源)1.4当浏览器解析到步骤3(script的src属性指向外部资源),会暂停解析并下载JavaScript脚本
1.5当JavaScript脚本下载完成后,浏览器的控制权交给JavaScript引擎。当脚本执行完成后,控制权会交回给渲染引擎,渲染引擎继续往下解析HTML页面
1.6此时,body元素内容开始被解析,浏览器开始渲染页面
因此,如果外部脚本加载时间很长,就会造成网页长时间失去响应,浏览器就会呈现“假死”状态,用户体验很差。为了提高网页性能,我们通常将JavaScript脚本放在body的最后面;将样式表放在head最前面
我们也可以通过defer/async/preload等属性来标记script标签,来控制JavaScript的加载顺序。
- script标签的 defer/async/preload 属性
<script src='xxx.js' defer></script>
设置defer属性会并行下载JavaScript脚本,等页面全部加载完成以后才会按顺序执行
执行过程为:浏览器解析HTML页面,解析过程中发现带有defer属性的script标签,浏览器继续往下解析HTML页面同时并行下载script标签中的外部脚本。浏览器完成解析HTML页面后再执行下载的脚本
<script src='xxx.js' async></script>
设置async属性将不会依赖于任何js和css的执行。js脚本下载完成之后会立刻执行,不能保证按照书写的顺序执行,哪个脚本先下载完成就先执行哪个脚本
执行过程为:浏览器解析HTML页面,解析过程中发现带有async属性的script标签,浏览器继续往下解析HTML页面同时并行下载script标签中的外部脚本。脚本下载完成后,浏览器暂停解析HTML页面开始执行下载的脚本,脚本执行完毕后浏览器恢复解析HTML页面
一般来说,如果脚本之间没有依赖关系就用async属性;如果脚本之间有依赖关系就用defer属性。如果同时使用async和defer属性,defer属性失效。
HTML和DOM
浏览器用HTML来描述网页的结构并渲染;DOM用来获取网页的结构并进行操作
随着应用程序越来越复杂,DOM操作越来越频繁,频繁的DOM操作会导致页面频繁的进行计算和渲染,导致不小的性能开销。于是虚拟DOM被提出
虚拟DOM是用来模板真实DOM的中间产物,它的设计大致分为以下3个过程:
1.用JavaScript对象模拟DOM树,得到一颗虚拟DOM树
2,当页面数据变更时,生成新的虚拟DOM树,比较新旧两颗虚拟DOM树的差异
3.把差异应用到真正的DOM树上
事件委托
浏览器中各个元素从页面中接收事件的顺序包括:事件捕获阶段、目标阶段、事件冒泡阶段。其中,基于事件冒泡机制,我们可以实现将子元素的事件委托给父元素来进行处理,这便是事件委托
使用事件委托的方式,我们可以大量减少浏览器对元素的监听,也是前端性能优化中比较简单和基础的一个做法
需要注意的是,如果我们直接在document.body上进行事件委托,可能会带来额外的问题