浏览器常见面试题
一、缓存机制
1. 概念
- 浏览器会将第一次请求后的资源存为离线资源,当下次需要该资源时候,浏览器会根据缓存机制决定直接使用缓存还是再次向服务器发送请求。
3. 强缓存
- 不向服务端发送求情,强制使用缓存数据。
- 浏览器在第一次请求后缓存资源,再次请求时,根据该资源的响应头中的 Expires 和 Cache-Control 都表示资源的有效时间,
- Expires,代表资源失效时间(绝对时间)如果服务端和客户端的时间偏差较大时候,会出现缓存混乱。
- Cache-Control,资源有效时间(相对时间),Date 表示资源发送时间,表示资在Date~Date+有效时间内是有效的。
- Cache-Control,的优先级高于 Expires 的优先级来判断是否命中强缓存,如果命中且如果资源没有过期,则直接从缓存中拿。
- 强缓存的状态码一般是 200 。
4. 协商缓存
- Etag 字段:表示请求资源在服务器的唯一标识,浏览器可以根据 ETag 值缓存数据,下次请求的时候以 If-None-Match 字段请求
- Last-Modified 字段:用于标记请求资源的最后一次修改时间
- 由服务器决定缓存资源是否可用。
- 都是成对出现的,即第一次请求的响应头带上某个字, Last-Modified 或者 Etag,则后续请求则会带上对应的请求字段 If-Modified-Since或者 If-None-Match,若响应头没有 Last-Modified 或者 Etag 字段,则请求头也不会有对应的字段。
- 如果没有命中强缓存,浏览器就会发送请求到服务器,这次请求会带上 IF-Modified-Since 或者 IF-None-Match, 它们的值分别是第一次请求返回 Last-Modified 或者 Etag。
- if-Modeified-Since/Last-modified 代表最后文件修改的时间 , 时间只能精确到秒级。根据文件的最后修改时间判断资源是否有变化。如果资源有变化,就正常返回资源内容,新的 Last-Modified 会在 response header 返回,并在下次请求之前更新本地缓存的 Last-Modified,下次请求时,If-Modified-Since会启用更新后的 Last-Modified。
- If-None-Match/Etag, 值都是由服务器为每一个资源生成的唯一标识串,只要资源有变化就这个值就会改变。由 hash 算法算出来的 hash 值。服务器通过比较两者是否一致来判定文件内容是否被改变。
- 优先级 Cache-Control > expires > Etag > Last-Modified
5. 操作流程
- 首先浏览器第一次进入之后,会向服务端发送请求,服务端返回请求结果,并把资源存储到缓存中。
- 当第二次进入浏览器时,请求资源的前,先判断是否有缓存,如果有缓存,根据 Expires 和 Cache-Control 判断资源是否过期。
- 如果没有过期那么会直接使用强缓存进行页面加载。
- 如果过期之后进行协商缓存的。
- 根据请求头的 If-None-Match 和 if-Modeified-Since 字段向服务器发起请求,判断资源是否更新。
- 如果资源过期但是没有更新,那么证明当前资源还可以使用,那么可以继续使用缓存然后加载页面。
- 如果资源更新,那么就返回最新资源和最新的缓存标识,存入缓存中,加载页面。
二、同源、跨域
- 同源:两个页面有相同的协议、域名、端口
- 同源策略:是浏览器提供的一个安全策略,他规定了:
-
不可以读取非同源网站的Cookie、Localstorage、IndexedDB
- 无法获取非同源网页的DOM
- 无法向非同源地址发送Ajax请求
- 跨域 :浏览器的同源策略不允许非同源的URL之间进行交互
- 怎么解决跨域:
- JSONP :缺点:只支持GET,不支持POST。
- 通过 script 的 src请求跨域的数据和接口,并用函数调用的形式(callback)接收响应回来的数据
- CORS:优:GET和POST都支持, 缺:不兼容低版本浏览器。
- 开启反向代理服务器:一个网页通过访问反向代理服务器,间接的访问另一个网页。
- JSONP :缺点:只支持GET,不支持POST。
浏览器访问本地服务器,因为是同源的,所以会访问成功,再通过本地服务访问远程服务器,服务器和服务器之间没有跨域问题,一次可以请求成功,接着把请求的数据通过本地服务器,传递给浏览器。

