浏览器常见问题

localstorage、sessionstorage、cookie 区别、大小限制?#

  • 生命周期:

cookie:可设置失效时间,没有设置的话,默认是关闭浏览器后失效

localStorage:除非被手动清除,否则将会永久保存。

sessionStorage: 仅在当前网页会话下有效,关闭页面或浏览器后就会被清除。

  • 存放数据大小:

cookie:4KB 左右

localStorage 和 sessionStorage:可以保存 5MB 的信息。

  • http 请求:

cookie:每次都会携带在 HTTP 头中,如果使用 cookie 保存过多数据会带来性能问题

localStorage 和 sessionStorage:仅在客户端(即浏览器)中保存,不参与和服务器的通信

  • 易用性:

cookie:需要程序员自己封装,源生的 Cookie 接口不友好

localStorage 和 sessionStorage:源生接口可以接受,亦可再次封装来对 Object 和 Array 有更好的支持

  • 应用场景:

从安全性来说,因为每次 http 请求都会携带 cookie 信息,这样无形中浪费了带宽,所以 cookie 应该尽可能少的使用,另外 cookie 还需要指定作用域,不可以跨域调用,限制比较多。但是用来识别用户登录来说,cookie 还是比 storage 更好用的。其他情况下,可以使用 storage

localStorage 和 sessionStorage 唯一的差别一个是永久保存在浏览器里面,一个是关闭网页就清除了信息。

localStorage 可以用来夸页面传递参数,sessionStorage 用来保存一些临时的数据,防止用户刷新页面之后丢失了一些参数。

  • 浏览器支持情况:

localStorage 和 sessionStorage 是 html5 才应用的新特性,可能有些浏览器并不支持,IE8+

cookie 可以通过下面这段代码来判断所使用的浏览器是否支持 cookie

if (navigator.cookieEnabled) {
alert("该浏览器支持cookie"); // 浏览器支持cookie
} else {
alert("该浏览器不支持cookie"); // 浏览器不支持cookie
}

jsonp/domain/cors/postmessage/iframe 怎么在开发中实现跨域#

  • 1.JSONP

全称:JSON with Padding;

作用:利用一些 HTML 标签天生的跨域能力来发送跨域请求的 eg:img、script、link

缺点:JSONP 只能发送 get 请求,不能发送 post 请求,且 JSONP 不是官方的跨域解决方案

  • 2.CORS

全称:Cross-Origin Resource 跨域资源共享

优点:CORS 是官方的跨域解决方案,且使用 CORS 不需要在客户端做任何修改,直接发送 AJAX 请求即可

CORS 支持 get/post 请求;IE8 以下不支持

方法:通过服务器设置一个响应头来告诉浏览器允许跨域请求

res.sendHeader(‘Access-Control-Allow-Origin’,’请求地址’)
// 允许所有的跨域请求 用 _ 即
res.sendHeader(‘Access-Control-Allow-Origin’, ’_’ )
  • 3.iframe 跨域

