浏览器渲染原理,重绘和回流
相关经典题目: 浏览器的输入栏中,输入一个url地址,到呈现页面经历了什么事情?
1. 浏览器向服务器发送请求。
- 包括DNS解析、HTTP、HTTPS、TCP等。
- 服务器返回给客户端indecx.html页面中的源码。
- 浏览器会分配一个线程“自上而下,自左而右”依次解析和渲染代码。
- 浏览器是多线程,页面渲染是单线程(JS是单线程)[具体可以看事件循环的文章]
2. 向下解析渲染遇到样式(CSS)时
1.行内样式
<body>
<h1 style = "color:white;background-color:blue">
This is a line of Text.
</h1>
<h1 style="color:red;">style属性的应用</h1>
<p style="font-size:14px;color:green;">直接在HTML标签中设置的样式</p>
</body>
- 使用style属性引入CSS样式。
- 特点:
- (1)内联样式放在代码中的HTML元素中。
- (2)使用内联样式时,样式只会影响你选择的元素。
- (3)内联样式没有选择器
2. style内嵌样式
<!DOCTYPE>
<html>
<head>
<meta charset="utf-8" />
<title>内部样式表</title>
<!--使用内部样式表引入CSS-->
<style type="text/css">
div{
background: green;
}
</style>
</head>
<body>
<div>我是DIV</div>
</body>
</html>
-
被放置在样式标签<styletype="text/css">编写的网页的头部部分中。
-
正常从上到下解析(解析完再继续解析DOM解构)
-
同步加载。
-
优化:在真实项目中,如果CSS样式代码不是很多(或者是移动端项目),我们应该使用内嵌式,以此来减少HTTP资源的请求,提高页面的渲染速度。 可以先把首屏的样式写内嵌,其他的写link加载。
-
嵌入方式的 CSS 只对当前的网页有效。因为 CSS 代码是在 HTML 文件中,所以会使得代码比较集中,当我们写模板网页时这通常比较有利。因为查看模板代码的人可以一目了然地查看 HTML 结构和 CSS 样式。因为嵌入的 CSS 只对当前页面有效,所以当多个页面需要引入相同的 CSS 代码时,这样写会导致代码冗余,也不利于维护。
3. link导入外部样式资源(链接式)
<head>
<link rel="stylesheet" type="text/css" href="css.css">
</head>
<body>
<h1>
This is a line of Text.
</h1>
</body>
- 在页面加载过程中,如果遇到Link, 浏览器会新开辟一个线程,去服务器加载对应的资源(不会阻碍主线程的渲染)
- 属于异步加载。
- 这是最常见的也是最推荐的引入 CSS 的方式。使用这种方式,所有的 CSS 代码只存在于单独的 CSS 文件中,所以具有良好的可维护性。并且所有的 CSS 代码只存在于 CSS 文件中,CSS 文件会在第一次加载时引入,以后切换页面时只需加载 HTML 文件即可。
4. @import导入
<head>
<style>
@import url("css.css");
</style>
</head>
<body>
<h1>
This is a line of Text.
</h1>
</body>
- 此时不会开辟新的线程去加载资源文件,而是让主线程去加载获取,这样阻碍了DOM解构的继续渲染;
- 只有等把外部样式导入进来,并且解析后,才会继续渲染DOM解构
- 属于同步编程(所以用异步的link 比较好)。如果规定了,先把谁的样式导入进来,才能进行之后的,可以考虑@import。
- 两者都是外部引用CSS的方式,但是存在一定的区别:
-
区别1:link是XHTML标签,除了加载CSS外,还可以定义RSS等其他事务;@import属于CSS范畴,只能加载CSS。
-
区别2:link引用CSS时,在页面载入时同时加载;@import需要页面网页完全载入以后加载。
-
区别3:link是XHTML标签,无兼容问题;@import是在CSS2.1提出的,低版本的浏览器不支持。
-
区别4:link支持使用Javascript控制DOM去改变样式;而@import不支持。
几种方式的优先级
答:行内样式>内部样式>外部样式(后两者是就近原则)
另外一个方面:选择器优先级
优先级:ID选择器>类选择器>标签选择器
什么时候用哪种方式比较好??
网络资源请求数(HTTP最大并发数)?
- 大部分浏览器都维持在6个左右。(当超过6个,其他的要排队等着处理了)
- 优化:为了避免并发的上线,导致某些资源要延迟加载,页面渲染的速度变慢,我们应该尽可能减少HTTP的请求数量。(减少HTTP的请求数)
3. 页面加载过程中遇到js(JS中会有操作DOM的代码)
(1)默认
- 主线程会从服务器获取到js资源,并且把js资源进行解析加载,加载完成后再继续渲染DOM结构。
经典问题:为什么把css(link)写在结构的上面,script写在结构底部?
=> link在顶部:为了让css快点加载回来
=> script 在底部是为了获取DOM元素或者不阻碍DOM的渲染。
- 现在浏览器的扫描机制:
如果遇到script需要同步加载和渲染的代码,浏览器在渲染JS的时候,同事会向下继续扫描代码,如果发现有一些异步资源代码,此时开始加载。
因为在JS中还有可能操作元素的样式,所以哪怕都是异步请求资源的情况下,JS代码先加载回来,也要等到在他之前发送的CSS加载并渲染完成后才会执行JS代码。
(2)想把js想css一样,用另外一个线程单独处理
可以设置defer或者async: 都是异步获取资源(不会阻碍DOM的渲染)
=> defer
如果设置了defer:可以遵循原有的加载顺序,获取后按照顺序去依次渲染JS
=> async
如果设置async: 是无序的(谁先获取到,谁先执行)
4. DOMContentLoaded和load事件
DOMContentLoaded事件
- 当DOM结构加载完成,就会触发这个事件
- DOM树有了,并且JS也执行加载了,此时触发这个事件
load事件
- 当所有资源都加载完才会触发这个
- 包含了需要等待图片等资源也都加载完才触发这个。
5. jQuery中(function())或者(document)
- 当dom结构加载完,才会执行函数中的代码。
- 原理就是应用DOMContentLoaded事件,但在低版本浏览中不兼容。
- 可以使用onreadystatechange事件代替,在这个事件中监听document.readyState值,为complete代表DOM结构加载完成。
回流和重绘
浏览器渲染的过程
- 解析HTML,生成DOM树,解析CSS,生成CSSOM树
- 将DOM树和CSSOM树结合,生成渲染树(Render Tree)
- Layout(回流): 根据生成的渲染树,计算它们在设备视口(viewport)内的确切位置和大小,这个计算的阶段就是回流
- Painting(重绘): 根据渲染树以及回流得到的几何信息,得到节点的绝对像素
- Display:将像素发送给GPU,展示在页面上
Layout(回流):
- 定义:元素的大小或者位置发生了变化(当页面布局和几何信息发生变化的时候),触发了重新布局,导致渲染树重新计算布局和渲染。
- 举例:
- 如添加或删除可见的DOM元素 ;
- 元素的位置发生变化;
- 元素的尺寸发生变化;
- 内容发生变化(比如文本变化或图片被另一个不同尺寸的图片所替代);
- 页面一开始渲染的时候(这个无法避免);
- 因为回流是根据视口的大小来计算元素的位置和大小的,所以浏览器的窗口尺寸变化也会引发回流....
Painting(重绘):
- 定义:元素样式的改变(但宽高、大小、位置等不变)
- 举例:如 outline, visibility, color、background-color等
注意:回流一定会触发重绘,而重绘不一定会回流
从输入url到看到页面,一定经过一次回流和重绘
前端性能优化之:避免DOM的回流
放弃传统操作dom的时代,基于vue/react开始数据影响视图模式
mvvm / mvc / virtual dom / dom diff ......
分离读写操作 (现代的浏览器都有渲染队列的机制)
offsetTop、offsetLeft、offsetWidth、offsetHeight、clientTop、clientLeft、clientWidth、clientHeight
scrollTop、scrollLeft、scrollWidth、scrollHeight、getComputedStyle、currentStyle....
会刷新渲染队列
样式集中改变
div.style.cssText = 'width:20px;height:20px;'
div.className = 'box';
let nav =document.getElementById('nav')
// 有渲染队列机制,统一渲染一次,引发一次回流
nav.style.width = '100px';
nav.style.height = '100px'
//触发2次,
nav.style.width = '100px';
console.log(nav.clientWidth)
nav.style.height = '100px'
缓存布局信息
div.style.left = div.offsetLeft + 1 + 'px'; div.style.top = div.offsetTop + 1 + 'px';
=>改为(获取的值用变量存起来了)
var curLeft = div.offsetLeft; var curTop = div.offsetTop;
div.style.left = curLeft + 1 + 'px'; div.style.top = curTop + 1 + 'px';
元素批量修改
- 文档碎片:createDocumentFragment
//10次
for (let i =0;i <10 ;i++){
let span = document.createElement('span');
nav.sppendChild(span)
}
// 1次
let frag = document.createDocumentFragment();
for (let i =0;i <10 ;i++){
let span = document.createElement('span');
frag.appendChild(span)
}
nav.sppendChild(frag)
//1次
let str =``;
for (let i =0;i <10 ;i++){
str + = `<span></span>`;
}
nacBox.innerHTML = str
动画效果应用到position属性为absolute或fixed的元素上(脱离文档流)
CSS3硬件加速(GPU加速)
- 比起考虑如何减少回流重绘,我们更期望的是,根本不要回流重绘;
- transform \ opacity \ filters ... 这些属性会触发硬件加速,不会引发回流和重绘......
- 可能会引发的坑:过多使用会占用大量内存,性能消耗严重、有时候会导致字体模糊等
牺牲平滑度换取速度
- 每次1像素移动一个动画,但是如果此动画使用了100%的CPU,动画就会看上去是跳动的,因为浏览器正在与更新回流做斗争。
- 每次移动3像素可能看起来平滑度低了,但它不会导致CPU在较慢的机器中抖动
避免table布局和使用css的javascript表达式
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!