//具体配置
vue.config.js
devServer: {
proxy: {
'/api': {
target: '接口url地址'
}
}
}
三、常见状态码
- 200 请求成功
- 301 永久重定向
- 302 临时重定向
- 303 该状态码表示由于请求对应的资源存在着另一个URI,应使用GET方法定向获取请求的资源, 303和302状态码有着相同的功能,但是303明确表示客户端应当采用get方法获取资源,这点与302状态码有区别。
- 400 请求错误
- 401 未授权(token失效)
- 403 禁止访问
- 404 未找到资源
- 408 请求超时
- 500 服务器内部错误
- 505 不支持HTTP版本

四、进程和线程
进程:是程序的一次是动态执行的过程。
- 特点:进程之间${\textcolor{red}{相互独立}}$,每个进程必须包含一个线程。
- 多进程浏览器:
- Browser进程:主线程
- 插件进程
- GPU进程:处理图形、视频的渲染
- 渲染进程:每一个页面都是一个进程,每个渲染进程都是多线程的。
线程:有的进程需要同时处理多个任务,传统的进程只能串行的执行,会导致阻塞的问题,所以需要引入线程,增加它的并发度。
- 特点:同一进程的各线程${\textcolor{red}{共享}}$进程拥有的资源,同一进程内的线程切换不会导致进程切换。
- GUI渲染引擎线程:解析HTML、CSS、构建DOM树,以及当页面发生重绘或者回流,会触发GUI线程。
- JS引擎线程(v8内核):负责处理js脚本,每个页面只有一个js线程,并且它和GUI渲染线程是${\colorbox{red}{互斥}}$的,也就是某一时间段内,二者只能触发执行其中一个,当一个执行完毕另一个才能执行。
- 事件触发线程:setTimeout、鼠标点击、Ajax异步请求等,当事件对应触发时候,会把回调放到事件触发线程中,等待JS引擎的处理,当JS引擎空闲时就会执行。
- 定时器触发线程:当定时器倒计时准备触发事件之前,会触发定时器线程。
五、地址栏敲回车的时候发生了什么
- 先找本机的域名映射,如果有本地 host 那么就走本地的服务。
- 如果本地没有,那么就去DNS服务器,根据DNS服务器解析ip地址,如果也没有,那么就404,如果有就找到对应服务器找到对应的资源进行加载。
- tcp连接:三次握手
- 发起http请求
- 响应请求
- 页面渲染:敲回车之后打开了一个进程,对应的HTML、CSS利用GUI引擎进行页面的渲染,JS利用V8JS引擎来处理JS。异步或者点击 事件的回调放到事件处理线程中等待JS线程的空闲处理。定时器在触发事件之前,会放到定时器线程中进行处理。
- 断开连接:在HTTP1.0中判断是否有 keep-alive
- 有:只有页面关闭时才会断开,如果进行新的请求则不会断开。
- 没有:请求完毕之后就会断开连接。
- 四次挥手:
- 客户端发出断开/等待,客户端进程发出连接释放报文。
- 服务端同意,如果同意断开就发回一个ACK确认字符然后带上一个随机生成的序列号seq=v。此时处于半链接,有可能有数据没发送完,继续发送未发送完的数据。
- 服务端发送完成/等待状态。
- 客户端确认收到数据,服务器只要收到客户端发出的确认就进入关闭状态。服务器结束TCP连接的时间要比客户端早
- 当敲回车时候会发请求,涉及到了三次握手,四次挥手
六、浏览器渲染原理
- 解析HTML生成DOM树 - 渲染引擎首先解析HTML文档,生成DOM树。
- 构建Render树 - 接下来不管是内联式,外联式还是嵌入式引入的CSS样式会被解析生成CSSOM树,根据DOM树与CSSOM树生成另外一棵用于渲染的树-渲染树(Render tree)。
- 布局Render树 - 然后对渲染树的每个节点进行布局处理,确定其在屏幕上的显示位置。
- 绘制Render树 - 最后遍历渲染树将每一个节点绘制出来以上步骤是一个渐进的过程,为了提高用户体验,渲染引擎试图尽可能快的把结果显示给最终用户。它不会等到所有HTML都被解析完才创建并布局渲染树。它会在从网络层获取文档内容的同时把已经接收到的局部内容先展示出来。
七、图片懒加载
- 存储图片的真实路径,把图片的真实路径绑定给一个以data开头的自定义属性data-url即可,页面中的img元素,如果没有src属性,浏览器就不会发出请求去下载图片(没有请求就提高了性能)
- 初始化img的时候,src不能是真实的图片地址(会一次性发送请求),也不可以是空地址或者坏地址(会出现出错图标)
- 设置img的默认src为一张1px*1px,很小很小的gif透明图片(所有的img都用这一张,只会发送一次请求),之所以需要是透明的,是需要透出通过background设置的背景图(一张loading.png,就是一个转圈圈的背景效果图)
- 需要一个滚动事件,判断元素是否在浏览器窗口,一旦进入视口才进行加载,当滚动加载的时候,就把这张透明的1px.gif图片替换为真正的url地址(也就是data-url里保存的值)
- 通过计算
innerHeight+scrollTop和offsetTop进行比较,如果二者之和大于offsetTop那么就证明图片处于当前视口,等到图片进入视口后,利用js提取data-url的真实图片地址赋值给src属性,就会去发送请求加载图片,真正实现了按需加载。
八、重绘与回流
1. 重绘
概念:页面中的元素样式发生改变,并没有影响结构,未影响在文档流中的位置,浏览器会对==元素==进行重新绘制。
导致重绘的事件:
- color、background 相关属性:background-color、background-image 等
- outline 相关属性:outline-color、outline-width 、text-decoration
- border-radius、visibility、box-shadow
2. 回流(重排)
概念:页面中的部分或者全部元素尺寸、结构、或者属性发生变化时,浏览器会重新渲染==部分或者全部文档==。
导致回流的事件:
- 页面的首次渲染
- 浏览器的窗口大小发生变化
- 元素的内容发生变化
- 元素的尺寸或者位置发生变化
- 元素的字体大小发生变化
- 激活CSS伪类
- 查询某些属性或者调用某些方法
- 添加或者删除可见的DOM元素
==注意:== 触发回流的时候,由于浏览器渲染页面基于流失布局,当触发回流时候,会导致周围的DOM元素重新排列:
- 全局:从根节点开始对整个渲染数进行重新布局。
- 局部:对渲染树的某一部分进行重新布局。
- 回流肯定会造成重绘,而重绘不一定会回流。
3. 解决办法
- 操作DOM时,尽量在低层级的DOM节点进行操作
- 不要使用table布局, 一个小的改动可能会使整个table进行重新布局
- 使用CSS的表达式
- 不要频繁操作元素的样式,对于静态页面,可以修改类名,而不是样式。
- 使用absolute或者fixed,使元素脱离文档流,这样他们发生变化就不会影响其他元素
- 避免频繁操作DOM,可以创建一个文档片段documentFragment,在它上面应用所有DOM操作,最后再把它添加到文档中
- 将元素先设置display: none,操作结束后再把它显示出来。因为在display属性为none的元素上进行的DOM操作不会引发回流和重绘。
- 将DOM的多个读操作(或者写操作)放在一起,而不是读写操作穿插着写。这得益于浏览器的渲染队列机制。
