浏览器知识零散学习(面试篇)

浏览器渲染过程

  1. 解析HTML,生成DOM树,解析CSS,生成CSSOM树。
  2. 将DOM树和CSSOM树结合,生成渲染树(Render Tree)。
  3. Layout(回流):根据生成的渲染树,进行回流,得到节点的几何信息(位置,大小)
  4. Painting(重绘):根据渲染树以及回流得到的几何信息,得到节点的绝对像素
  5. Display:将像素发送给GPU,展示在页面上

生成渲染树

  1. 从DOM树的根节点开始遍历每个可见节点
  2. 对于每个可见的节点,找到CSSOM树中对应的规则,并应用它们。
  3. 根据每个可见节点以及其对应样式,组合生成渲染树。

注意:渲染树只包含可见的节点。
不可见节点:不会渲染输出的节点,比如script meta link等。还有通过css隐藏的节点比如display:none

回流(reflow)

通过构造渲染树,将可见DOM节点以及它对应的样式结合起来,然后计算节点在设备视口的确切位置和大小。这个计算阶段就是回流。

重绘(repaint)

通过构造渲染树和回流阶段,已知可见节点的样式和具体的几何信息(位置,大小),将渲染树的每个节点都转换为屏幕上的实际像素,这个阶段就叫做重绘节点。

何时发生回流重绘

回流阶段主要计算节点位置和几何信息,那么当页面布局和几何信息发生变化的时候,就需要回流。比如以下情况:

  • 添加/删除可见DOM元素
  • 元素位置发生变化
  • 元素尺寸发生了变化(边距、边框、宽高等)
  • 内容发生变化,比如文本变化,图片变化
  • 浏览器尺寸发生变化。(回流是根据视口的大小来计算元素的位置和大小的)

注意:回流一定会触发重绘,重绘不一定触发回流。

如何减少回流和重绘?

  1. 最小化回流和重绘
    合并多次对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'
  1. 批量修改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)

理论上是可以优化的,但是现代浏览器实际上会使用队列来存储多次修改,进行优化,所以在现代浏览器上优化效果不明显。

  1. 对于动画可以使用绝对定位使其脱离文档流,避免影响父元素后续频繁回流。

  2. CSS3硬件加速(GPU加速)

css3硬件加速可以让transform、opacity、filters、这些动画不会引起回流重绘。
常见触发硬件加速css属性

缺点:硬件加速过多会导致内存过大,会有性能问题。

参考:
你真的了解回流和重绘吗

浏览器数据本地存储

localStorage

生命周期永久,除非用户清除浏览器中的localStorage信息,否则永远存在;
localStorage中一般浏览器支持的是5M大小
优点:

  • 拓展了cookie的4k限制
  • 遵循同源策略,不同的网站直接不能使用相同的localStorage
    缺点:
  • 需要手动删除,否则长期存在
  • 各浏览器支持不太统一
  • 只支持String类型的存储,JSON对象须使用JSON.stringify()转换
  • 本质是对字符串的读取,如果存储内容多会消耗内存,导致页面变卡

sessionStorage

会话存储,在浏览器被关闭之前使用,关闭浏览器之后数据消失。(关闭当前页面不会消失,再次打开该页面,sessionStorage还存在)

Cookie

由于http协议是无状态的而服务端的业务必须是有状态的,Cookie诞生的最初目的是为了存储web中的状态信息,以方便服务器端使用。比如判断用户是否是第一次访问网站。它是一个有浏览器和服务器共同协作实现的规范。
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 入门教程

浏览器缓存(强缓存与协商缓存)

第一次听说强缓存与协商缓存也是在面试中。之前一直以为缓存就是本地存储,这次就整理一下关于缓存的知识。

缓存从微观上可以分为以下几类:

  • 浏览器缓存
  • 代理服务器缓存
  • CDN缓存
  • 数据库缓存
  • 应用层缓存

浏览器缓存又称http缓存:
根据是否需要服务器参与是否使用缓存,分为强缓存与协商缓存。

强缓存
强缓存不会向服务器发送请求,直接从缓存中读取资源,请求返回200的状态码,在chrome控制台的network选项中可以看到size显示from disk cache 或from memory cache.

    1. 浏览器第一次向服务器请求一个资源,服务器在返回资源的同时,在响应头(Response Header)上添加上Cache-Control
    1. 浏览器在接收到这个资源后,会把这个资源连同所有的Response Header一起缓存下来
    1. 浏览器再请求这个资源时,先从缓存中寻找,找到这个资源后,如果当前时间没有超过设置的Catch-Control就能命中缓存,否则就不行。
    1. 如果缓存没有命中,浏览器直接从服务器加载资源时,Response Header在重新加载的时候会被更新

