html图片加载不出来_探索浏览器对HTML的渲染原理(过程)

探索目的

为了更好地优化我们前端页面的性能,特对基础原理进行考究

大致过程

从浏览器获取到HTML文件开始,浏览器会经历解析、渲染、交互三大阶段;

解析

浏览器会在收到HTML文件的第一次响应包后开始解析(即使该HTML大于14kb),解析过程包括DOM树和CSSOM树的构建、资源的预加载(通过预加载扫描器异步加载)、JavaScript 编译以及构建辅助功能树。DOM包含了页面的所有内容,CSSOM包含了页面的所有样式。

渲染

渲染过程包括Style、Layout、Paint以及还可能会有Compositing这些阶段,
渲染器会在DOM树和CSSOM树构建好之后,将两棵树组合成一个render树,这个过程会计算所有可以显示标签的样式,可以显示的标签包括从body开始(包括body)没有display: none的所有节点,包含带有visibility: hidden的节点。4d2b88f27fac96db3944adf15d196413.png

布局是浏览器从根节点开始遍历整棵render树,计算每个节点的尺寸和位置;第一次确定节点的大小和位置称为布局。随后对节点大小和位置的重新计算称为回流。如果布局完成后有图片加载完成并且该图片没有指定大小,这样就会造成回流。
绘制是将布局阶段生成的render树的多有节点转换成屏幕上的实际像素,包括文本、颜色、边框、阴影和替换的元素(如按钮和图像)。

在这个过程中,浏览器会将布局树中的元素分解为多个层,将内容提升到GPU上的层(脱离CPU上的主线程),从而提高绘制和重新绘制的性能。每一个带有一些特定的CSS属性的元素和一些特定标签元素都可以实例化一个层,像和元素,以及任何带有opacity``,3D转换will-changeCSS属性的元素都会和它们的子节点单独绘制一个层,当然,如果子节点满足以上条件则会再单独实例一个层。
浏览器针对处理CSS动画和不会很好地触发重排(因此也导致重新绘制)的动画属性进行了优化。为了提高性能,可以将被动画化的节点从主线程移到GPU上。将导致合成的属性包括 3D transforms (transform: translateZ(), rotate3d(),etc.),animating transform 和 opacityposition: fixedwill-change,和 filter。一些元素,例如  和 ,也位于各自的图层上。将元素提升为图层(也称为合成)时,动画转换属性将在GPU中完成,从而改善性能,尤其是在移动设备上。

交互

当我们看到页面显示出来后,整个页面的所有渲染工作可能并没有完成,因为这时页面可能还无法进行点击,滚动,触摸等操作,因为这个时候可能还有js没有执行完,也就是主线程仍在占用状态,特别是像绑定了window.onLoad这种的js逻辑。
在测试页面性能的时候,有一项重要的指标就是TTI(Time to Interactive)是从第一个请求导致DNS查找和SSL连接到页面可交互时所用的时间。

webkit 渲染流程图

703c9b6d4d0cf12965b680c15df2e115.png

gecko 渲染流程图

a4388b3fc3b5d010e12887e0ac734bf2.png

问题探究

CSS的加载会阻塞页面的渲染吗?

提出假设

我们来分析一下,从上面的页面渲染流程来看,HTML的渲染过程是将解析阶段生成的DOM树和CSSOM树组合成一个render树,那么CSS的加载肯定是会阻塞CSSOM树的建立,那没有CSSOM树也就没办法合成render树,因此也就没办法渲染,所以CSS的加载是会阻塞页面的渲染的。

验证假设

我们来写段代码测试一下

span style="box-sizing: border-box;">><html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Documenttitle><script>console.log('走js这里了')script><link rel="stylesheet" href="https://www.google.com/123123.css">head><body><h1>Hello World!h1>body>html>

执行结果

215ebfb406399b3742fd7a856c5f7a08.gif

从结果来看,当css文件请求没有结束之前页面是空白的,等css加载失败后页面才显示内容,这就说明我们的假设成立。

在这个结果中,也可以看出css没有加载结束之前,js被执行了,那我们将js代码写在link标签的后面,他还会被执行吗?

css加载会阻塞j后面的s运行吗?

直接测试

span style="box-sizing: border-box;">><html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Documenttitle><link rel="stylesheet" href="https://www.google.com/123123.css"><script>console.log('走js这里了')script>head><body><h1>Hello World!h1>body>html>

执行结果

c262c8c7b1a2da6664f45b747ab2d415.gif

结果来看js是在css加载后执行了,则说明css的加载阻塞了后面js的运行。

那么如果不是内嵌的js,使用src来引入一个js文件呢?

span style="box-sizing: border-box;">><html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Documenttitle><script src="./test000.js">script><script>console.log('走link标签前面的内嵌js了')script><link rel="stylesheet" href="https://www.google.com/123123.css"><script src="./test111.js">script><script>console.log('走link后面的内嵌js了')script>head><body><h1>Hello World!h1>body>html>

在test000.js中这样写

console.log('This is test000.js')

在test111.js中这样写

console.log('This is test111.js')

执行结果

cf1060a6d2767e2f555098368438b88a.gif

结果是一样的

那如果使用了defer或者async属性呢?

span style="box-sizing: border-box;">><html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Documenttitle><script src="./test000.js" defer>script><script>console.log('走link标签前面的内嵌js了')script><link rel="stylesheet" href="https://www.google.com/123123.css"><script src="./test111.js" defer>script><script>console.log('走link后面的内嵌js了')script>head><body><h1>Hello World!h1>body>html>

结果是什么样呢?

7ab39dc8b5ab5f8bde4246a774a3aafd.gif

那要是把defer全部换成async呢?

67684dc536eb04594c2422a02a163207.gif

为什么会这样?
因为defer和async都可以使浏览器异步加载并解析执行js文件,所以link标签不能阻塞js的文件的执行,你可能会说,那为什么defer被阻塞了呢?我们知道defer和async有点不一样的,在没有内嵌js时,defer修饰的js会按照DOM先后顺序依次执行,async则是先加载完成的先执行;在有内嵌js时,无论是defer还是async都会等待内嵌js执行完才会去执行它们,如果没有这两个属性就会按照DOM中的顺序依次执行各个js。

在实际中,我们会使用一些方法来应对css加载阻塞js执行的问题,比如把js放在link之前然后使用DOMContentLoaded方法,或者之前在jQuery中常用的ready方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值