浏览器渲染过程
- 解析HTML,生成DOM树,解析CSS,生成CSSOM树。
- 将DOM树和CSSOM树结合,生成渲染树(Render Tree)。
- Layout(回流):根据生成的渲染树,进行回流,得到节点的几何信息(位置,大小)
- Painting(重绘):根据渲染树以及回流得到的几何信息,得到节点的绝对像素
- Display:将像素发送给GPU,展示在页面上
生成渲染树
- 从DOM树的根节点开始遍历每个可见节点
- 对于每个可见的节点,找到CSSOM树中对应的规则,并应用它们。
- 根据每个可见节点以及其对应样式,组合生成渲染树。
注意:渲染树只包含可见的节点。
不可见节点:不会渲染输出的节点,比如script meta link等。还有通过css隐藏的节点比如display:none
回流(reflow)
通过构造渲染树,将可见DOM节点以及它对应的样式结合起来,然后计算节点在设备视口的确切位置和大小。这个计算阶段就是回流。
重绘(repaint)
通过构造渲染树和回流阶段,已知可见节点的样式和具体的几何信息(位置,大小),将渲染树的每个节点都转换为屏幕上的实际像素,这个阶段就叫做重绘节点。
何时发生回流重绘
回流阶段主要计算节点位置和几何信息,那么当页面布局和几何信息发生变化的时候,就需要回流。比如以下情况:
- 添加/删除可见DOM元素
- 元素位置发生变化
- 元素尺寸发生了变化(边距、边框、宽高等)
- 内容发生变化,比如文本变化,图片变化
- 浏览器尺寸发生变化。(回流是根据视口的大小来计算元素的位置和大小的)
注意:回流一定会触发重绘,重绘不一定触发回流。
如何减少回流和重绘?
- 最小化回流和重绘 合并多次对DOM的修改 比如:
let el = document.getElementById("root")
el.width = "100px";
el.height = "100px";
el.padding = "10px";
以上代码对于元素进行了多次修改,理论上每一次修改都会引起一次回流,以上修改了三次因此三次回流,但是大部分现代浏览器都做了优化,只会引起一次回流,但是在旧版本浏览器中还是会引起三次回流。
因此可以考虑合并所有修改统一处理
let el = document.getElementById("root");
el.style.cssText += `el.width = 100px;el.height = 100px;el.padding = 10px;`
或者
let el = document.getElementById("root");
el.className += 'active'
- 批量修改DOM 当对DOM进行一系列修改的时候,可以通过以下步骤减少回流重绘次数。 (1). 使元素脱离文档流 (2). 对元素进行操作 (3). 让元素回归文档流 按照这个步骤操作。1,3两步会引起回流,但是频繁操作的第二步却是不会引起回流的,因为目标元素已经不在渲染树了。 有三种方式可以让DOM脱离文档流:
- 隐藏元素,修改,重新显示
- 使用文档片段(document fragment)在当前DOM之外构建一个子树,再把它拷贝回文档。
- 将原始元素拷贝到一个脱离文档的节点中,修改节点再替换原始元素。
var ul = document.getElementById("root");
for (var i = 0; i<3; i++){
var li = document.createElement('li');
li.innerText = "text"
ul.appendChild(li)
}
以上代码回流三次 以下是优化方案
var ul = document.getElementById("root");
ul.style.display = "none"
for (var i = 0; i<3; i++){
var li = document.createElement('li');
li.innerText = "text"
ul.appendChild(li)
}
ul.style.display = "block"
回流2次
var ul = document.getElementById("root");
var frag = document.createDocumentFragment();
for (var i = 0; i<3; i++){
var li = document.createElement('li');
li.innerText = "text"
frag.appendChild(li)
}
ul.appendChild(frag)
var ul = document.getElementById("root");
var clone = ul.cloneNode(true);
for (var i = 0; i<3; i++){
var li = document.createElement('li');
li.innerText = "text"
clone.appendChild(li)
}
ul.parentNode.replaceChild(clone,ul)
理论上是可以优化的,但是现代浏览器实际上会使用队列来存储多次修改,进行优化,所以在现代浏览器上优化效果不明显。
-
对于动画可以使用绝对定位使其脱离文档流,避免影响父元素后续频繁回流。
-
CSS3硬件加速(GPU加速)
css3硬件加速可以让transform、opacity、filters、这些动画不会引起回流重绘。 常见触发硬件加速css属性
- transform
- opacity
- filters
- will-change
缺点:硬件加速过多会导致内存过大,会有性能问题。
参考: 你真的了解回流和重绘吗
浏览器数据本地存储
localStorage
生命周期永久,除非用户清除浏览器中的localStorage信息,否则永远存在; localStorage中一般浏览器支持的是5M大小 优点:
- 拓展了cookie的4k限制
- 遵循同源策略,不同的网站直接不能使用相同的localStorage 缺点:
- 需要手动删除,否则长期存在
- 各浏览器支持不太统一
- 只支持String类型的存储,JSON对象须使用JSON.stringify()转换
- 本质是对字符串的读取,如果存储内容多会消耗内存,导致页面变卡
sessionStorage
会话存储,在浏览器被关闭之前使用,关闭浏览器之后数据消失。(关闭当前页面不会消失,再次打开该页面,sessionStorage还存在)
Cookie
Cookie主要由服务端生成,前端也可以设置,保存在客户端本地,通过response响应头的set-Cookie字段进行设置,且Cookie的内容自动在请求的时候被传递给服务器。 如下:
Cache-Control: max-age=0, private, must-revalidate
Connection: keep-alive
Date: Mon, 25 May 2020 09:09:03 GMT
ETag: W/"b9e6480ac475b399cd9533cab4fa89ce"
Server: Tengine
Set-Cookie: locale=zh-CN; path=/
Set-Cookie: _m7e_session_core=1c249e10b2aaad51dcbe0aa865cf3f34; domain=.jianshu.com; path=/; expires=Mon, 25 May 2020 15:09:03 -0000; secure; HttpOnly
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-Request-Id: 9b702ad0-88f3-4b48-8e43-48d971c57888
X-Runtime: 0.065999
X-XSS-Protection: 1; mode=block
Cookie格式如下
Set-Cookie: "name=value;domain=.domain.com;path=/;expires=Sat, 11 Jun 2016 11:29:42 GMT;HttpOnly;secure"
其中name=value是必选项,其他都是可选项。 主要构成如下:
name:一个唯一确定的cookie名称。
value:存储在cookie中的字符串值,一般情况下都会进行加密编码
domin:指明cookie对哪个域是有效的。所有向该域发送的请求中都会包含这个cookie信息。这个值可以包含子域(如:domain=.jianshu.com则表示对于jianshu.com的所有子域都有效)
path:表示这个cookie影响到的路径,浏览器会根据这项配置向指定域中匹配的路径发送cookie
expires:失效时间,表示cookie何时应该删除的时间戳(也就是,何时停止向服务器发送该cookie)。如果不设置,浏览器会在页面关闭时删除所有cookie;不过也可以自己设置删除时间。这个值是GMT格式,如果客户端和服务器端时间不一致,使用expires就会存在偏差。
max-age:与expires的作用相同,用来告诉浏览器此cookie多久过期(单位:秒)而不是一个固定的时间点。正常情况下,max-age的优先级高于expires。
HttpOnly告知浏览器不允许通过脚本document.cookie去更改这个值,同样这个值在doucment.cookie中也不可见,但在http请求中仍然会携带这个cookie,虽然在脚本中不可获取,但仍然在浏览器安装目录中以文件形式存在。这项通常在服务器端设置。
secure:安全标志,制定后只有在使用SSL链接时候才能发送的浏览器,如果是http链接则不会传递该信息。就算设置了secure属性也并不代表他人不能看到机器本地保存的cookie信息,所以不要把重要信息放cookie就对了。
Cookie作用: 可以记录用户ID、密码、浏览过的网页、停留的时间等信息。一个网站只能读取它自己放置的信息,不能读取其他网站的Cookie文件。因此,Cookie文件还保存了host属性,即网站的域名或ip。这些属性以键值对的方式保存,为了安全,内容大多加密处理。 Cookie优点:
- 提升用户体验,如记住密码等功能
- 弥补了HTTP无连接特性
- 站点统计访问人数的一个依据
Cookie缺点:
- 无法解决多人共用一台电脑的问题,有不安全因素。
- Cookie文件容易被误删
- 容量有限制
- 在请求头上携带存在数据安全性问题
- Cookies欺骗,修改host文件,可以非法访问目标站点的Cookie
indexDB
浏览器发展越来越成熟,很多应用考虑做离线功能,将大量数据存储在客户端,这样可以减少从服务器获取数据,直接从本地获取数据。 由于存储空间大小和其他原因,浏览器的存储方案都不适合做大量数据存储。 IndexDB就是浏览器提供的本地数据库,可以被网页脚本创建,操作。允许存储大量数据,提供查找接口,建立索引等。浏览器数据库 IndexedDB 入门教程
写在最后:文中内容大多为自己平时从各种途径学习总结,文中参考文章大多收录在我的个人博客里,欢迎阅览www.tianleilei.cn