Expires是服务器返回的一个绝对时间,对比的时间是客户端的时间,而客户端的时间可以随意修改,因此,会影响缓存效果,在http1.1的时候,提出一个新的header,Cache-Control,这是一个相对时间,在进行缓存的时候,都是利用客户端进行判断,而且是以时间长度为依据判断的,因此会更有效。在配置缓存的时候以秒为单位,用数值表示:Cache-Control : max-age=1000000000,他的缓存过程是:

    1. 浏览器第一次向服务器请求一个资源,服务器在返回资源的同时,在响应头(Response Header)上添加上Cache-Control
    1. 浏览器在接收到这个资源后,会把这个资源连同所有的Response Header一起缓存下来
    1. 浏览器再请求这个资源时,先从缓存中寻找,找到这个资源后,拿出Cache-Control毫秒数跟当前的请求时间比较,如果请求时间在过期时间之前,就能命中缓存,否则就不行。
    1. 如果缓存没有命中,浏览器直接从服务器加载资源时,Response Header的Catch-Control在重新加载的时候会被更新

这两个header可以只用一个,也可以同时使用,同时存在,Cache-Control优先级高于Expires。

协商缓存
协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程,协商缓存生效,返回304和Not Modified,协商缓存利用的是【Last-Modified、If-Modified-Since】、【Etag、If-None-Match】这两对header来管理的。

【Last-Modified、If-Modified-Since】

    1. 浏览器第一次向服务器发送加载资源请求时,服务器会在响应头(Response Header)上加上Last-Modified,表示该资源在服务器最后一次修改时间,
    1. 浏览器再一次请求该资源的时候,会在请求头(Request Header)加上If-Modified-Since,值为Last-Modified的值。
    1. 服务器再次收到资源请求时,根据浏览器传过来的If-Modified-Since和资源在服务器上的最后修改时间判断资源是否有变化,如果没有发生变化则返回304 Not Modified,但是不会返回资源内容,如果有变化就返回资源内容,当服务器返回304 Not Modified的响应时,response header中不会再添加Last-Modified的header,因为资源没有变化,Last-Modified的值也不变。
    1. 浏览器收到304的响应后,就会从缓存中加载资源
    1. 如果协商缓存没有命中,浏览器直接从服务器加载资源,同时更新Last-Modofied为当前资源在服务器最终修改时间,下次请求时,If-Modified-Since会采用上一次返回的Last-Modified的值

缺点:文件修改时间改了,但文件内容没有变。

【Etag、If-None-Match】

    1. 浏览器第一次向服务器请求一个资源,服务器在返回这个资源的同时,在response的header加上Etag,这个Etag是服务器根据当前请求的资源生成的一个唯一标识,是一个字符串,只要资源内容发生改变,这个字符串也会改变,跟时间没有关系。
    1. 浏览器再次请求这个资源的时候,在request的header上加上If-None-Match。这个If-None-Match的值是上一次请求返回的Etag的值
    1. 服务器再次收到资源请求时,根据客户端传过来的If-None-Match和重新生成的资源的新的Etag做比较,相同则返回304 Not Modified,不会返回资源内容,但这里这里即使资源没有发生变化,也会返回Etag,因为这个Etag重新生成过,即使Etag没有发生变化
    1. 浏览器收到304响应后,就从缓存中加载资源。

一般服务器上的【Last-Modified、If-Modified-Since】和【Etag、If-None-Match】会同时启用,但在精度上Etag优先级高,比如Last-Modified的时间单位是秒,如果某个文件1秒内修改很多次,那么他们的Last-Modified并没有体现出来修改。但是Etag每次都会生成一个hash值,以保证精度。
在性能上Last-Modified优先级高,Last-Modified只需要记录时间,而Etag需要服务器通过算法计算出一个hash值。
协商缓存需要配合强缓存使用。
缓存机制
强缓存优先于协商缓存,若强缓存【Expires、Cache-Control】生效则使用强缓存,若不生效,则使用协商缓存【Last-Modified、If-Modified-Since】【Etag、If-None-Match】协商缓存由服务器决定是否使用缓存,若协商失败,那么代表该请求的缓存失败,返回200,重新返回资源和缓存标识,再存入浏览器缓存中;生效则返回304继续使用缓存。
流程图如下:

缓存使用流程图

绿色代表命中缓存,橙黄色代表重新从服务器获取数据。

面试官问:

  1. 页面什么时候加载资源从内存中,什么时候加载资源从磁盘中
    答:当浏览器访问资源的时候,访问到的是缓存资源,当第一次访问后没有关闭该浏览器tab,在第二次访问时资源来自memory,当tab被关闭再访问时,资源来自disk.

写在最后:文中内容大多为自己平时从各种途径学习总结,文中参考文章大多收录在我的个人博客里,欢迎阅览http://www.tianleilei.cn

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,539评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,594评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,871评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,963评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,984评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,763评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,468评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,357评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,850评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,002评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,144评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,823评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,483评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,026评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,150评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,415评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,092评论 2 355