1、主域相同、子域不同 使用 document.domain=主域名 两个域都设置:document.domain=‘xxx.com’ 2、主域不同 解决办法: 1、location.hash hash 属性是一个可读可写的字符串,该字符串是 URL 的锚部分(从 # 号开始的部分) A 通过 location.hash 方式传递参数给 B,B 通过定时器检测 hash 变化,执行对应操作 location.hash 原理: 1、动态改变 location.hash,iframe 不会重载 2、无论跨域与否,iframe 内可以获取自己的 location.hash 3、只要域名相同就能通信,即使 ABC 三层嵌套 location.hash 缺点 传递数据量有限 不太安全

2、window.name name 在浏览器环境中是一个全局 window 对象的属性 当在 iframe 中加载新页面时,name 的属性值依旧保持不变 name 属性仅对相同域名的 iframe 可访问 window.name 的优势: 数据量更大(2M) 更安全 可传递多种数据格式 window.name 的劣势: 只适用于隐藏 iframe 的情形

  • 4.document.domain 来跨子域

例如 a.qq.com 嵌套一个 b.qq.com 的 iframe ,如果 a.qq.com 设置 document.domain 为 qq.com 。b.qq.com 设置 document.domain 为 qq.com,那么他俩就能互相通信了,不受跨域限制了。

  • 5.postMessage()

window.postMessage(message, targetOrigin) 此方法是 html5 新引进的特性,

优点:可以使用它来向其它的 window 对象发送消息,无论这个 window 对象是属于同源或不同源(目前 IE8+、FireFox、Chrome、Opera 等浏览器都已经支持)。 缺点:缺点是 IE6、IE7 不支持,所以用不用还得根据实际需要来决定。

说下浏览器缓存/http 缓存#

浏览器缓存是由 header 决定的

1.缓存介绍 常见的 HTTP 缓存只能存储 GET 响应,对于其他类型的响应则无能为力。缓存的关键主要包括 request method 和目标 URI(一般只有 GET 请求才会被缓存)。

浏览器在每次 GET URL 时都会先检查 URL 对应的缓存,除非你指定不使用缓存(强制刷新或者 Disable Cache 等)

  • 强缓存

强缓存两个相关字段:Expires Cache-Control。 强缓存分为两种情况,一种是发送 HTTP 请求,一种不需要发送。 首先检查强缓存,这个阶段不需要发送 HTTP 请求。通过查找不同的字段来进行,不同的 HTTP 版本所以不同:HTTP1.0 版本,使用的是 Expires,HTTP1.1 使用的是 Cache-Control

Cache-Control 包括:

  • max-age
  • s-maxage
  • public
  • private
  • no-cache
  • no-store
  • must-revalidate max-age(单位为 s)指定设置缓存最大的有效时间,定义的是时间长短。当浏览器向服务器发送请求后,在 max-age 这段时间里浏览器就不会再向服务器发送请求了。

Expires

缓存过期时间,用来指定资源到期的时间,是服务器端的具体的时间点。也就是说,Expires=max-age + 请求时间。在上面我们提到过,cache-control 的优先级更高。 Expires 是 Web 服务器响应消息头字段,在响应 http 请求时告诉浏览器在过期时间前浏览器可以直接从浏览器缓存取数据,而无需再次请求。这个方式有一个问题:「服务器的时间和浏览器的时间可能并不一致」,所以 HTTP1.1 提出新的字段代替它

  • 协商缓存

强缓存失效后,浏览器在请求头中携带响应的缓存 Tag 来向服务器发送请求,服务器根据对应的 tag,来决定是否使用缓存。

缓存分为两种,Last-Modified 和 ETag,两者各有优势

Last-modified

服务器端文件的最后修改时间,需要和 cache-control 共同使用,是检查服务器端资源是否更新的一种方式。当浏览器再次进行请求时,会向服务器传送 If-Modified-Since 报头,询问 Last-Modified 时间点之后资源是否被修改过。如果没有修改,则返回码为 304,使用缓存;如果修改过,则再次去服务器请求资源,返回码和首次请求相同为 200,资源为服务器最新资源。

ETag

根据实体内容生成一段 hash 字符串,标识资源的状态,由服务端产生。浏览器会将这串字符串传回服务器,验证资源是否已经修改,如果没有修改, 使用 ETag 可以解决 Last-modified 存在的一些问题:

a、某些服务器不能精确得到资源的最后修改时间,这样就无法通过最后修改时间判断资源是否更新 b、如果资源修改非常频繁,在秒以下的时间内进行修改,而 Last-modified 只能精确到秒 c、一些资源的最后修改时间改变了,但是内容没改变,使用 ETag 就认为资源还是没有修改的

  • 缓存实现方式

方式一:客户端指定 request header 携带 cache-control 服务端接收到 header cache-control 字段,并且使用该字段设置 response 的 cache-control 浏览器接收到 response header,根据 cache-control 设置缓存

方法二:服务端指定 服务端接收到请求,设置 response 的 cache-control 并返回 response 浏览器接收到 response header,根据 cache-control 设置缓存

其他情况 浏览器可以根据用户操作直接跳过缓存,比如勾选了控制台的 Disable cache

参考:https://segmentfault.com/a/1190000008377508

浏览器从输入 URL 到渲染完页面的整个过程 越详细越好#

1.根据域名,进行 DNS 域名解析; 2.拿到解析的 IP 地址,建立 TCP 连接; 3.向 IP 地址,发送 HTTP 请求; 4.服务器处理请求; 5.返回响应结果; 6.关闭 TCP 连接; 7.浏览器解析 HTML; 8.浏览器布局渲染;

1.DNS 解析 以 chrome 浏览器为例,当输入 baidu.com 的时候,我们实际访问的是 14.215.177.39,这是百度的 IP 地址,从 baidu.com 到 14.215.177.39 的过程就是一个 DNS 解析的过程,首先会从浏览器里 DNS 缓存查找,chrome://dns/,一旦查找到了就完成了这个解析过程,但是如果没有呢? 那么接着会从电脑本地的 hosts 文件中查找

2.三次握手 当了解了互联网协议后,我们接着之前的 URL 访问过程,获得了服务器 IP 地址以后,我们需要进行通信,这会进行一次连接,这是通过 TCP 协议完成的 三次握手: 第一次由客户端发送 SYN 包到服务器,等待服务器确认; 第二次是服务器接收到 SYN 数据包,将 SYN + 自己发送的 ACK 包一同发送给客户端; 第三次是客户端接收到服务器发送过来的 SYN + ACK 数据包后,再向服务器发送确认包 ACK,客户端和服务器进入连接状态,完成三次握手。 TCP 四次挥手 TCP 断开链接的过程和建立链接的过程比较类似,只不过中间的两部并不总是会合成一步走,所以它分成了 4 个动作,客户端挥手发送(fin)——服务端发送(ack)——服务端发送(fin)——客户端发送(ack)。 3.HTTP 通信 当客户端和服务器进入连接状态后,那么就可以进行 HTTP(应用层) 的通信了。

4.页面渲染 当浏览器接受到响应报文,举例是 html 文件,就开始解析和渲染并呈现给用户也就是我们。 一个完整的 html 文件包括了 html 部分,css 部分,javascript 部分

讲讲前端路由#

前端路由的实现方式主要有两种: hash history 1、hash 即 window.loacation.hash,url 中以“#”为标识符,如:www.xxx/com/list.ht… ,这个值可读可写,读取时,可以用来判断网页状态,写入时会在不重新载入网页的情况下给浏览器增加一条历史记录,有了这种特性就有了前端路由的雏形,因为改变#之后的内容相当于改变了 url,但是并没有重新向服务器发送请求。JavaScript 可以通过 window.onhashchange 来监听 url 变化,以实现不同组件的切换。

2、由于 html5 的发布,引入了 history.pushState() 和 history.replaceState() 方法,它们分别可以添加和修改历史记录条目。pushState 需要三个参数,一个状态对象(可以通过 onpopstate 事件获取到),一个标题 (目前被忽略)和一个 URL,replaceState 参数也是一样。通常与 window.onpopstate 配合使用,这个为前端路由的另一种模式奠定了基础,但是这种方式的 url 是一个完整的如http://www.xxx.com/list/complete,他每一次改变都会向服务发送一次请求资源(其实我们是没有这个页面地址的),所以需要服务器端增加一条配置,如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是我们的主页面。

浏览器中的进程介绍#

进程和线程的概念 进程:一个进程就是一个程序的运行实例。启动一个程序的时候,操作系统会为该程序创建一块内存,用来存放代码、运行中的数据和一个执行任务的主线程。

线程:线程不能单独存在,需要由进程来启动和管理。

  • 进程和线程的特点:

进程中任一线程执行出错都会导致整个进程崩溃。 线程之间共享进程中的数据。 当一个进程关闭之后,操作系统会回收进程所占用的内存,即使其中有线程因执行不当导致内存泄露,进程退出时,这些内存也会被正确回收。 进程之间的内容相互隔离,使用进程间通信(IPC)机制。

浏览器中的渲染流程#

渲染进程会先创建一个空白页面,叫做解析白屏。之后开始页面解析和子资源加载。

  • 构建 DOM 树(Parse HTML)——产出 DOM 树

由于浏览器无法理解 HTML,因此需要将 HTML 解析为浏览器能理解的结构——DOM 树。DOM 是保存在内存中的树结构,DOM 是生成页面的基础数据结构,给 JavaScript 脚本提供一套查询和改变文档结构、样式、内容的接口,也是一道安全防护线,一些不安全的内容在 DOM 解析阶段就被排除了。

渲染引擎内部有个 HTML 解析器的模块,负责将 HTML 字节流转换为 DOM 结构。解析过程是渐进的,网络进程加载了多少数据,HTML 解析器就解析多少数据。渲染进程从之前和网络进程建立的数据管道的一端动态接收字节流,并将其解析为 DOM。

解析过程中遇到了 script 标签时,HTML 解析器暂停工作,JavaScript 引擎介入,执行脚本,此时只能访问位于 script 之上已经构建了的 DOM。访问后面的元素会返回 null,执行操作会报错。脚本执行完,HTML 解析器恢复解析。

在有 src 属性加载脚本的 script 标签中添加 async 属性表明这个脚本是异步的,在后台加载,HTML 解析器继续工作,等脚本加载完成后立即打断 HTML 解析器(如果还没解析完的话)开始执行脚本。 添加 defer 属性也会异步加载,但是会等到 HTML 解析完毕,在 DOMContentLoaded 事件之前执行。这两个属性对无 src 属性的脚本不会生效。

对于 CSS,由于不直接参与 DOM 构建,本来是不会阻塞 DOM 树的构建的。但是如果页面有同步执行的脚本时,因为执行前是不知道脚本有没有操作 CSSOM 的,因此渲染引擎为了避免脚本执行出错的可能性,直接假定脚本会依赖 CSSOM。于是下载 CSS 文件并解析成 CSSOM,再执行脚本。期间 HTML 解析器一直是暂停的状态,直到脚本执行完毕才继续工作。

  • 二 样式计算(Recalculate Style)——产出 ComputedStyle

样式计算是为了计算出 DOM 节点中每个元素的具体样式,这个阶段大体可以分为三个步骤:

1.构建 CSSOM。CSS 的来源有三种:通过 link 标签引用的外部 CSS、style 标签、元素的 style 属性。由于浏览器也无法直接理解纯文本的 CSS,因此渲染引擎需要先将其转为浏览器可以理解的结构——CSSOM(可以通过 document.styleSheets 访问)。最终会把所有不同来源的样式都包含进 styleSheets 中。CSSOM 的作用一个是为 JavaScript 提供操作样式表的接口,一个是为布局树的合成提供基础的样式信息。

2.标准化样式属性值:将例如 2em、blue、bold 之类的数值转换为渲染引擎容易理解的、标准化的计算值。最终解析成类似 32px、rgb(255,0,0)、700 这样的标准值。

3.计算 DOM 树中每个节点的具体样式:需要使用两个规则: 继承:每个 DOM 节点都会包含父节点中可以继承的属性,例如 body 的 font-size。 层叠:定义了合并多个来源的属性值的算法,计算结果保存在 ComputedStyle 的结构内。

  • 三 布局(Layout)——产出布局树

此阶段用来计算 DOM 树中可见元素的几何位置。有两个步骤: 1.创建布局树:遍历 DOM 树,用所有可见节点构建一颗布局树,不可见的节点如 head 或 display:none 的元素都会被过滤掉。这个过程的样式查询会使用到 ComputedStyle。

2.布局计算:计算布局树中节点的坐标位置,把布局计算的结果重新写回布局树中。因此布局树既是输入内容,也是输出内容,这也是布局阶段一个不合理的地方,Chrome 团队正在重构布局代码,下一代布局系统叫 LayoutNG,将会尝试分离输入和输出,简化布局算法。

  • 四 分层(Update Layer Tree)——产出图层树

由于页面中可能包含很多复杂的效果如 3D 变换、页面滚动、z-index 的 z 轴排序等,为了避免每次改动都要引发整个页面重排或重绘,会将页面分成多个图层,并生成一颗对应的图层树(LayerTree),层树中的每个节点都对应一个图层,这些图层按照一定顺序叠加在一起(层合成 composite)构成了最终的页面图像。

  • 五 图层绘制(Paint)——产出图层的待绘制列表

得到图层树之后,渲染引擎会对图层树中的每个图层进行绘制,把一个图层的绘制拆分成很多小的绘制指令,再把这些指令按照顺序组成一个待绘制列表:

  • 六 栅格化(raster)——产出合成后的位图

当图层的绘制列表准备好之后,渲染进程中的主线程会把该绘制列表提交给合成线程。 通常页面内容都比屏幕大得多,如果等待所有图层都绘制完毕再进行合成的话,会产生一些不必要的开销,也会让合成图片的时间变得更久。合成线程会将图层划分为固定的图块(tile),大小通常是 256x256 或 512x512。如果这个图层非常大,会优先处理视口(屏幕上页面的可见区域)附近的图块。

  • 七 显示

一旦所有图块都被栅格化,合成线程就会生成一个绘制图块的指令——“DrawQuard”,然后将该指令提交给浏览器进程。浏览器进程中的 viz 组件接收到指令后,将页面内容绘制到内存中,最后再将内存中的页面显示在屏幕上。

总结

渲染进程将 HTML 解析为为 DOM 树结构。 渲染引擎将 CSS 样式表解析为 CSSOM,并计算出 DOM 节点的样式。 创建布局树,并计算元素的布局信息。 对布局树进行分层,并生成分层树。 为每个图层生成绘制列表,并将其提交到合成线程。 合成线程将图层分成图块,并在光栅化线程池中将图块转换成位图。 合成线程发送绘制图块命令 DrawQuad 给浏览器进程。 浏览器进程根据 DrawQuad 消息生成页面,并显示到显示器上。

参考:https://juejin.im/post/6844904146982666254#heading-0

说说重绘和重排#

重绘不一定导致重排,但重排一定会导致重绘。

重排 概念: 当 DOM 的变化影响了元素的几何信息(元素的的位置和尺寸大小),浏览器需要重新计算元素的几何属性,将其安放在界面中的正确位置,这个过程叫做重排。

重排也叫回流,简单的说就是重新生成布局,重新排列元素。 如果修改了元素的几何位置属性,如改变宽高,会引发重新布局及之后一系列流程,这个过程叫做重排

重排优化建议: 重排的代价是高昂的,会破坏用户体验,并且让 UI 展示非常迟缓。通过减少重排的负面影响来提高用户体验的最简单方式就是尽可能的减少重排次数,重排范围。下面是一些行之有效的建议,大家可以用来参考

重绘 当一个元素的外观发生改变,但没有改变布局,重新把元素外观绘制出来的过程,叫做重绘。 不改变几何位置信息,只改变元素的如背景颜色等信息,引发重新绘制及之后一系列流程,这个过程叫做重绘。

执行效率方面:合成 > 重绘 > 重排。在性能优化方面可以依据这个原则来调整方案

哪些属性可以直接避免重绘和重排 元素的多次样式修改合并成一次修改 如需进行对 DOM 节点进行多次操作,先将其脱离文本流之后再进行多次操作 table 布局的渲染与普通 DOM 节点的操作相比,性能消耗更大,如果可以,尽量减少 table 布局的使用

介绍一下对浏览器内核的理解 v8 引擎#