这一段的工作涉及到了提高网站的Google评分,以前对浏览器的渲染机制不是很了解这段时间了解了一下,记录下来以便日后查阅。
浏览器的加载、解析、渲染流程
1.用户访问网站,会根据用户提供的域名查找对应的IP地址,系统会向对应IP地址的网络服务器发送一个http请求。
2.网络服务器解析请求,并发送请求给数据库服务器。
3.数据库服务器将请求的资源返回给网络服务器,网络服务器解析数据,并生成html文件,放入http response中,返回给浏览器。
4.浏览器解析 http response。
5.浏览器解析 http response后,需要下载html文件,以及html文件内包含的外部引用文件,及文件内涉及的图片或者多媒体文件。
注意:1.加载过程中遇到css或者图片,不影响html的加载,因为是异步请求。但是文档加载过程中遇到js文件,html文档会挂起渲染(加载解析渲染同步)的线程,不仅要等待文档中js文件加载完毕,还要等待解析执行完毕,才可以恢复html文档的渲染线程。(JS有可能会修改DOM,最为经典的document.write,这意味着,在JS执行完成前,后续所有资源的下载可能是没有必要的,这是js阻塞后续资源下载的根本原因。办法:可以将外部引用的js文件放在</body>前。)
2.即使css文件的加载不影响js文件的加载,但是却影响js文件的执行,即使js文件内只有一行代码,也会造成阻塞。(原因:可能会有 var width = $('#id').width(),这意味着,js代码执行前,浏览器必须保证css文件已下载和解析完成。这也是css阻塞后续js的根本原因。)
预加载技术:
DNS优化:1.一个是减少DNS的请求次数2.进行DNS预获取 。
DNS Prefetch:DNS Prefetching 是让具有此属性的域名不需要用户点击链接就在后台解析,而域名解析和内容载入是串行的网络操作,所以这个方式能 减少用户的等待时间,提升用户体验 。
默认情况下浏览器会对页面中和当前域名(正在浏览网页的域名)不在同一个域的域名进行预获取,并且缓存结果,这就是隐式的 DNS Prefetch。如果想对页面中没有出现的域进行预获取,那么就要使用显示的 DNS Prefetch 了。
目前大多数浏览器已经支持此属性,支持版本如下:
– Safari: 5+
– Chrome: All
– Firefox: 3.5+
– Opera: Unknown
– IE: 9+ (called “Pre-resolution” on blogs.msdn.com)
其中 Chrome 和 Firefox 3.5+ 内置了 DNS Prefetching 技术并对DNS预解析做了相应优化设置。所以 即使不设置此属性,Chrome 和 Firefox 3.5+ 也能自动在后台进行预解析 。
需要注意的是,虽然使用 DNS Prefetch 能够加快页面的解析速度,但是也不能滥用,因为有开发者指出 禁用DNS 预读取能节省每月100亿的DNS查询 。
如果需要禁止隐式的 DNS Prefetch,可以使用以下的标签:
1 |
|
preconnect不光会解析DNS,还会建立TCP握手连接和TLS协议(如果需要)
<link rel="preconnect"href="http://css-tricks.com">
注意浏览器的支持:
1.解释href的属性值,如果是合法的URL,然后继续判断URL的协议是否是http或者https否则就结束处理。
2.如果当前页面host不同于href属性中的host。
3.继续进行连接
prefetch
能够让浏览器预加载一个资源(HTML,JS,CSS或者图片等),可以让用户跳转到其他页面时,响应速度更快。 一般形式就是
<link rel="prefetch" href="//example.com/next-page.html" as="html" crossorigin="use-credentials">
<link rel="prefetch" href="/library.js" as="script">
浏览器会在空闲的时候,下载main.js, 并缓存到disk。当有页面使用的时候,直接从disk缓存中读取。其实就是把决定是否和什么时间加载这个资源的决定权交给浏览器。
注意:如果prefetch还没下载完之前,浏览器发现script标签也引用了同样的资源,浏览器会再次发起请求,这样会严重影响性能的,加载了两次,所以不要在当前页面马上就要用的资源上用prefetch,要用preload。
Preload
浏览器会在遇到如下link标签时,立刻开始下载main.js(不阻塞parser),并放在内存中,但不会执行其中的JS语句。 只有当遇到script标签加载的也是main.js的时候,浏览器才会直接将预先加载的JS执行掉。
<link rel="preload" href="/main.js" as="script">
defer
defer的执行时间是在所有元素解析完成之后,DOMContentLoaded 事件触发之前。
async
async的执行时间是在当前JS脚本下载完成后,所以多个async script是执行顺序是不固定的。async只能用于加载一些独立无依赖的代码,比如Google Analysis之类。
css优化
首页CSS内联,非必要CSS异步加载,首页用到的CSS内联写在<head>
中,其余CSS均采用异步加载,可以采用这种自己实现的加载CSS的方法,在合适的需要时加载需要的css
function LoadStyle(url) {
try {
document.createStyleSheet(url)
} catch(e) {
var cssLink = document.createElement('link');
cssLink.rel = 'stylesheet';
cssLink.type = 'text/css';
cssLink.href = url;
var head = document.getElementsByTagName('head')[0];
head.appendChild(cssLink)
}
}
解析
1、浏览器通过请求的 URL 进行域名解析,向服务器发起请求,接收文件(HTML、CSS、JS、Images等等)。 2、HTML 文件加载后,开始构建 DOM Tree 3、CSS 样式文件加载后,开始解析和构建 CSS Rule Tree 4、Javascript 脚本文件加载后, 通过 DOM API 和 CSSDOM API 来操作 DOM Tree 和 CSS Rule Tree
渲染: 1、浏览器引擎通过 DOM Tree 和 CSS Rule Tree 构建 Rendering Tree 2、Rendering Tree 并不与 DOM Tree 对应,比如像 <head> 标签内容或带有 display: none; 的元素节点并不包括在 Rendering Tree 中 。 3、通过 CSS Rule Tree 匹配 DOM Tree 进行定位坐标和大小,是否换行,以及 position、overflow、z-index 等等属性,这个过程称为 Flow 或 Layout 。 4、最终通过调用Native GUI 的 API 绘制网页画面的过程称为 Paint(注意:painting: 按照算出来的规则,通过显卡,把内容画到屏幕上。)
注意:
1.reflow(回流):当浏览器发现某个部分发生了点变化影响了布局,需要倒回去重新渲染,内行称这个回退的过程叫 reflow。reflow 会从 <html> 这个 root frame 开始递归往下,依次计算所有的结点几何尺寸和位置。reflow 几乎是无法避免的。现在界面上流行的一些效果,比如树状目录的折叠、展开(实质上是元素的显 示与隐藏)等,都将引起浏览器的 reflow。鼠标滑过、点击……只要这些行为引起了页面上某些元素的占位面积、定位方式、边距等属性的变化,都会引起它内部、周围甚至整个页面的重新渲 染。通常我们都无法预估浏览器到底会 reflow 哪一部分的代码,它们都彼此相互影响着。 2.repaint(重绘):改变某个元素的背景色、文字颜色、边框颜色等等不影响它周围或内部布局的属性时,屏幕的一部分要重画,但是元素的几何尺寸没有变。
注意:(1)display:none 的节点不会被加入Render Tree,而visibility: hidden 则会,所以,如果某个节点最开始是不显示的,设为display:none是更优的。
(2)display:none 会触发 reflow,而 visibility:hidden 只会触发 repaint,因为没有发现位置变化。
(3)有些情况下,比如修改了元素的样式,浏览器并不会立刻reflow 或 repaint 一次,而是会把这样的操作积攒一批,然后做一次 reflow,这又叫异步 reflow 或增量异步 reflow。但是在有些情况下,比如resize 窗口,改变了页面默认的字体等。对于这些操作,浏览器会马上进行 reflow。
webkit的主要流程:
作者:Q丁 链接:https://www.jianshu.com/p/2d522fc2a8f8 來源:简书 简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
前端性能优化
1.css选择器优化
首先我们梳理一下浏览器的匹配规则:浏览器解析css选择器的规则是从右向左的,这样会提高查找选择器所对应的元素的效率。
注意:在建立 Render Tree 时(WebKit 中的「Attachment」过程),浏览器就要为每个 DOM Tree 中的元素根据 CSS 的解析结果(Style Rules)来确定生成怎样的 renderer。对于每个 DOM 元素,必须在所有 Style Rules 中找到符合的 selector 并将对应的规则进行合并。选择器的「解析」实际是在这里执行的,在遍历
DOM Tree 时,从 Style Rules 中去寻找对应的 selector。因为所有样式规则可能数量很大,而且绝大多数不会匹配到当前的 DOM 元素(因为数量很大所以一般会建立规则索引树),所以有一个快速的方法来判断「这个 selector 不匹配当前元素」就是极其重要的。如果正向解析,例如「div div p em」,我们首先就要检查当前元素到 html 的整条路径,找到最上层的 div,再往下找,如果遇到不匹配就必须回到最上层那个 div,往下再去匹配选择器中的第一个 div,回溯若干次才能确定匹配与否,效率很低。逆向匹配则不同,如果当前的 DOM 元素是 div,而不是 selector 最后的 em,那只要一步就能排除。只有在匹配时,才会不断向上找父节点进行验证。但因为匹配的情况远远低于不匹配的情况,所以逆向匹配带来的优势是巨大的。同时我们也能够看出,在选择器结尾加上「*」就大大降低了这种优势,这也就是很多优化原则提到的尽量避免在选择器末尾添加通配符的原因。
本文来自 _陌默 的优快云 博客 ,全文地址请点击:https://blog.youkuaiyun.com/qq_21397815/article/details/72874932?utm_source=copy
(1)避免通配规则 除了 * 之外,还包括子选择器、后台选择器等。 而它们之间的组合更加逆天,譬如:li * 浏览器会查找页面的所有元素,然后一层一层地寻找他的祖先,看是不是li,这对可能极大地损耗性能。
(2)不限定ID选择器 ID就是唯一的,不要写成类似div#nav这样,没必要。
(3)不限定class选择器 我们可以进一步细化类名,譬如li.nav 写成 nav-item
(4)尽量避免后代选择器 通常后代选择器是开销最高的,如果可以,请使用子选择器代替。
替换子选择器 如果可以,用类选择器代替子选择器,譬如 nav > li 改成 .nav-item
(5)依靠继承 了解那些属性可以依靠继承得来,从而避免重复设定规则
出处http://www.cnblogs.com/MarcoHan/