前几天刚刚学完css,这两天开始进军js部分,看了一些老师们的操作视频,有理论的,有展示的现象得到的一些结论的东西,第一遍看起来,其实很没有感的,因为满脑子都在引入概念,跟着老师走,自己其实在被动接受,也不敢自己跟着操作,怕跟不上节奏了,都怪自己是计算机界的一种小白了。所以心里发誓,有必要把c和Java好好下来研究研究了。废话不多说,我第二遍看视频,一遍看,一遍看代码,思考,才有了感的。
浏览器的渲染机制
其实这个就是理论上的东西,最具有逻辑性了,所有很容易明白,理解,就算是拿来主义,也不可耻,因为本来就是如此,除非是作为一个浏览器开发者才会以此为耻的吧。
记得学HTML时,也有一个机制攻略的,那个就是从URL输入到浏览器展示页面的一个总体的思路过程了,资料可回顾参考《初学从url到展示网页的感官》,这个过程里的其中一部分就是现在要讲的浏览器渲染机制了。当时只是总揽全局,并且在逻辑上明白了一个思路,现在学到这里,我们需要细细拿出这 其中一部分,好好地品味下内在的想法如何实现的。
具体思路:浏览器发出请求,服务器收到后,回复给html文件,浏览器接收并解析:解析到css文件,发请求要css文件;解析到js文件,发请求要js文件。
html文件在第一时间解析完成,在浏览器的逻辑下,生成DOM树。
同时,接收到css文件后,解析,生成cssOM树,也就是样式树。
然后两棵树互相联系作用,一个是角色,一个是角色的各种属性,搭配起来,形成了一颗渲染树,这棵树也是存在于浏览器机制的逻辑里的,只是为了让人更明白,所有,我觉得把这个理念概念化语义化了哦。
然后浏览器通过渲染树,对各个节点标签进行布局计算,计算完了,就开始画画,画好了就显示成页面了。
流程图如下:
其实在这里,我就感到奇怪了,比如js文件呢?还有实际操作的话,这个理论的应用层面在浏览器上会有什么样的现象和问题?这就是我下面想要说的,不过我才疏学浅,时间很紧,只能把一些现象说出来,具体的一些结论,我能查出根据的会觉得会好,模凌两可的只能作为自己的猜想了。说真的,浏览器其实原理一样,但是耐不住开发人员的牛脾气,所以,原理仅仅是在开发人员眼里的一条让普通人更容易明白的小路而已,实际上,各种开发权限让浏览器很搞怪,有时候让你觉得,跟所学的知识相悖,根本没有什么大一统的结论啊,比如,说js文件一加载就立刻执行,然后出来了一个异步加载;谷歌浏览器是白屏的效果,可为啥火狐的是闪烁了?反正,明白一句话,实践出真知,越认识到错误,越接近真实,好比小学里0是最小的,那是对的,到初中你再说就错了。
css文档流的分析
首先css文件,这个研究对象,起初啊,第一次跟着老师走的时候,是蒙的,没有思路,只是跟着他走,模仿他的思路,分析方法,现象验证,总结,这一套路吧,那时候憋屈啊,自己讨厌自己没思路,听得云里雾里的,只是记录了一些所谓的结论和感官,以及对研究对象的一种很深很厌恶的感官了。第二遍看视频,对照着代码,才能有自己的思路,而且有点成就感的是,对老师最后的几个现象反而得不出大一统的结论想到了一些东西,心里还是比较满足的。
我按我的猜想走吧。
这个研究对象啊,在html中css的位置有两个,一个在head中,一个在body里。在这里,我先对css的添加样式做个说明,不管是外部链接还是内部的,还是几乎废弃的在html标签里写的,首先直接说最优的就是内部的,不用在发请求像外部的要文件了,加载快,省事。抛去这个因素,再从位置入手。
先说一下,大一统的结论,从老师的代码实验中得到了,一个HTML文件,几乎所有的css文件都是同时开始加载的,不论位置。当然,除去几乎要费了的那种在html标签里写的css,不是说它不适合或适合,只是说它没法证明。
在head里的css,最明了,简单。这里的css文件是全部加载完成后才开始执行,而且伴随着执行,html的内容开始出现,符合渲染树的步骤。
其实,我比较喜欢放这里,觉得很区块化,而且,确实效果上很好的。
在body里的css文件,加载时遵循顺序,而无论上下方是何种html标签,都会在它们之后执行,谷歌嘛。但是关于图片的加载时机的影响,这是我看到代码实验的一种猜想,仅仅限于谷歌浏览器上,尤其是当css上面或者下面有图片或其他加载数据大时,影响会很明显,图片会显得不受css文件影响一样,该加载就加载。这里有一个因素是js文档在head部时产生的阻碍作用,图片会在所有css文件执行后加载,而把阻碍的js移到body底部时,图片就好像不受css文件影响一样,该加载就加载,也不等css文件了。
反正,对于研究加载的话,显得很苍白,特立独行的是js文件,最具主动性,它只会影响HTML和css,而自身不受它们影响。css加载几乎是同时加载的,它影响html的一些元素如图片的加载时机的。执行的话,就比较简单了,js文件加载后立即执行,css文件一个一个地执行,这里非要针对影响的话,我又查了下资料,貌似跟请求有关——
css文件的下载和渲染是同步的吗? 还是先下载完, 再渲染?
不确定下载过程中是否同步做词法分析parseCss,但是可能性很大,毕竟是种无损失的优化方案,但是最终肯定需要下载完再layout生成渲染树,进而渲染。
css文件的下载&执行 和 html文件的下载&执行同步吗?
并行的。但是需要注意一些限制,比如一个域名下最大并发6个请求,再多就得串行。
图形的加载 和 html文件的下载/执行同步吗, 音视频呢, 别的资源呢?
同上。
有没有可能出现html文件/图片/css文件/js文件同时下载的情况?
常态。
有没有可能出现html/css文件/js文件同时执行的情况?
html parse和css parse是并行的,两者完成后才会layout、paint,新的css挂载会延迟layout、paint。js parse会阻塞html parse ,所以后面的layout、paint一定不会同时执行。
作者:钱多多
链接:https://www.zhihu.com/question/59024365/answer/161615976
来源:知乎
然后,再说说无样式加载,白屏,闪烁现象——这是浏览器加载与显示页面方式不同造成的。
谷歌呢是当发现<link rel ="stylesheet"> 后立即停止渲染,在所有css加载完成之前页面上不会有任何内容,所有白屏了。火狐呢有意思了,:<head>标签中的<link rel ="stylesheet">与Chrome和Safari中完全一致,这些link标签全部加载完之前,页面上不显示任何内容,而<body>中的内容则不阻塞任何内容显示,也就是说,放<body>内,先渲染没有样式的,再渲染有样式的。
repaint和reflow
- 简介
repaint主要是针对某一个DOM元素进行的重绘,reflow则是回流,针对整个页面的重排。字面意思来说:repaint就是重绘,reflow就是回流。repaint和reflow的目的是:展示一个新的页面样貌。 - 触发
reflow是只要涉及到页面排版和布局变动的,元素本身布局变动,style变化,就会触发,而且功耗很大。repaint是除了上述情况之外,还有就是标签本身的属性渲染发生变化,显示效果不一样时,也会触发,比如颜色变化,不过功耗小得多。 - 避免功耗问题
能不改就不改,尽量避免reflow,这就是想法了。
比如说经常有些刷新的对象啊标签啊,比如动画效果,对象一变化,整个页面就相当于刷新了,所以,变动影响最小的就是把对象搞成绝对定位,或其他,反正脱离文档流就行,并且如动画一类的可控变化,让变化频率变小。
多用class选择器,减少计算的量,精简css的逻辑量,然后从这个角度入手,就是想办法把css树结构变得小巧,所以DOM元素能不用css的就不用,所以减少外部链接引入css文件。
说了这么多,都是跟css有关的,心里有点意思的,莫名其妙哦,这可是在学js啊!只能说明,学css时,自己太蠢,仅仅当成了静态页面,给它们化妆打扮了,自以为可以为所欲为,可是站在更广阔的大局观,看到的风景却是很不一样的。这里的css里的东西,我其实偷懒了,时间关系,根本没有自己把实例一一展示,我仅仅是通过视频里的代码展示推出了一些猜想,并自觉地认为,这仅仅是一个谷歌浏览器啊,那IE,火狐等等其他的呢?难道都要一一验证?我暂时没有时间了,只能说这个是未完结版,待续,,,
js文件的分析
终于到了新鲜货了哦。不过js文件其实算是比较有原则的吧,毕竟它是为了实现功能和效果的,相比前两者来说,却是有些特立独行。相比于如今的宽带,研究白屏或者内容闪烁,比较无聊在于,几乎没有这种显示体验了,研究的意义在于,哦,让我明白浏览器的渲染逻辑的更清晰化更具有逻辑,出来了一种现象,哦,我可以解释,我懂得!所有我觉得我是前端工程师!包括js文件在整个运行过程中的位置,我还是有必要说说滴。
js文件的研究,就不那么俗套了,直接说位置放在head和body中 的情况吧。
先说,一般情况下,大一统的结论就是,加载了js文件,立刻执行,所以会阻碍后面的文件的执行,也会阻碍后面的内容的展示的。所以,一般情况下,都会把这个家伙放到body的最后,反正它就是个功能效果,对页面的显示体验影响不大,那就先让页面显示出来,最后再加载页面的功能效果。并且js文件有作用元素,如果元素还没出来,js先执行了,就会报错。
如果放head里,不用说了,按标签顺序来,到了它这里,开始加载,加载的话不会影响其他哥们的加载,但是加载 完了,就立刻执行,执行时,它下面的css不能执行,html内容不能展示的。
在body里的js文件嗯,一般来说,应该也是跟head里的差不多的,但是偏偏有特例,嗯,在谷歌里,body里的js文件,有一个很有意思的现象,所以不是结论,视频里老师称之为谷歌的自己的优化——body里的js文件的first child,嗯就是大儿子的意思,有特殊待遇,在比其他的js兄弟们更早地加载执行,如何更早?几乎相当于html解析到它这里时,做了个停顿,判断处理,一秒左右,就开始加载了,这不算特殊。从除了它之外,其他的兄弟们受到队伍前面的css文件执行的阻碍,前面的执行完了,它们才开始加载,所以大儿子是个异类。
然后,说到底,研究js如果太钻牛角尖到具体的加载时机上,其实太复杂了,这仅仅是一个谷歌,还是那句话,不是浏览器开发者,就不懂得制定规则,只能按规则来,但是一个开发者一个规则,乱七八糟的,对初学者甚至于仅仅是个前端层次的大局来说,很无力,尤其是现在网速如此发达了,我们应该更关注于一些最基本的运行原理,比如js的阻碍效果,如何优化?做到看到现象,能够模拟,解析出来逻辑,就可以了,哪里有那么多大一统的总结?
异步加载
defer 和 async
避免js文件的加载和执行挡路,于是有两个属性。
defer 和 async 在网络读取(下载)这块儿是一样的,都是异步的(相较于 HTML 解析)。
它俩的差别在于脚本下载完之后何时执行,显然 defer 是最接近我们对于应用脚本加载和执行的要求的。
关于 defer,它是按照加载顺序执行脚本的,这一点要善加利用。
async 则是一个乱序执行的主,反正对它来说脚本的加载和执行是紧紧挨着的,所以不管你声明的顺序如何,只要它加载完了就会立刻执行。
仔细想想,async 对于应用脚本的用处不大,因为它完全不考虑依赖(哪怕是最低级的顺序执行),不过它对于那些可以不依赖任何脚本或不被任何脚本依赖的脚本来说却是非常合适的,最典型的例子:网页的广告。
此处拿来主义《defer和async的区别》。