《浏览器 && 计算机网络 面试题》
概念
Q1:浏览器的同源策略是什么
难度:⭐
答案
同源策略(Same-Origin Policy)是浏览器中一种关键的安全策略,它定义了浏览器如何限制一个网页文档或脚本从一个源加载的文档或脚本与来自其他源的资源进行交互。同源是指协议、主机和端口号都相同。
引申
同源策略的主要目的是防止恶意网站通过脚本等手段,访问用户在其他网站上的敏感信息,从而保护用户的隐私和安全。
同源策略限制了以下操作:
- Cookie、LocalStorage 和 IndexDB: 一个网页只能访问自己域下的 Cookie、LocalStorage 和 IndexDB,无法访问其他域下的存储。
- DOM: 不能使用 JavaScript 获取或修改其他网页的 DOM。
- AJAX 请求: 不能通过 AJAX 请求向其他域发送请求。
同源策略的例外情况包括:
- 跨域资源共享(CORS): 服务器可以设置响应头,允许来自其他域的请求访问资源。
- JSONP: 通过动态创建
<script>
标签,可以加载其他域上的脚本。 - 图片、样式、脚本等资源文件: 可以通过相关标签加载其他域上的资源。
虽然同源策略提供了一定程度的安全性,但也限制了一些正常的 Web 开发操作。为了实现跨域访问,需要服务器端和客户端进行一些配置,以确保安全的数据交换
Q2:谈谈你对浏览器中进程和线程的理解
难度:⭐⭐
答案
在谈论浏览器中的进程和线程时,我们首先需要明确这两个概念的基本区别。在操作系统中,进程和线程是程序执行的基本单位,但它们在资源管理和执行方式上有所不同。
进程(Process)
- 进程是操作系统分配资源和调度的独立单位,每个进程都拥有自己的一套虚拟内存、全局变量等资源
- 进程之间是相互独立的,一个进程崩溃不会直接影响到其他进程。为了实现进程间的通信,需要特定的IPC(进程间通信)机制
- 在浏览器中,不同的标签页通常运行在不同的进程中(取决于浏览器及其版本)。这种设计可以增强网页的稳定性、安全性和响应性。例如,如果一个标签页崩溃,其他标签页仍可以正常工作
线程(Thread)
- 线程是进程内的执行单位,同一进程内的线程共享该进程的资源
- 线程之间的通信和切换开销比进程之间要小很多,因此在同一个进程内并发执行多个任务时,通常会使用多线程
- 浏览器中的进程通常包含多个线程,包括渲染线程、JS执行线程、事件触发线程、网络请求线程等。这些线程共同工作,让我们能够享受流畅且互动性强的网页体验
浏览器中进程和线程的交互
渲染进程
大多数浏览器采用多进程架构,每个标签页一般对应一个渲染进程,这个进程里包含了渲染线程、JS执行线程等
JS执行线程
这是渲染进程中的一个线程,负责执行JS脚本
值得注意的是,JS执行线程是单线程的,意味着同一时间只能执行一个JS任务
事件触发线程
属于浏览器的事件处理模型,用来控制事件循环,处理用户交互、定时器等异步任务
网络请求线程
浏览器中专门处理网络请求的线程,例如加载网页、下载文件等
由于浏览器是多进程的,它可以同时处理多个任务,如打开多个标签页
而浏览器的多线程能力则确保了在单个进程内可以高效地并行处理渲染、JS执行、事件处理等任务
这种多进程加多线程的架构让现代浏览器能够提供复杂且响应迅速的网络应用,同时也保证了网页运行的稳定性和安全性
引申
浏览器中的进程和线程之间的通信主要通过以下方式进行:
进程间通信(IPC)
进程是独立的运行单元,每个进程拥有自己的资源和内存空间,不同进程的内存空间彼此隔离
因此为了在进程之间传递信息,浏览器需要使用IPC(Inter-Process Communication,进程间通信)机制
IPC可以通过不同的方式实现,例如通过管道(Pipe)、信号(Signal)、套接字(Socket)、消息队列(Message queue)、共享内存等方式进行数据传输
但是,这种机制通常对于浏览器来说是透明的,浏览器会接管这部分工作,我们在使用浏览器时不需要处理这种底层细节
线程间通信
同一进程内的线程共享该进程的内存空间和资源,因此线程间的通信相对简单一些。下面的一些方式可以用于线程间的通信:
共享变量
由于线程间共享内存,因此可以通过在共享内存中设置变量来进行通信
但是这种方式需要小心处理并发问题,防止产生死锁、竞态条件等问题
使用队列
可以使用线程安全的队列来进行通信
一个线程可以将数据放入队列,其他线程则可以从队列中取出数据
在浏览器环境中,主线程与Web Workers之间的通信常常通过postMessage
和onmessage
事件进行
主线程通过postMessage
向Worker发送数据,Worker可以监听onmessage
事件来接收这些数据
Q3:RESTful规范是什么
难度:⭐⭐⭐
答案
RESTful接口规范(Representational State Transfer,表述性状态传递)是一组架构约束条件和原则,它指导着网络应用程序或Web服务的设计和开发
它由Roy Fielding在他的博士论文中提出,旨在利用现有的Web协议,如HTTP,来创建可读写的Web API
RESTful不是一种标准,而是一种风格,被广泛应用于网络服务的实现中
RESTful服务通常有以下特点:
客户端-服务器架构
客户端和服务器之间的关系通过请求和响应进行沟通
无状态
每个请求都包含了处理该请求所必须的全部信息。服务器不会存储任何客户端请求相关的状态信息
可缓存
为了提升网络效率,服务器响应的数据能够在一定条件下进行缓存
统一接口
通过一致的接口来交互,这提高了不同客户端和服务器之间交互的独立性
分层系统
客户端通常不能直接与后端服务器通信,可能通过一个或多个中介(如负载均衡器、缓存服务器等)进行交互
按需代码(可选)
服务器可以临时扩展客户端的功能,通过发送可执行的代码给客户端(例如JavaScript脚本)
REST通常使用HTTP协议,并遵循其方法来实施操作:
- GET: 请求指定的资源。GET请求应该只检索数据并且不会产生其他效果
- POST: 用于提交一个实体到指定的资源,通常导致服务器上的状态变化或副作用
- PUT: 用于替换目标资源的所有当前表述
- DELETE: 用于删除指定的资源
- PATCH: 用于对资源进行部分修改
- HEAD: 与GET方法相同,但服务器在响应中只返回头信息而不是实际的资源
- OPTIONS: 用于描述目标资源的通信选项
- CONNECT: 通常用于SSL加密服务器的HTTP代理连接
- TRACE: 回显服务器收到的请求,主要用于测试或诊断
RESTful接口通过URI来表示资源,并利用HTTP的方法来表示对这些资源的操作
这些资源在请求响应中以JSON或XML等形式的表述层来进行传输和状态表达
这种方式易于理解和使用,并且利用了Web已有的功能,避免了额外的协议或框架的需求
Q4: websocket跟http的区别
难度:⭐
答案
协议类型
HTTP:是一种无状态的请求-响应协议。客户端发送请求,服务器返回响应,通信结束后连接关闭
WebSocket:是一种全双工通信协议,允许客户端和服务器之间进行双向通信,一旦连接建立,通信通道会保持打开状态,直到一方关闭连接
连接方式
HTTP:每次请求和响应都是独立的,服务器在响应请求后关闭连接。每个请求都需要重新建立连接
WebSocket:通过一次握手建立连接后,客户端和服务器之间的连接保持打开状态,可以进行持续的双向通信,不需要每次都重新建立连接
数据传输
HTTP:数据以请求-响应的形式传输,数据包通常较大,包含头部信息
WebSocket:数据以帧(frame)的形式传输,数据包较小,头部信息少,传输效率高
通信模式
HTTP:基于请求-响应模式,客户端发起请求,服务器响应,通信结束
WebSocket:基于事件驱动模式,客户端和服务器都可以主动发送消息,适用于实时通信场景
使用场景
HTTP:适用于传统的网页请求、API 调用等需要短暂通信的场景
WebSocket:适用于需要实时双向通信的场景,如在线聊天、实时游戏、股票行情更新等
连接建立过程
HTTP:直接通过 TCP/IP 建立连接,发送 HTTP 请求
WebSocket:首先通过 HTTP/HTTPS 协议进行握手(Upgrade 请求),握手成功后切换到 WebSocket 协议进行通信
状态管理
HTTP:无状态,每次请求都是独立的,状态管理需要依赖于 cookies、session 等机制
WebSocket:有状态,连接建立后,通信过程中可以保持状态
资源消耗
HTTP:每次请求都需要建立和关闭连接,资源消耗较大,适合短暂通信
WebSocket:连接建立后保持打开状态,减少了频繁建立和关闭连接的开销,适合长时间持续通信
总结
- HTTP:适合短暂、无状态的请求-响应通信,常用于网页加载和 API 调用
- WebSocket:适合长时间、双向的实时通信,常用于在线聊天、实时更新等场景
Q5:http2里面的多路复用原理是什么
难度:⭐⭐⭐
答案
HTTP/2 引入了多路复用(Multiplexing)机制,以提高数据传输效率并减少延迟
多路复用允许多个请求和响应在同一个TCP连接上并行进行,而不是像HTTP/1.x那样每个请求都需要一个单独的TCP连接
以下是HTTP/2多路复用原理的详细解释:
数据流(Streams)
在HTTP/2中,数据流是客户端和服务器之间的双向通信通道
每个数据流都有一个唯一的标识符(Stream ID),并且可以同时存在多个数据流
帧(Frames)
HTTP/2将所有通信数据分割成较小的帧
每个帧属于一个特定的数据流,并包含流ID
帧类型包括数据帧、头帧、优先级帧、设置帧等
多路复用过程
多路复用的核心在于在同一个TCP连接上并行传输多个数据流的帧
以下是多路复用的具体步骤:
建立连接
客户端和服务器建立一个单一的TCP连接
发送请求和响应
客户端将多个HTTP请求分割成帧,并通过同一个TCP连接发送给服务器
服务器处理请求后,将响应分割成帧并返回给客户端
流的创建和管理
每个请求和响应被分配到一个独立的数据流。客户端和服务器可以同时创建、使用和关闭多个数据流
帧的传输
帧在TCP连接上传输时,可以按照任意顺序发送和接收
每个帧包含流ID,接收方可以根据流ID将帧重新组装成完整的请求或响应
流优先级和依赖
HTTP/2允许客户端为每个数据流指定优先级和依赖关系
这样,服务器可以根据优先级来优化资源分配和响应顺序
流控制
为了防止单个数据流占用过多资源,HTTP/2引入了流控制机制,限制每个数据流可以发送的帧数量
流控制由接收方管理,确保公平资源分配
多路复用的示意图
1 | 客户端 服务器 |
关键点总结
- 单一TCP连接:HTTP/2在单一TCP连接上并行传输多个请求和响应,减少了连接建立和维护的开销
- 数据流和帧:HTTP/2将数据分割成帧,每个帧属于一个特定的数据流。多个数据流的帧可以交错传输
- 流优先级和流控制:HTTP/2支持流优先级和流控制,优化资源分配和响应顺序
通过多路复用,HTTP/2显著提高了数据传输效率,减少了延迟和资源消耗,改善了网络性能和用户体验
Q6:
难度:⭐
解析
答案
引申
渲染
Q1:你对以下几个页面生命周期事件的理解DoMContentLoaded, load , beforeunload, unload
难度:⭐⭐
答案
- DOMContentLoaded:
- 触发时机: 当 DOM 树构建完成,即 HTML 文档解析完毕并生成 DOM 树时触发。此时,外部资源(如样式表、图像等)可能仍在加载。
- 用途: 适合执行不依赖外部资源的初始化操作,例如操作 DOM 元素、绑定事件监听器等。在这个阶段,页面结构已经可以被访问,但是资源的加载可能还在进行中。
- load:
- 触发时机: 当整个页面及其依赖资源都加载完成时触发。包括外部样式表、图像等。
- 用途: 适合执行依赖外部资源的操作,如处理图像大小、启动动画等。在这个阶段,所有资源都已加载完毕,页面已完全呈现给用户。
- beforeunload:
- 触发时机: 在用户尝试离开页面或关闭浏览器窗口之前触发。
- 用途: 可以用于防止用户误关闭页面时丢失数据。返回一个字符串将触发浏览器弹出确认框,允许用户取消关闭。
- unload:
- 触发时机: 当页面即将被卸载时触发,可能是用户关闭浏览器、导航到其他页面等。
- 用途: 主要用于执行清理操作,如释放资源、取消定时器等。在这个阶段,页面即将被卸载,执行时间很短。
Q2:输入url到页面显示的过程
难度:⭐⭐⭐⭐
答案
- 地址栏输入 URL:
- 用户在浏览器地址栏中输入网址(URL)。
- DNS 解析:
- 浏览器通过 DNS(域名系统)将域名解析成对应的 IP 地址。
- 如果浏览器有缓存中存在对应的 IP 地址,则跳过 DNS 解析。
- 建立 TCP 连接(三次握手):
- 浏览器通过 TCP 连接向服务器发起请求。
- 在三次握手中,首先客户端发送 SYN(同步)请求到服务器,服务器回复 SYN-ACK(同步-确认),最后客户端发送 ACK(确认)。
- 发起 HTTP 请求:
- 浏览器向服务器发起 HTTP 请求,请求中包括请求行、请求头、请求体等信息。
- 请求行包含请求的方法(GET、POST 等)和请求的资源路径。
- 服务器处理请求:
- 服务器接收到请求后,根据请求的路径和方法,执行相应的处理。
- 服务器可能会处理动态页面,执行服务器端脚本,或者直接返回静态文件。
- 服务器返回响应:
- 服务器返回 HTTP 响应,响应包括响应状态码、响应头、响应体等信息。
- 如果请求成功,响应状态码为 200;如果请求重定向,状态码为 3xx;如果有错误,状态码为 4xx 或 5xx。
- 浏览器接收响应:
- 浏览器接收到服务器返回的响应,开始解析响应内容。
- 如果响应是压缩的(如 gzip),浏览器会进行解压缩。
- 构建 DOM 和 CSSOM:
- 浏览器根据 HTML 和 CSS 构建 DOM(文档对象模型)和 CSSOM(CSS 对象模型)。
- DOM 表示页面的结构,CSSOM 表示页面的样式。
- 渲染树构建:
- 浏览器将 DOM 和 CSSOM 合并成渲染树(Render Tree),渲染树包含需要渲染的可见元素及其样式信息。
- 布局(回流):
- 浏览器计算渲染树中每个元素的位置和大小,进行布局(回流)。
- 绘制:
- 浏览器根据布局信息进行绘制,将页面绘制到屏幕上。
- TCP 连接关闭(四次挥手):
- 当浏览器获得页面内容后,开始进行四次挥手以关闭 TCP 连接。
- 在四次挥手中,首先浏览器发送 FIN(结束)请求到服务器,服务器回复 ACK(确认),然后服务器发送 FIN 请求到浏览器,最后浏览器回复 ACK。
- 交互与动画:
- 如果页面包含 JavaScript,浏览器执行脚本,可能会修改 DOM 或触发动画。
- 这些更改可能导致重新布局和重绘,特别是在修改影响几何属性的情况下。
- 渲染完成:
- 最终,用户可以看到完整的页面,页面上的元素处于可交互状态
引申
TCP(Transmission Control Protocol)是一种面向连接的协议,使用三次握手建立连接,四次挥手关闭连接。以下是三次握手和四次挥手的详细过程:
三次握手(建立连接):
- 客户端发送 SYN(同步)请求:
- 客户端向服务器发送一个包含 SYN 标志的请求,表示请求建立连接。
- 客户端选择一个初始序列号(ISN,Initial Sequence Number)。
- 服务器回应 SYN-ACK:
- 服务器接收到 SYN 请求后,回复一个包含 SYN 和 ACK(确认)标志的响应。
- 服务器也选择一个初始序列号。
- 客户端发送 ACK:
- 客户端收到服务器的 SYN-ACK 后,向服务器发送一个带有 ACK 标志的确认。
- 这个 ACK 包含服务器发送的序列号加一作为确认。
这样,通过三次握手,连接建立成功,双方都知道彼此已经准备好传输数据。
四次挥手(关闭连接):
客户端发送 FIN:
- 客户端决定不再发送数据,并向服务器发送一个包含 FIN(结束)标志的请求。
- 客户端进入 FIN-WAIT-1 状态。
服务器回应 ACK:
- 服务器收到客户端的 FIN 后,发送一个带有 ACK 的确认。
- 服务器进入 CLOSE-WAIT 状态,此时客户端可以不再接收数据。
服务器发送 FIN:
- 服务器决定不再发送数据,向客户端发送一个包含 FIN 标志的请求。
- 服务器进入 LAST-ACK 状态。
客户端回应 ACK:
- 客户端接收到服务器的 FIN 后,发送一个带有 ACK 的确认。
- 客户端进入 TIME-WAIT 状态,等待可能出现的延迟数据包。
注:TIME-WAIT 状态的目的是等待可能丢失的最后一个 ACK,以确保服务器正确关闭连接。等待时间通常是 2MSL(最大报文存活时间)。
连接关闭完成后,双方都知道对方不再发送数据,连接正式关闭。这四个步骤确保了数据的可靠传输和正确的连接关闭
Q3:DNS预解析是什么?实现方式有哪些?分别有什么优缺点?
难度:⭐⭐⭐
答案
是什么:
DNS 预解析(DNS Prefetching)是一种浏览器优化技术,旨在提前解析与当前页面相关的域名,以便在请求实际资源时减少 DNS 解析的时间,从而加速页面加载。这可以通过浏览器在后台进行域名解析来实现,而不是在资源请求时才进行 DNS 解析
实现方式:
通过
<link>
标签:- 使用
<link>
标签的rel
属性设置为 “dns-prefetch”。
1
<link rel="dns-prefetch" href="//example.com">
优点:
- 简单易用: 实现简单,只需在 HTML 中添加相应的
<link>
标签。 - 独立性: 不依赖服务器配置,可直接由前端实现。
缺点:
- 全局生效:
<link>
标签的方式是全局生效的,对所有页面都会生效,无法精确指定特定页面的预解析需求。
- 使用
通过 HTTP 头部信息:
- 服务器端可以通过发送 HTTP 头部信息来指示浏览器进行 DNS 预解析。
1
2# 在 Nginx 服务器配置中使用
add_header Link "<//example.com>; rel=dns-prefetch";1
2# 在 Apache 服务器配置中使用
Header add Link "</>; rel=dns-prefetch"优点:
- 灵活性: 可以通过服务器端配置选择性地启用或禁用 DNS 预解析,提高灵活性。
缺点:
- 服务器依赖: 需要服务器端进行配置,前端无法独立实现。
- 复杂度: 在某些情况下,服务器配置可能相对繁琐,需要更多的工作量。
引申
什么是 DNS Prefetching(DNS 预解析):
DNS Prefetching 是一种浏览器优化技术,旨在提高网页加载速度。它通过在用户点击链接之前提前解析可能会用到的域名,将解析结果缓存到系统中。这样,在用户实际点击链接时,DNS 解析时间就可以减少,从而加速页面的加载过程。
DNS 缓存与 DNS Prefetching 的关系:
- DNS 缓存:
- 浏览器和操作系统会对解析过的域名进行缓存,以避免在后续的请求中重复的进行 DNS 解析。
- DNS Prefetching:
- DNS Prefetching 是一种主动的预解析技术,它在页面加载过程中提前解析可能会用到的域名,将解析结果缓存到系统中,而不是等到实际请求时才触发 DNS 解析。
优点与缺点:
优点:
- 加速页面加载:
- DNS Prefetching 可以减少实际资源请求时的 DNS 解析时间,从而加速页面的加载速度,特别是对于包含多个域名的页面。
- 提高用户体验:
- 用户点击链接时,提前解析可能用到的域名,减少了相关资源的加载延迟,提高了用户体验。
缺点:
- 额外的网络负担:
- 预解析会在后台触发额外的 DNS 解析请求,可能增加网络负担,尤其是在用户不点击链接的情况下,可能存在一定的资源浪费。
- 安全性考虑:
- DNS Prefetching 可能会被用于追踪用户的行为,因为它预先解析了用户可能会访问的域名,从而暴露了用户的浏览意图。
- 不适用于所有场景:
- 预解析并不是在所有场景下都适用,特别是在一些网络条件下或者对于某些域名,可能并不会带来显著的性能提升。
Q4:导致页面加载时间长白屏的原因有哪一些?分别要怎么解决优化?
难度:⭐⭐⭐⭐⭐
答案
白屏问题通常指的是用户在访问网页时,页面长时间保持空白而没有显示任何内容。导致页面加载时间长白屏的原因可能有很多,以下是一些常见原因以及相应的解决优化方法:
- 资源加载慢:
- 解决方法: 优化静态资源,确保它们被正确压缩、合并,使用适当的缓存策略。使用 CDN(内容分发网络)可以提高资源加载速度。
- JavaScript执行时间过长:
- 解决方法: 检查页面中的 JavaScript 代码,确保它们优化且没有不必要的阻塞。可以将脚本放在页面底部,使用
async
或defer
属性,或者将不需要阻塞页面加载的脚本进行懒加载。
- 解决方法: 检查页面中的 JavaScript 代码,确保它们优化且没有不必要的阻塞。可以将脚本放在页面底部,使用
- CSS阻塞渲染:
- 解决方法: 确保关键的 CSS 文件被尽早加载,使用媒体查询和嵌套样式以提高页面渲染速度。避免使用阻塞渲染的 CSS,将其异步加载或按需加载。
- 服务端响应慢:
- 解决方法: 优化服务器端响应时间,确保后端代码运行高效。使用缓存、CDN、负载均衡等技术来改善服务器性能。
- 大量的DOM操作:
- 解决方法: 减少不必要的 DOM 操作,尽可能使用文档碎片或直接操作字符串来批量插入元素。使用虚拟DOM或类似的技术来最小化页面重绘和重排。
- 网络问题:
- 解决方法: 检查网络连接是否稳定,确保服务器的网络响应时间较短。使用 HTTPS 来加密连接,减少网络请求时间。
- 页面过大:
- 解决方法: 减小页面的体积,删除不必要的代码和资源。使用图片压缩、懒加载等技术来降低页面大小。
- 没有使用浏览器缓存:
- 解决方法: 启用适当的缓存策略,确保静态资源能够被浏览器缓存,减少重复加载。
- 第三方脚本或插件问题:
- 解决方法: 谨慎选择并使用第三方脚本或插件,确保它们不会成为性能瓶颈。使用异步加载或懒加载以减少对第三方资源的阻塞。
引申
白屏是什么:
白屏是指用户访问网页时,页面在一定时间内保持空白,没有显示任何内容的状态。在这段时间内,用户无法看到页面的任何可视化元素,这被视为用户体验的一个负面方面。
为什么要优化白屏时间:
优化白屏时间是为了提升用户体验和页面加载性能。用户往往期望页面能够迅速展示内容,而长时间的白屏会导致用户感到不耐烦,可能选择离开页面。快速加载页面不仅能提升用户满意度,还有助于提高页面的转化率。
白屏过程是什么样的:
白屏过程是指从用户发起页面请求到页面展示内容之间所经历的一系列步骤。以下是一般的白屏过程:
- 用户输入 URL: 用户在浏览器地址栏输入网页的 URL。
- DNS解析: 浏览器通过DNS解析获取对应的IP地址。
- 建立TCP连接: 浏览器通过IP地址建立与服务器的TCP连接。
- 发送HTTP请求: 浏览器向服务器发送HTTP请求,请求相应的页面资源。
- 服务器处理请求: 服务器接收到请求后,处理并返回相应的HTML、CSS、JavaScript等资源。
- 下载资源: 浏览器接收到响应后开始下载页面所需的资源。
- HTML解析: 浏览器开始解析HTML,构建DOM树。
- CSS解析: 解析CSS,构建CSSOM树。
- JavaScript执行: 执行页面上的JavaScript代码,可能修改DOM树或CSSOM树。
- 布局(Reflow)和绘制(Repaint): 根据DOM树和CSSOM树计算布局,然后绘制页面。
在这个过程中,用户会感知到的白屏主要出现在第7和第8步,即HTML和CSS的解析过程。为了减少白屏时间,可以采取以下措施:
- 减小资源体积: 压缩和合并CSS、JavaScript等资源,减小文件大小。
- 异步加载: 使用
async
和defer
属性,将不影响渲染的脚本异步加载。 - 懒加载: 将页面中不是立即需要的资源,如图片等,延迟加载。
- 服务端渲染(SSR): 在服务器端完成一部分渲染工作,将渲染好的HTML传输给浏览器。
Q5:浏览器的每一帧都会干什么事情
难度:⭐⭐⭐
答案
在浏览器的绘制过程中,每一帧的生成都是为了在屏幕上渲染出新的图像
为了达到流畅动画和交互的效果,现代浏览器通常目标是每秒达到60帧,也就是每帧大约16.67毫秒来完成所有的工作
每一帧中,浏览器大致会进行以下几个步骤:
处理用户输入
浏览器会处理用户的交互,如鼠标点击、滚动、键盘输入等。这些输入会触发相应的事件处理程序
JavaScript执行
执行页面中的JavaScript代码
这包括响应用户的操作(例如,点击事件),或执行任何定时任务(例如,
setTimeout
和requestAnimationFrame
的回调)请求动画帧
如果有通过
requestAnimationFrame
注册的动画,此时会执行这些动画的回调函数样式计算
计算出哪些CSS规则适用于文档中的元素
这个过程生成了一份元素和它们对应样式的映射表
布局
一旦浏览器知道了哪些规则适用于哪些元素,它就会开始计算每个元素的位置和大小
这个过程被称为布局(Layout)或重排(Reflow)
绘制
根据计算出的样式和布局信息,浏览器会开始将每个可见元素绘制到一个或多个图层上。这个过程称为绘制(Painting)
合成
最后,浏览器会将各个图层合并(Composite)起来,并显示在屏幕上
有些情况下,如果页面的某些部分只是移动而没有发生样式变化,浏览器可以只重新合成这些部分,而无需重新布局或绘制,这可以提高性能
每个步骤消耗的时间都可能不同,如果任何一个环节耗时过多,都可能导致帧率下降,从而感觉到卡顿
因此,为了保持流畅的用户体验,开发者需要关注和优化这个流程中可能导致性能瓶颈的环节,如优化JavaScript执行时间,减少重排和重绘,使用层提升等技术来提高合成的效率
请求 && 缓存
Q1:浏览器有哪几种缓存,各种缓存的优先级是什么样的
难度:⭐⭐⭐
解析
Service Worker
缓存Service Worker
缓存优先级最高Service Worker
通过编程的方式处理缓存,它可以捕获请求,也可以将响应直接放入缓存中Memory Cache
(内存缓存)Memory Cache
是存储在内存中的资源,主要用于临时缓存如用户在浏览器会话期间反复访问相同的页面,或在用户导航页面时(如点击回退按钮、重新加载页面),用于加快页面的加载速度
Memory Cache
的生命周期只在一个标签页(Tab
)关闭前有效强缓存/硬盘缓存
强缓存指的是没有进行版本比对就直接从缓存读取的资源,对应的
HTTP Header
有Expires
和Cache-Control(max-age)
浏览器在判断一个
HTTP
请求是否可以用强缓存时,会根据请求的URL
进行匹配,并确认其有效期一旦命中强缓存,浏览器会直接提供缓存数据,不会和服务器发生通信
协商缓存
当强缓存失效时,浏览器会发起 HTTP 请求检查资源是否更新,这就是协商缓存
对应的
HTTP Header
有 Last-Modified/Etag 和If-Modified-Since/If-None-Match
Web Storage(LocalStorage/sessionStorage)
Web Storage
包括localStorage
和sessionStorage
,它们用于在浏览器中存储数据注意,这些缓存不参与和服务器的通信,也不同于前面讨论的缓存策略
localStorage
数据在浏览器关闭后依然存在,除非用户或者代码删除数据sessionStorage
的生命周期只在一个浏览器窗口(或者标签页)关闭前有效推测/预加载缓存
此类缓存通过预测用户可能需要的资源,提前加载到预加载缓存中,用于提升加载性能
浏览器历史记录缓存
(Back/Forward Cache
)当用户操作浏览器的“后退”或“前进”按钮时,浏览器为了更快的加载页面,会将整个页面(包含JS运行的状态)都缓存在内存中,创建一个完成状态的拷贝
当用户再次访问该页面时,可以直接显示,不需要重新加载和运行JS
答案
优先级顺序
Service Worker
Memory Cache
(内存缓存)- 强缓存/硬盘缓存
- 协商缓存
Web Storage(LocalStorage/sessionStorage)
- 推测/预加载缓存
- 浏览器历史记录缓存 (
Back/Forward Cache
)
对于优先级,需要注意的是,Service Worker
、Memory Cache
、强缓存和协商缓存等都是从网络请求的角度出发考虑的,而 Web Storage
则更多的是作为一种在前端进行数据存储的手段
同时,因浏览器的实现细节和策略可能不同,实际的缓存行为表现可能有所差异
Q2:https握手的过程
难度:⭐⭐⭐⭐
答案
HTTPS(HyperText Transfer Protocol Secure)是HTTP的安全版本,通过SSL/TLS协议对数据进行加密,以确保数据在传输过程中不会被窃听或篡改。以下是HTTPS通信的详细过程:
客户端发起HTTPS请求
当用户在浏览器中输入一个HTTPS网址(例如:https://example.com)时,浏览器会尝试与服务器建立一个安全连接
服务器响应并发送证书
服务器接收到客户端的请求后,会返回一个包含公钥的数字证书
这个证书由可信的证书颁发机构(CA)签署,包含以下信息:
- 服务器的公钥
- 服务器的域名
- 证书的有效期
- 证书颁发机构的数字签名
客户端验证证书
客户端(通常是浏览器)会对服务器发送的证书进行验证,验证步骤包括:
- 检查证书是否由可信的CA签署
- 确认证书的域名与请求的域名匹配
- 确认证书在有效期内
如果证书验证失败,浏览器会显示一个警告,提示用户连接不安全。如果验证成功,进入下一步
生成会话密钥
客户端生成一个随机的会话密钥(对称密钥),并使用服务器的公钥对这个会话密钥进行加密,然后将加密后的会话密钥发送给服务器
服务器解密会话密钥
服务器使用其私钥解密客户端发送的会话密钥
此时,客户端和服务器都拥有了相同的会话密钥,可以用它来加密和解密后续的通信数据
建立加密通信
客户端和服务器使用会话密钥对后续的HTTP请求和响应进行加密
这样,数据在传输过程中即使被截获,也无法被解读
传输数据
客户端发送加密的HTTP请求,服务器解密后处理请求,再将加密的响应返回给客户端
整个通信过程都在加密通道中进行,确保数据的安全性
结束会话
当通信结束时,客户端和服务器可以通过发送“关闭连接”的消息来终止加密会话,释放资源
HTTPS通信过程的示意图
1 | 客户端 服务器 |
关键点总结
- SSL/TLS协议:HTTPS通过SSL/TLS协议对数据进行加密,确保数据传输的安全性
- 数字证书:服务器通过数字证书向客户端证明其身份,证书由可信的CA签署
- 会话密钥:客户端和服务器使用非对称加密交换会话密钥,之后使用对称加密进行数据传输
- 数据加密:所有传输的数据都经过加密,确保数据的机密性和完整性
Q3:http报文结构是什么样子的
难度:⭐⭐⭐
答案
HTTP请求报文
请求行(Request Line):
包含HTTP方法(如GET、POST)、请求的URL和HTTP版本
示例:
1
GET /index.html HTTP/1.1
请求头部(Request Headers):
提供关于客户端和请求的附加信息
每个头部字段由字段名和字段值组成,字段名和值之间用冒号分隔
示例:
1
2
3Host: www.example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9
空行:
请求头部和请求体之间的空行,用于分隔头部和主体
示例:
1
(空行)
请求体(Request Body)(可选):
包含要发送给服务器的数据,仅在POST、PUT等方法中存在
示例:
1
username=johndoe&password=12345
HTTP响应报文
状态行(Status Line):
包含HTTP版本、状态码和状态描述。
示例:
1
HTTP/1.1 200 OK
响应头部(Response Headers):
提供关于服务器和响应的附加信息。
每个头部字段由字段名和字段值组成,字段名和值之间用冒号分隔。
示例:
1
2
3Content-Type: text/html; charset=UTF-8
Content-Length: 138
Server: Apache/2.4.1 (Unix)
空行:
响应头部和响应体之间的空行,用于分隔头部和主体。
示例:
1
(空行)
响应体(Response Body):
包含服务器返回的实际数据,如HTML文档、图片、JSON数据等。
示例:
1
2
3
4
5
6
7
8
9
<html>
<head>
<title>Example</title>
</head>
<body>
<h1>Hello, World!</h1>
</body>
</html>
示例:完整的HTTP请求报文
1 | GET /index.html HTTP/1.1 |
示例:完整的HTTP响应报文
1 | HTTP/1.1 200 OK |
总结
HTTP报文是HTTP协议通信的基础,分为请求报文和响应报文
请求报文包含请求行、请求头部、空行和请求体(可选)
响应报文包含状态行、响应头部、空行和响应体
理解HTTP报文的结构对于调试网络通信和开发Web应用程序非常重要
Q4:POST 请求的 Content-Type有几种类型
难度:⭐⭐⭐
答案
POST 请求的 Content-Type 可以有多种类型,常见的包括:
application/x-www-form-urlencoded
:用于普通的 HTML 表单提交,在请求正文中将表单字段编码为键值对multipart/form-data
:用于上传文件或二进制数据的表单提交,请求正文以多部分形式进行编码application/json
:用于发送 JSON 格式的数据,请求正文中的数据将以 JSON 形式进行传输text/plain
:纯文本格式,适用于发送纯文本数据application/xml
:用于发送 XML 数据text/html
:用于发送 HTML 数据application/octet-stream
:用于发送二进制数据,如文件下载时使用application/graphql
:用于发送 GraphQL 查询或请求application/x-www-form-urlencoded;charset=UTF-8
:类似于application/x-www-form-urlencoded
,但指定了字符编码为 UTF-8- 其他自定义的 Content-Type 类型
服务器根据 Content-Type 来解析和处理请求数据,具体选择哪种 Content-Type 取决于请求所携带的数据类型和服务器端的要求
Q5:TCP跟UDP的区别是什么
难度:⭐
答案
TCP(Transmission Control Protocol)和UDP(User Datagram Protocol)是两种常见的传输层协议,它们在数据传输方式、可靠性、速度等方面有显著的区别
以下是详细的比较:
连接方式
TCP:面向连接的协议。数据传输前需要建立一个连接(三次握手),传输完毕后需要断开连接(四次挥手)
UDP:无连接的协议。数据传输前不需要建立连接,传输完毕后也不需要断开连接
可靠性
TCP:提供可靠的数据传输。通过序列号、确认应答(ACK)、重传机制和流量控制等手段确保数据包按顺序、无错误地到达目标
UDP:不保证数据的可靠传输。没有序列号、确认应答、重传机制和流量控制,数据包可能会丢失、重复或乱序到达
数据传输方式
TCP:面向字节流。数据以字节流的形式传输,接收方需要按顺序组装数据
UDP:面向数据报。数据以独立的数据报(Datagram)形式传输,每个数据报是一个完整的消息
速度和效率
TCP:由于需要建立连接、确认应答和重传机制,传输速度较慢,开销较大,但保证了可靠性
UDP:由于没有连接建立和确认应答等机制,传输速度较快,开销较小,但可靠性较低
头部开销
TCP:头部较大,通常为20字节,包含序列号、确认号、窗口大小、校验和等信息
UDP:头部较小,通常为8字节,仅包含源端口、目的端口、长度和校验和
适用场景
TCP:
- 文件传输:如FTP、HTTP/HTTPS等,要求数据完整性和顺序性
- 电子邮件:如SMTP,要求可靠传输
- 远程登录:如SSH、Telnet,要求可靠和有序的数据传输
UDP:
- 实时应用:如视频会议、在线游戏、VoIP等,要求低延迟和快速传输
- 广播和多播:如DNS查询、DHCP等,适用于一次发送多个接收的场景
- 简单的请求-响应协议:如TFTP、SNMP等,数据量小且对可靠性要求不高
总结
- TCP:面向连接,可靠传输,适合需要高可靠性的数据传输场景,但传输速度较慢,开销较大
- UDP:无连接,不保证可靠传输,适合对实时性要求高但对可靠性要求较低的场景,传输速度快,开销
安全
Q1:浏览器为什么要有跨域限制?
难度:⭐
答案
因为存在浏览器同源策略,所以才会有跨域问题。那么浏览器是出于何种原因会有跨域的限制呢。其实不难想到,跨域限制主要的目的就是为了用户的上网安全。 如果浏览器没有同源策略,会存在什么样的安全问题呢。
下面从 DOM 同源策略和 XMLHttpRequest 同源策略来举例说明:
如果没有 DOM 同源策略,也就是说不同域的 iframe 之间可以相互访问,那么黑客可以这样进行攻击:
做一个假网站,里面用 iframe 嵌套一个银行网站 http://mybank.com。
把 iframe 宽高啥的调整到页面全部,这样用户进来除了域名,别的部分和银行的网站没有任何差别
这时如果用户输入账号密码,我们的主网站可以跨域访问到 http://mybank.com 的 dom 节点,就可以拿到用户的账户密码了
如果没有 XMLHttpRequest 同源策略,那么黑客可以进行 CSRF(跨站请求伪造) 攻击:
用户登录了自己的银行页面 http://mybank.com
http://mybank.com 向用户的 cookie 中添加用户标识
用户浏览了恶意页面 http://evil.com,执行了页面中的恶意 AJAX 请求代码。
http://evil.com 向 http://mybank.com 发起 AJAX HTTP 请求
请求会默认把 http://mybank.com 对应 cookie 也同时发送过去
银行页面从发送的 cookie 中提取用户标识,验证用户无误,response 中返回请求数据
此时数据就泄露了。 而且由于 Ajax 在后台执行,用户无法感知这一过程
因此,有了浏览器同源策略,我们才能更安全的上网
Q2:web常见的攻击方式有哪些
难度:⭐⭐⭐
答案
SQL 注入(SQL Injection)
描述
攻击者通过输入查询字符串来尝试访问或篡改数据库
防御
使用参数化查询、预编译的SQL语句和ORM工具,避免直接将用户输入嵌入到SQL查询中
例子
攻击
用户在登录表单的用户名字段中输入
admin' --
,攻击者试图注释掉SQL语句的其余部分,来无条件访问管理员帐户1
2
3
4// 用户在登录表单中输入:
// 用户名: hacker' --
// 密码: xxx
// 恶意构造的输入用于修改 SQL 查询防御
使用参数化查询,如在PHP中使用PDO进行带参数的查询
1
2
3
4
5
6
7const mysql = require('mysql');
const connection = mysql.createConnection({ /*...*/ });
const username = '用户输入的用户名';
const password = '用户输入的密码';
connection.query('SELECT * FROM users WHERE username = ? AND password = ?', [username, password], function(error, results, fields) {
// ...
});
跨站脚本攻击(XSS, Cross-Site Scripting)
描述
攻击者在网页上注入恶意脚本,当其它用户浏览该网页时执行这些脚本
防御
对所有的输入进行适当的过滤或转义,使用CSP(内容安全策略)减少被攻击的风险
例子
攻击
在评论或消息中加入
<script>
标签1
2// 用户评论
const userComment = "<script>maliciousCode()</script>";防御
输入内容转义
1
2
3
4
5
6
7
8
9
10
11
12function escapeHTML(text) {
const map = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
};
return text.replace(/[&<>"']/g, function(m) { return map[m]; });
}
// 使用转义函数保护输出
const safeComment = escapeHTML(userComment);
跨站请求伪造(CSRF, Cross-Site Request Forgery)
描述
攻击者诱使已登录用户在不知情的情况下执行非本意的操作,如状态改变请求(State-changing request)
防御
使用Anti-CSRF令牌,确保只有来自用户本意的请求才被执行
例子
攻击
在第三方网站中嵌入执行状态修改操作的请求
1
<img src="http://bank.com/transfer.do?amt=1000&toAccount=hacker">
防御
使用CSRF Token
1
2
3
4
5
6
7
8
9
10
11// 在服务端生成CSRF Token,并在客户端表单中包含它
<form action="/transfer" method="POST">
<input type="hidden" name="csrf_token" value="服务器生成的CSRF Token">
<!-- 其他表单内容 -->
</form>
// 在处理请求时检验CSRF Token
if (request.body.csrf_token !== request.session.csrf_token) {
// CSRF Token 不匹配或不存在
throw new Error('Invalid CSRF token');
}
会话劫持(Session Hijacking)
描述
攻击者尝试窃取或篡改用户的会话令牌来盗用用户的身份
防御
使用HTTPS保持传输层的安全,利用HttpOnly和Secure标志设置Cookie,限制Cookie的访问
例子
攻击
通过网络嗅探等手段获取用户的Session ID
1
// 攻击者通过某种手段获得了 victimSessionId
防御
安全设置Cookie标志
1
2// 在服务器端设置 Cookie 时,使用 HttpOnly 和 Secure 标志
response.setHeader("Set-Cookie", "sessionId=您的SessionID; HttpOnly; Secure");
DDOS 攻击(Distributed Denial of Service)
描述
通过大量恶意流量使网站服务不可用
防御
使用DDoS防护服务,如云服务提供的流量分析和过滤工具
例子
攻击
DDoS攻击通常由网络机器实施,不通过前端JavaScript实现
防御
配置Web服务器或使用服务商提供的工具
这是在服务端或网络层进行的配置,通常不是用 JavaScript 代码完成的
例如,配置 Nginx 或使用 Cloudflare 等服务
上传漏洞
描述
攻击者通过上传恶意文件(如脚本)到服务器执行
防御
限制上传文件的类型和大小,对上传的文件进行扫描和验证,不直接在Web根目录下存储或执行用户上传的文件
例子
攻击
上传包含恶意代码的文件
1
2<!-- 攻击者试图上传一个恶意脚本文件 -->
<input type="file" name="maliciousFile">防御
服务器端文件验证
1
2
3
4
5
6
7
8
9
10// 在服务器端使用 JavaScript 进行文件扩展名检查(以 Node.js 为例)
const path = require('path');
app.post('/upload', function (req, res) {
const fileExtension = path.extname(req.files.uploadedFile.name).toLowerCase();
if (fileExtension !== '.png' && fileExtension !== '.jpg') {
// 拒绝上传
return res.status(400).send('Invalid file type!');
}
// 允许上传
});
敏感数据暴露
描述
敏感信息(如密码、令牌、秘钥)被泄露
防御
使用强加密算法保护数据传输和存储,对敏感数据加密,使用安全的密码散列函数存储密码
安全配置错误
描述
错误配置安全头部、错误暴露系统信息、不必要的服务运行在服务器上
防御
遵循安全的默认配置原则,定期进行安全检查和更新,最小化暴露的系统信息
使用已知的有漏洞的组件
描述
软件使用了含有已知漏洞的组件或框架
防御
定期使用工具检查和更新依赖项以修补已知漏洞,使用依赖检查工具管理项目的依赖
不安全的直接对象引用(IDOR, Insecure Direct Object References)
描述
通过修改URL或表单参数来访问没有权限的数据
防御
实施严格的访问控制检查,确保用户只能访问他们授权的数据
每种攻击都有其特定的防御策略,维护Web安全需要持续的努力和更新策略以对抗新出现的威胁。
Q3:什么是DNS劫持
难度:⭐⭐⭐
答案
DNS劫持(DNS Hijacking)是一种网络攻击方式
攻击者通过篡改域名系统(DNS)的解析过程,将用户对特定域名的请求重定向到错误或恶意的IP地址
以下是对DNS劫持的详细解释:
工作原理
当用户在浏览器中输入一个域名(如example.com)时,DNS会将该域名解析为对应的IP地址,以便浏览器能够连接到目标服务器
DNS劫持的攻击者通过多种手段篡改这一解析过程,使得用户被引导到错误或恶意的服务器
常见的DNS劫持方法
本地劫持:
攻击者在用户的计算机或路由器上安装恶意软件,篡改本地的DNS设置,使用户的DNS请求被重定向到攻击者控制的DNS服务器
路由劫持:
攻击者通过篡改网络路由器或交换机的设置,拦截并修改DNS请求和响应数据包
DNS服务器劫持:
攻击者直接入侵DNS服务器,篡改其上的DNS记录,使得用户的DNS请求返回错误的IP地址
中间人攻击(MITM):
攻击者在用户和DNS服务器之间插入一个中间人,拦截并篡改DNS请求和响应
影响和危害
隐私泄露:
攻击者可以通过DNS劫持将用户引导到钓鱼网站,从而窃取用户的敏感信息,如用户名、密码、信用卡信息等
恶意软件传播:
用户被引导到恶意网站,可能会被强制下载和安装恶意软件,进一步危害用户的系统安全
广告注入:
攻击者可以通过DNS劫持将用户引导到带有大量广告的网站,获取非法广告收益
服务中断:
DNS劫持可能导致合法网站的访问中断,影响业务的正常运营和用户体验
防护措施
使用安全DNS服务:
选择安全、可信赖的DNS服务提供商,如Google Public DNS、Cloudflare DNS等,这些服务通常具有更好的安全性和防护措施
启用DNSSEC:
DNSSEC(DNS Security Extensions)是一种安全协议,通过对DNS数据进行数字签名,确保数据的完整性和真实性,防止DNS劫持
定期更新和扫描:
定期更新操作系统、路由器固件和防病毒软件,扫描系统中的恶意软件,防止本地劫持
使用HTTPS:
通过使用HTTPS加密通信,防止中间人攻击,即使DNS被劫持,攻击者也无法篡改加密的数据
监控和检测:
实时监控DNS流量,检测异常的DNS解析行为,及时发现和应对潜在的DNS劫持攻击
总结
DNS劫持是一种严重的网络安全威胁,可能导致隐私泄露、恶意软件传播、广告注入和服务中断
通过采取适当的防护措施,如使用安全DNS服务、启用DNSSEC、定期更新和扫描系统、使用HTTPS以及监控DNS流量,可以有效减少DNS劫持的风险
Q4:https为什么是安全的
难度:⭐⭐⭐⭐
答案
HTTPS(HyperText Transfer Protocol Secure)之所以被认为是安全的,主要是因为它在HTTP的基础上增加了SSL/TLS(Secure Sockets Layer/Transport Layer Security)加密层
从而提供了以下几个关键的安全特性:
数据加密
加密通信:HTTPS使用SSL/TLS协议对传输的数据进行加密,使得数据在客户端和服务器之间传输时无法被窃听或篡改
对称加密:在通信过程中,使用对称加密算法(如AES)对数据进行加密和解密。对称加密的密钥是通过非对称加密算法(如RSA)安全交换的
数据完整性
- 防篡改:通过消息认证码(MAC)或哈希函数(如SHA-256),确保数据在传输过程中未被篡改。如果数据被篡改,接收方可以通过校验发现并拒绝篡改的数据
身份验证
服务器身份验证:通过数字证书(由可信的证书颁发机构CA签发),客户端可以验证服务器的身份,确保与之通信的确实是预期的服务器
客户端身份验证(可选):在某些情况下,服务器也可以要求客户端提供数字证书,以验证客户端的身份
防止中间人攻击
- 防止中间人攻击(MITM):通过加密和身份验证机制,HTTPS可以有效防止中间人攻击。攻击者无法在不被发现的情况下截获、篡改或伪造通信内容
HTTPS 工作原理简述
客户端发起HTTPS请求:
客户端向服务器发起HTTPS连接请求
服务器返回数字证书:
服务器返回包含公钥的数字证书,该证书由可信的CA签发
客户端验证证书:
客户端验证服务器的数字证书是否合法和有效
如果验证通过,客户端生成一个随机的对称加密密钥
密钥交换:
客户端使用服务器的公钥加密对称加密密钥,并发送给服务器
服务器使用自己的私钥解密,得到对称加密密钥
建立安全通信:
双方使用对称加密密钥进行加密通信,确保数据在传输过程中保持机密性和完整性
HTTPS的优势
- 数据保密性:加密通信确保数据在传输过程中不会被窃听
- 数据完整性:防止数据在传输过程中被篡改
- 身份验证:确保通信双方的身份是合法的,防止中间人攻击
- 用户信任:使用HTTPS的网站通常会在浏览器地址栏显示锁定图标,增加用户的信任感
总结
HTTPS通过SSL/TLS协议提供数据加密、数据完整性和身份验证,确保客户端和服务器之间的通信安全
它有效防止了窃听、篡改和中间人攻击等安全威胁,使得用户在访问网站时能够更加放心地传输敏感信息
调试开发
Q1:浏览器乱码原因是什么?怎么解决?
难度:⭐⭐
答案
字符编码不匹配:
原因: HTML 文件和浏览器解析的字符编码不一致,或者在请求资源时服务器未正确设置字符编码。
解决: 在 HTML 文件的
<head>
部分添加正确的字符编码声明。1
<meta charset="UTF-8">
确保服务器正确设置响应头的字符编码。
字体缺失或不支持:
原因: 浏览器默认字体无法显示特定字符,或者网页使用的字体不被系统支持。
解决: 使用通用字体,如宋体、微软雅黑等,或者在 CSS 中指定多个备选字体。
1
2
3body {
font-family: 'Arial', 'Helvetica', sans-serif;
}
文本文件编码问题:
原因: 文本文件(如 JavaScript 文件)使用了不同的编码,导致浏览器解析时出现乱码。
解决: 统一使用相同的字符编码,并确保在文件头部声明正确的编码。
1
<script src="example.js" charset="UTF-8"></script>
网络传输问题:
- 原因: 在网络传输过程中,如果没有正确设置字符编码,可能导致乱码。
- 解决: 在服务器端设置正确的字符编码,确保响应头中包含正确的 Content-Type。
非法字符或损坏的文件:
- 原因: 文件中包含非法字符或文件损坏。
- 解决: 检查文件内容,确保文件中的字符都是合法的,并尝试重新下载或替换文件。
浏览器缓存问题:
- 原因: 之前的资源缓存可能与当前网页字符编码不一致。
- 解决: 尝试清除浏览器缓存,或使用浏览器的无痕模式加载页面。
Q2:如何实现浏览器内多个标签页之间的通信
难度:⭐⭐⭐
答案
LocalStorage 或 SessionStorage:
- 使用
localStorage
或sessionStorage
存储数据,这两者在同一域下的不同标签页之间是共享的。
1
2
3
4
5// 在一个标签页中设置数据
localStorage.setItem('key', 'value');
// 在另一个标签页中获取数据
let data = localStorage.getItem('key');- 使用
Broadcast Channel API:
- 使用
BroadcastChannel
API,可以在同一域下的不同标签页之间广播消息。
1
2
3
4
5
6
7
8
9// 在一个标签页中发送消息
const channel = new BroadcastChannel('my_channel');
channel.postMessage('Hello from Tab 1');
// 在另一个标签页中接收消息
const channel = new BroadcastChannel('my_channel');
channel.onmessage = function(event) {
console.log(event.data); // 输出:Hello from Tab 1
};- 使用
Window.postMessage:
- 使用
window.postMessage
方法,该方法允许一个窗口向另一个窗口发送消息。
1
2
3
4
5
6
7// 在一个标签页中发送消息
window.postMessage('Hello from Tab 1', '*');
// 在另一个标签页中接收消息
window.addEventListener('message', function(event) {
console.log(event.data); // 输出:Hello from Tab 1
});- 使用
Service Workers:
- 使用 Service Workers 在后台执行脚本,可以通过
postMessage
进行通信,从而实现多个标签页之间的消息传递。
1
2
3
4
5
6
7// 主线程(页面)
navigator.serviceWorker.controller.postMessage('Hello from Tab 1');
// Service Worker
self.addEventListener('message', function(event) {
console.log(event.data); // 输出:Hello from Tab 1
});- 使用 Service Workers 在后台执行脚本,可以通过
Shared Workers:
- 使用 Shared Workers 共享状态和消息,这样不同的标签页可以共享相同的工作线程。
1
2
3
4
5
6
7
8
9
10
11
12
13// 在多个标签页中共享的脚本
const sharedWorker = new SharedWorker('shared-worker.js');
sharedWorker.port.start();
sharedWorker.port.postMessage('Hello from Tab 1');
// Shared Worker
self.addEventListener('connect', function(event) {
const port = event.ports[0];
port.addEventListener('message', function(event) {
console.log(event.data); // 输出:Hello from Tab 1
});
port.start();
});
他们之间的区别:
方法 | 区别与注意事项 | 适用场景 |
---|---|---|
LocalStorage | - 存储容量较小,通常在 5MB 左右。 - 数据改变时,其他标签页需要轮询检查。 | - 小量数据的共享。 |
Broadcast Channel API | - 要求同一域下的标签页。 - 可以轻松地广播消息给所有其他标签页。 | - 需要实时广播消息给所有标签页的场景。 |
Window.postMessage | - 跨域通信时,需要确保目标窗口的 origin 和当前窗口的 targetOrigin 匹配。 - 安全性要求高。 | - 跨域通信,安全性要求高的场景。 |
Service Workers | - 可以在后台执行脚本。 - 仅在 HTTPS 或 localhost 下工作。 - 可以实现离线缓存等功能。 - 需要考虑 Service Worker 生命周期。 | - 后台执行任务,实现离线功能,推送通知等场景。 |
Shared Workers | - 多个标签页可以共享相同的工作线程。 - 需要使用相对较新的浏览器。 - 全局共享状态,需要注意状态同步和互斥。 - 需要考虑 Shared Worker 的生命周期。 | - 多个标签页需要共享状态,如在线编辑文档等场景。 |
Q3:前端跨页面通信有哪些方法?
难度:⭐⭐⭐⭐
答案
前端跨页面通信是指在不同的浏览器标签页或窗口之间进行信息传递
以下是一些常见的前端跨页面通信的方法:
LocalStorage 或 SessionStorage
使用浏览器的本地存储机制,将数据存储在LocalStorage或SessionStorage中
这样可以在同一浏览器的不同标签页之间共享数据
1
2
3
4
5// 在页面1中设置数据
localStorage.setItem('key', 'value');
// 在页面2中获取数据
var data = localStorage.getItem('key');Cookies
将数据存储在 Cookies 中,因为 Cookies 是在同一域名下的所有页面之间共享的
1
2
3
4
5// 在页面1中设置Cookie
document.cookie = "key=value";
// 在页面2中获取Cookie
var cookies = document.cookie;Broadcast Channel API
使用 Broadcast Channel API,该 API 允许一个文档向其他具有相同频道名称的文档广播消息
1
2
3
4
5
6
7
8
9// 在页面1中广播消息
var channel = new BroadcastChannel('myChannel');
channel.postMessage('Hello from Page 1');
// 在页面2中监听消息
var channel = new BroadcastChannel('myChannel');
channel.onmessage = function(event) {
console.log('Received message:', event.data);
};Window.postMessage
使用
window.postMessage
方法,允许从一个窗口向另一个窗口传递数据1
2
3
4
5
6
7
8
9// 在页面1中发送消息
window.postMessage('Hello from Page 1', 'https://example.com');
// 在页面2中监听消息
window.addEventListener('message', function(event) {
if (event.origin === 'https://example.com') {
console.log('Received message:', event.data);
}
});Shared Workers
使用共享 Web Worker,它是在多个浏览上下文(页面、标签页等)之间共享的后台线程,可以用于进行跨页面通信
1
2
3
4
5// 在页面1中创建 Shared Worker
var worker = new SharedWorker('worker.js');
// 在页面2中连接到 Shared Worker
var worker = new SharedWorker('worker.js');IndexedDB: 使用 IndexedDB 进行本地数据库存储,不同页面可以访问同一个数据库
这些方法的选择取决于具体的需求和使用场景。localStorage 和 sessionStorage 适用于较小量的数据,而 Broadcast Channel API 和 Window.postMessage 更适用于需要频繁通信的情况。 Shared Workers 和 IndexedDB 则适用于更复杂的场景,涉及到大量数据或需要在后台进行处理。
Q4:为什么推荐把静态资源放在CDN上
难度:⭐⭐
答案
将静态资源放在内容分发网络(CDN)上有很多优势
以下是一些主要原因:
提高加载速度
CDN在全球范围内有多个分布式的服务器节点
当用户请求静态资源时,CDN会将请求路由到离用户最近的服务器节点,从而减少了网络延迟和加载时间
这对于用户体验的提升非常显著,特别是对于全球用户访问的网站
减少服务器负载
通过将静态资源托管在CDN上,可以显著减少原始服务器的负载
CDN会缓存这些资源并处理大部分的请求,这样原始服务器可以专注于处理动态内容和业务逻辑,提高整体系统的性能和稳定性
增强可扩展性
CDN具有很强的扩展能力,可以应对突发的大量请求
例如,在某个资源突然变得非常受欢迎时,CDN能够迅速扩展以处理大量的并发请求,而不需要对原始服务器进行复杂的扩容操作
提高可靠性
CDN通常具有高可用性和冗余机制,即使某个节点出现故障,流量也可以迅速切换到其他可用节点,确保资源的高可用性和可靠性
安全性
CDN提供了多种安全功能,如DDoS防护、WAF(Web应用防火墙)、SSL/TLS加密等,可以帮助保护网站免受各种网络攻击
节省带宽成本
CDN通过缓存和优化传输,减少了原始服务器的带宽消耗
许多CDN提供商还与ISP有合作,可以进一步降低带宽成本
降低延迟
CDN通过智能路由和优化传输路径,减少了数据包在网络中的传输时间,进一步降低了延迟,提高了资源加载速度
缓解网络拥堵
CDN的分布式架构可以有效地缓解网络拥堵问题,通过将请求分散到多个节点,避免了单一服务器或网络路径的过载
地理位置优化
CDN会根据用户的地理位置动态选择最优的服务器节点进行响应,这样可以最大程度地利用网络资源,提高访问速度
版本控制和缓存管理
CDN提供了强大的缓存管理功能,可以设置缓存策略、版本控制和缓存刷新策略,确保用户始终获取最新的资源版本
总结
将静态资源放在CDN上,不仅可以显著提升网站的性能和用户体验,还可以提高系统的可靠性、安全性和可扩展性,同时降低运营成本
因此,使用CDN来托管静态资源是一个非常推荐的实践
Q5:Blob、ArrayBuffer和Base64分别有什么使用场景
难度:⭐⭐⭐⭐
答案
Blob
Blob(Binary Large Object)是表示二进制数据的对象,通常用于处理文件数据
它可以包含任意类型的数据,并且可以通过JavaScript的File API进行操作
功能
- 存储二进制数据:Blob可以存储图像、视频、音频、文本文件等各种类型的二进制数据
- 生成文件对象:可以通过Blob对象生成文件对象,从而进行文件的读取、上传和下载操作
- 流式处理:Blob支持分块读取,适合处理大文件
使用场景
- 文件上传:将用户选择的文件转换为Blob对象,然后通过FormData上传到服务器
- 文件下载:从服务器获取二进制数据,生成Blob对象并创建下载链接,供用户下载文件
- 图像处理:将Canvas绘制的图像数据转换为Blob对象,然后进行保存或上传操作
- 音视频处理:将音频或视频数据存储为Blob对象,以便进行播放或传输
ArrayBuffer
ArrayBuffer是一个通用的、固定长度的二进制数据缓冲区
它不能直接操作数据,但可以通过视图(如TypedArray和DataView)来读取和写入数据
功能
- 存储和操作二进制数据:ArrayBuffer可以存储二进制数据,并通过视图进行读取和写入操作
- 高效的数据处理:适用于需要高效处理大量二进制数据的场景,如图像处理、音视频处理等
- 与Web API集成:ArrayBuffer可以与许多Web API(如WebSockets、Fetch API等)集成,进行二进制数据的传输和处理
使用场景
- 音视频处理:使用ArrayBuffer存储音频或视频数据,并通过TypedArray进行高效处理
- 图像处理:使用ArrayBuffer存储图像数据,并通过视图进行操作和转换
- 网络传输:通过WebSockets或Fetch API传输二进制数据,如文件或流媒体
- 数据解析:解析二进制文件格式,如读取和解析二进制协议数据
Base64
Base64是一种基于64个字符的编码方式,用于将二进制数据编码为ASCII字符串
它常用于在不支持二进制数据传输的场景中传输二进制数据
功能
- 数据编码:将二进制数据编码为Base64字符串,以便在文本环境中传输或存储
- 数据解码:将Base64字符串解码为原始的二进制数据
- 数据嵌入:将二进制数据嵌入到文本内容中,如HTML、CSS或JSON
使用场景
- 数据传输:在不支持二进制传输的协议或环境中(如JSON、XML)传输二进制数据
- 数据存储:将二进制数据以字符串形式存储在数据库或文本文件中
- 数据嵌入:将图像、音频或其他二进制数据嵌入到HTML、CSS或JSON中,如在HTML中嵌入Base64编码的图像数据
- 电子邮件附件:在电子邮件中嵌入Base64编码的附件,以便在不支持二进制附件的环境中传输文件
总结
- Blob:用于存储和处理二进制数据,适合文件上传、下载和流式处理等场景
- ArrayBuffer:用于高效存储和操作二进制数据,适合音视频处理、图像处理和网络传输等场景
- Base64:用于将二进制数据编码为ASCII字符串,适合在不支持二进制传输的环境中传输或存储数据