浏览器之基础/缓存/渲染(问答)

目前准备到了浏览器这部分,加上之前面试过程中,关于这部分的问题都答的很差,所以把一些自认为有价值的知识点整理了出来。


什么是跨域?为什么浏览器要使用同源策略?你有几种方式可以解决跨域问题?了解预检请求嘛?

浏览器的同源策略导致了跨域,同源策略属于浏览器的安全机制,如果协议、域名、端口有一个不同就是跨域,请求会失败。 同源策略主要是用于防范CSRF攻击的。简单点说,CSRF 攻击是利用用户的登录态发起恶意请求。

解决跨域的方法: JSONP/CORS/代理服务

1.JSONP

JSONP 的原理很简单,就是利用 <script> 标签没有跨域限制的漏洞。通过 <script> 标签指向一个需要访问的地址并提供一个回调函数来接收数据当需要通讯时。JSONP 使用简单且兼容性不错,但是只限于 get 请求。

1
2
3
4
5
6
<script src="http://domain/api?param1=a&param2=b&callback=jsonp"></script>
<script>
    function jsonp(data) {
        console.log(data)
    }
</script>  

2.CORS

CORS 需要浏览器和后端同时支持。浏览器会自动进行 CORS 通信,实现 CORS 通信的关键是后端。只要后端实现了 CORS,就实现了跨域。 服务端设置 Access-Control-Allow-Origin 就可以开启 CORS。 该属性表示哪些域名可以访问资源,如果设置通配符则表示所有网站都可以访问资源。 虽然CORS和前端没什么关系,但是需要知道通过这种方式解决跨域问题的话,发送的请求会分别为简单请求和复杂请求。 对于复杂请求来说,首先会发起一个预检请求,该请求是 option 方法的,通过该请求来知道服务端是否允许跨域请求。

3.代理 通过代理把前端请求的域名转发到后端域名上,隐藏真实的服务端。可通过webpacknigix实现。


有几种方式可以实现存储功能,分别有什么优缺点?什么是 Service Worker?

cookie,localStorage,sessionStorage,indexDB皆可实现存储

特性 cookie localStorage sessionStorage indexDB
数据生命周期 一般由服务器生成,可以设置过期时间 除非被清理,否则一直存在 页面关闭 除非被清理,否则一直存在
数据存储大小 4K 5M 5M 无限

cookie使用时需要注意安全性

属性 作用
value 如果用于保存用户登录态,应该将该值加密,不能使用明文的用户标识
http-only 不能通过 JS 访问 Cookie,减少 XSS 攻击
secure 只能在协议为 HTTPS 的请求中携带
same-site 规定浏览器不能在跨域请求中携带 Cookie,减少 CSRF 攻击

Service Worker 是运行在浏览器背后的独立线程,一般可以用来实现缓存功能。使用 Service Worker 的话,传输协议必须为 HTTPS。因为 Service Worker 中涉及到请求拦截,所以必须使用 HTTPS 协议来保障安全。实现缓存功能一般分为三个步骤:

  • 首先需要先注册 Service Worker,
  • 然后监听到 install 事件以后就可以缓存需要的文件,
  • 在下次用户访问的时候就可以通过拦截请求的方式查询是否存在缓存,存在缓存的话就可以直接读取缓存文件,否则就去请求数据。

事件的触发过程是怎么样的?知道什么是事件代理嘛?

事件触发有三个阶段:

  1. window 往事件触发处传播,遇到注册的捕获事件会触发
  2. 传播到事件触发处时触发注册的事件
  3. 从事件触发处往 window 传播,遇到注册的冒泡事件会触发

一般来说,如果我们只希望事件只触发在目标上,这时候可以使用 stopPropagation 来阻止事件的进一步传播。

事件代理 如果一个节点中的子节点是动态生成的,那么子节点需要注册事件的话应该注册在父节点上 事件代理的方式相较于直接给目标注册事件来说,因为不需要给子节点注销事件,所以节省了内存。


浏览器缓存机制

对于一个数据请求来说,可以分为发起网络请求、后端处理、浏览器响应 三个步骤。浏览器缓存可以帮助我们在第一和第三步骤中优化性能。比如说直接使用缓存而不发起请求,或者发起了请求但后端存储的数据和前端一致,那么就没有必要再将数据回传回来,这样就减少了响应数据。

接下来的内容中我们将通过以下几个部分来探讨浏览器缓存机制:

缓存位置:Service Worker > Memory Cache > Disk Cache > Push Cache > 网络请求,以上表示优先级从大到小排序。

缓存策略: 强缓存和协商缓存,并且缓存策略都是通过设置 HTTP Header 来实现的。不细说了,至今还没人问过。

实际场景应用缓存策略: 对于频繁变动的资源:首先需要使用 Cache-Control: no-cache 使浏览器每次都请求服务器,然后配合 ETag 或者 Last-Modified 来验证资源是否有效。这样的做法虽然不能节省请求数量,但是能显著减少响应数据大小。

对于代码文件:现在都会使用工具来打包代码,那么我们就可以对文件名进行哈希处理,只有当代码修改后才会生成新的文件名。基于此,我们就可以给代码文件设置缓存有效期一年 Cache-Control: max-age=31536000,这样只有当 HTML 文件中引入的文件名发生了改变才会去下载最新的代码文件,否则就一直使用缓存。

目前还没遇到面试官问这块的知识点,性能优化方面还是围绕webpack聊的比较多。


浏览器渲染原理

不同的浏览器有不同的渲染引擎,这里主要说的是 Webkit 渲染引擎。

当我们打开一个网页时,浏览器都会去请求对应的 HTML 文件。虽然平时我们写代码时都会分为 JS、CSS、HTML 文件,也就是字符串,但是计算机硬件是不理解这些字符串的,所以在网络中传输的内容其实都是 01 这些字节数据。当浏览器接收到这些字节数据以后,它会将这些字节数据转换为字符串,也就是我们写的代码。

当数据转换为字符串以后,浏览器会先将这些字符串通过词法分析转换为标记token),这一过程在词法分析中叫做标记化tokenization)。

那么什么是标记呢?简单来说,标记还是字符串,是构成代码的最小单位。这一过程会将代码分拆成一块块,并给这些内容打上标记,便于理解这些最小单位的代码是什么意思。

当结束标记化后,这些标记会紧接着转换为 Node,最后这些 Node 会根据不同 Node 之前的联系构建为一颗 DOM 树。

以上就是浏览器从网络中接收到 HTML 文件然后一系列的转换过程,过程大致如下: 字节数据 => 字符串 => 标记(Token) => Node => DOM

在解析 HTML 文件的时候,浏览器还会遇到 CSS 和 JS 文件,这时候浏览器也会去下载并解析这些文件,解析 CSS 文件的过程与 HTML 文件极为类似,最后会生成一颗 CSSOM树。

当我们生成 DOM 树和 CSSOM 树以后,浏览器会将这两棵树组合为渲染树,然后根据渲染树来进行布局(也可以叫做回流),然后调用 GPU 绘制,合成图层,显示在屏幕上。


除此之外,还有些零碎的知识点,因为已经熟记于心就不做展开了,包括: 1.重绘(Repaint)和回流(Reflow) 2.requestAnimationFrame API 绘制动画 3.操作 DOM 性能很差的原因

前天拿到了一个 offer,但是这次面试的体验很差,待遇也没达到预期,三月继续加油吧,祝我好运 🍀