浏览器渲染流程css
JavaScript实现动画效果,DOM元素操做等。html
肯定每一个DOM元素应该应用什么CSS规则。web
注意: CSS选择器越详细,匹配工做越多,匹配节点越慢。浏览器
RenderTree包含了渲染网页所需的节点, 无需渲染的节点不会被添加到RenderTree中。 如:<head>,display: none的节点网络
注意:由于设置了visibility:hidden的元素虽不可见,但仍然占有空间,仍会被添加到RenderTree。app
计算每一个DOM元素在最终屏幕上显示的大小和位置。因为web页面的元素布局是相对的,因此其中任意一个元素的位置发生变化,都会联动的引发其余元素发生变化,这个过程叫reflow。异步
注意:影响web性能的一个重要的问题就是repaint和reflow。async
屏幕旋转函数
浏览器视窗改变布局
与大小位置相关的CSS属性改变
根据background,border,box-shadow等样式,将Layout生成的区域填充为最终将显示在屏幕上的像素
按照合理的顺序合并图层而后显示到屏幕上。
实际场景下,大概会有三种常见的渲染流程:
JavaScript -> CSS -> Layout -> Paint -> Composite
JavaScript -> CSS -> Paint -> Composite
JavaScript -> CSS -> Composite
注意:Layout和Paint步骤是可避免的
浏览器会在DOM和CSSOM加载完开始渲染页面。
<style>/* styles here */</style> <link rel="stylesheet" href="index.css">
经过以上两种方式定义的CSS,均会阻塞初次渲染。浏览器会在解析完CSS后,再进行渲染。这是为了防止样式突变带来的抖动。经过link标签引入的CSS阻塞的时间可能更长,由于加载它须要一个网络来回时间
<link rel="stylesheet" href="index_print.css" media="print">
此样式表仍会加载。当浏览器环境不匹配媒体查询条件时,该样式表不会阻塞渲染。咱们可针对不一样媒体环境拆分CSS文件,并为link标签添加媒体查询,避免为了加载非关键CSS资源,而阻塞初次渲染
var style = document.createElement('link'); style.rel = 'stylesheet'; style.href = 'index.css'; document.head.appendChild(style);
该方法不会阻塞初次渲染。
<link rel="preload" href="index_print.css" as="style" onload="this.rel='stylesheet'">
rel不是stylesheet,所以不会阻塞渲染。preload是resoure hint规范中定义的一个功能,resource hint经过告知浏览器提早创建链接或加载资源,以提升资源加载的速度。浏览器遇到遇到标记为preload的link时,会开始加载,当onload事件发生时,将rel改成stylesheet,便可应用此样式。
引入CSS资源的方法 | 是否阻塞初次渲染 |
---|---|
<link rel="stylesheet" href="index.css" /> | 是 |
经过document.write写入以上标签 | 是 |
经过DOM API插入HTMLLinkElement对象 | 否 |
使用preload方式载入CSS | 否 |
为link添加media query | 当媒体查询不匹配时,不会阻塞 |
因为浏览器的优化,现代浏览器的样式计算直接对目标元素执行,而不是对整个页面执行,因此咱们应该尽量减小须要执行样式计算的元素的个数。
<-- inline js --> <script>/* app logics here */</script> <-- external js --> <script src="somescript.js"></script>
经过以上两种方式引入js均会阻塞HTML parser,于是会阻塞出如今脚本后面的HTML标记的渲染。而外部script阻塞的时间通常更长,由于可能包含了一个网络来回时间。
Javascript能够经过document.write修改HTML文档流,所以在执行js时,浏览器会暂停解析DOM的工做。
<-- inline js --> <script>/* app logics here */</script> <-- external js --> <script src="somescript.js"></script>
经过以上两种方式引入的JS均会被CSS阻塞,因为这些Javascript可能会读取或修改CSSOM,所以需等待CSSOM构造完成后,它们才能执行
<html> <head></head> <body> <h1>世界上最美丽的语言是什么?</h1> <button>See answer</button> <!-- index.js内容: 为button标签添加点击事件,点击后,alert答案 --> <script src="index.js"></script> <!-- 百度统计代码 --> <script src="tongji.js"></script> </body> </html>
<html> <head> <!-- index.js内容: 为button标签添加点击事件,点击后,alert答案 --> <script src="index.js" defer></script> <!-- 百度统计代码 --> <script src="tongji.js" defer></script> </head> <body> <h1>世界上最美丽的语言是什么?</h1> <button>See answer</button> </body> </html>
当script标签拥有defer属性时,该脚本会被推迟到整个HTML文档解析完后,再开始执行。被defer的脚本,在执行时会严格按照在HTML文档中出现的顺序执行
注意: 使用defer时,浏览器会保证脚本按照在文档中出现的顺序执行
<html> <head> <!-- index.js内容: document.addEventListener('DOMContentLoaded', function() { document.querySelector('p').onclick=function() { alert('surprise') } }); --> <script src="index.js" async></script> <!-- 百度统计代码 --> <script src="tongji.js" async></script> </head> <body> <p>Hello World</p> </body> </html>
当script标签拥有async属性时,该脚本不会再阻塞HTML parser。且不会被CSS阻塞。
脚本只要加载完成,即可开始执行。
被async的脚本,在执行时会不会严格按照在HTML文档中出现的顺序执行
async适用于无依赖的独立资源
引入JS资源的方法 | 是否阻塞文档内容初次渲染 |
---|---|
在head中引入外部脚本<script src="index.js"></script> 或内联脚本<script>/* app logics */</script> |
是 |
将脚本放到body底部 | 否 |
为脚本添加defer属性 | 否 |
为脚本添加async属性 | 否 |
setTimeout(callback)和setInterval(callback)没法保证callback函数的执行时机,极可能在帧结束的时候执行,从而致使丢帧。requestAnimationFrame(callback)能够保证callback函数在每帧动画开始的时候执行。
帧丢失
JavaScript代码运行在浏览器的主线程上,与此同时,浏览器的主线程还负责样式计算、布局、绘制的工做,若是JavaScript代码运行时间过长,就会阻塞其余渲染工做,极可能会致使丢帧。
每帧的渲染应该在16ms内完成,但在动画过程当中,因为已经被占用了很多时间,因此JavaScript代码运行耗时应该控制在3-4毫秒。
若是真的有特别耗时且不操做DOM元素的纯计算工做,能够考虑放到Web Workers中执行。
var dataSortWorker = new Worker("sort-worker.js"); dataSortWorker.postMesssage(dataToSort); // 主线程不受Web Workers线程干扰 dataSortWorker.addEventListener('message', function(evt) { var sortedData = e.data; // Web Workers线程执行结束 // ... });
因为Web Workers不能操做DOM元素的限制,因此只能作一些纯计算的工做,对于不少须要操做DOM元素的逻辑,能够考虑分步处理,把任务分为若干个小任务,每一个任务都放到requestAnimationFrame中回调执行。
var taskList = breakBigTaskIntoMicroTasks(monsterTaskList); requestAnimationFrame(processTaskList); function processTaskList(taskStartTime) { var nextTask = taskList.pop(); // 执行小任务 processTask(nextTask); if (taskList.length > 0) { requestAnimationFrame(processTaskList); } }
当修改了元素的属性以后,浏览器将会检查为了使这个修改生效是否须要从新计算布局以及更新渲染树,对于DOM元素的“几何属性”修改,好比width/height/left/top等,都须要从新计算布局。
老的布局模型以相对/绝对/浮动的方式将元素定位到屏幕上。Floxbox布局模型用流式布局的方式将元素定位到屏幕上。
经过一个小实验能够看出两种布局模型的性能差距,一样对1300个元素布局,浮动布局耗时14.3ms,Flexbox布局耗时3.5ms
浏览器为了不FOUT(Flash Of Unstyled Text),会尽可能等待字体加载完成后,再显示应用了该字体的内容
只有当字体超过一段时间仍未加载成功时,浏览器才会降级使用系统字体。每一个浏览器都规定了本身的超时时间
但这也带来了FOIT(Flash Of Invisible Text)问题。内容没法尽快地被展现,致使空白。