对浏览器的理解
Jeff Jayden

浏览器的事件循环

浏览器的事件循环是一种机制,用于管理和处理异步任务,以确保 JavaScript 在单线程环境中能够处理事件、执行代码和保持响应性。

事件循环的基本原理如下:

  1. 调用栈(Call Stack): JavaScript代码的执行是通过调用栈来处理的。当你调用一个函数,它会被推入调用栈,当函数执行完成,它就会从调用栈中弹出。
  2. 任务队列(Task Queue): 任务队列存放异步任务,如事件处理函数、定时器回调等。任务队列中的任务在调用栈为空时才会被执行。
  3. 事件循环(Event Loop): 不断地检查调用栈和任务队列。如果调用栈为空,它会从任务队列中取出一个任务推入调用栈执行。这个过程不断重复,形成事件循环。

基本流程:

  • 当执行同步代码时,代码会依次推入调用栈执行。
  • 当遇到异步任务时(如定时器、事件监听器、网络请求等),会将任务放入任务队列。
  • 当调用栈为空时,事件循环检查任务队列是否有任务。
  • 如果有任务,将任务推入调用栈执行,重复上述过程。

以下是事件循环的简单示例:

1
2
3
4
5
6
7
8
9
10
11
javascriptCopy codeconsole.log('Start');

setTimeout(() => {
console.log('setTimeout callback');
}, 0);

Promise.resolve().then(() => {
console.log('Promise resolved');
});

console.log('End');

输出的顺序是:

1
2
3
4
javascriptCopy codeStart
End
Promise resolved
setTimeout callback

这是因为 setTimeout 回调会被放入任务队列,而 Promise 的回调则会在微任务队列中,微任务队列的执行优先级高于任务队列。因此,先执行微任务中的 Promise,然后再执行 setTimeout 的回调。

settimeout为什么不一定在指定时间后调用

setTimeout 的确切执行时间并不是精确的,并且不能保证在指定的时间后立即执行。这是因为JavaScript是单线程的,它在事件循环中处理任务队列,而不是通过多线程实现的。

setTimeout 的行为受到事件循环和浏览器的实现方式的影响。具体来说,setTimeout 的执行时间取决于下面的几个因素:

  1. 最小延迟时间: 浏览器通常有一个最小的定时器延迟,即使你将定时器设置为0,它也可能会有一定的延迟,通常是4毫秒。这是因为浏览器会将定时器任务添加到任务队列,并在下一个事件循环迭代中执行。
  2. 事件循环阶段: 如果在执行 setTimeout 的时候,事件循环正处于某个特定的阶段,那么定时器的回调会等到下一个合适的事件循环阶段再执行。这可能导致定时器的执行时间延迟。
  3. 页面是否被隐藏: 如果页面被隐藏(例如,用户切换到其他标签或最小化浏览器),某些浏览器可能会将定时器的执行时间延迟,以减少对系统资源的使用。

总体来说,setTimeout 并不是实现实时精确计时的最佳工具。如果需要更精确的计时,可以考虑使用 requestAnimationFrameWeb Workers,这些机制提供了更可靠的定时和并行处理能力。

内存泄漏

监听器 变量引用 闭包 循环引用

垃圾回收机制:标记清除法 引用计数法有问题(循环引用)

强制缓存与协商缓存

HTTP缓存是一种优化性能的重要机制,它可以减少资源的加载时间并减轻服务器的负担。缓存主要分为强制缓存和协商缓存两种策略。

强制缓存:

强制缓存是通过Cache-ControlExpires两个HTTP头来实现的。它告诉浏览器在特定时间内不需要再次请求相同的资源,而是直接从本地缓存中获取。常用的设置有:

  • Cache-Control: 使用max-age来设置资源在本地缓存中的最长时间(秒),例如 Cache-Control: max-age=3600 表示缓存有效期为1小时。
  • Expires: 是一个过时的设置,它指定了缓存资源的到期时间。服务器返回的时间是绝对时间,由于存在时钟不同步等问题,因此Cache-Control的使用更为推荐。

如果资源在强制缓存期间被请求,浏览器会直接从缓存中获取资源,而不发起实际的请求。

协商缓存:

协商缓存依赖于服务器与浏览器之间的“协商”,即服务器根据请求的条件来判断是否返回实际的资源或告诉浏览器直接使用缓存。协商缓存的实现主要依赖于以下两个HTTP头:

  • Last-Modified / If-Modified-Since: 服务器在响应头中返回资源的最后修改时间,下次请求时浏览器通过If-Modified-Since头将上次的最后修改时间发送给服务器。如果资源在这之后没有被修改,服务器返回304 Not Modified,浏览器直接使用缓存。
  • ETag / If-None-Match: 服务器在响应头中返回资源的唯一标识,称为ETag。下次请求时浏览器通过If-None-Match头将上次的ETag发送给服务器。如果资源的ETag匹配,服务器返回304 Not Modified,浏览器直接使用缓存。

协商缓存更加灵活,不仅可以判断资源是否修改,还可以使用更精确的标识来验证。如果同时存在Last-ModifiedETag,浏览器会优先使用ETag

cookie/sessionStorage/localStorage 的区别

参考答案:
cookie sessionStorage、localStorage都是保存本地数据的方式
其中,cookie 兼容性较好,所有浏览器均支持。

浏览器针对 cookie 会有一些默认行为,比如当响应头中出现 set-cookie 字段时,浏览器会自动保存 cookie 的值;

再比如,浏览器发送请求时,会附带匹配的 cookie 到请求头中。这些默认行为,使得cookie 长期以来担任着维持登录状态的责任。与此同时,也正是因为浏览器的默认行为给了恶意攻击者可乘之机,CSRF 攻击就是一个典型的利用 cookie 的攻击方式。虽然cookie 不断的改进,但前端仍然需要另一种更加安全的保存数据的方式
HTML5 新增了 sessionStorage 和 localStorage,前者用于保存会话级别的数据后者用于更持久的保存数据。浏览器针对它们没有任何默认行为,这样一来,就把保存数据读取数据的工作交给了前端开发者,这就让恶意攻击者难以针对登录状态进行攻击。

cookie 的大小是有限制的,一般浏览器会限制同一个域下的 cookie 总量为 4M,

而sessionStorage 和 localStorage 则没有限制cookie 会与 domain、path 关联,而 sessionStorage 和 localStorage 只与domain 关联

image-20240507202004397

cookie/sessionStorage/localStorage 的区别

Cookie、SessionStorage 和 LocalStorage 是用于在客户端存储数据的三种机制,它们之间有以下区别:

  1. 存储容量
    • Cookie:每个 Cookie 的大小一般被限制在 4KB 左右。
    • SessionStorage:通常可以存储 5MB 到 10MB 的数据。
    • LocalStorage:通常可以存储 5MB 到 10MB 的数据。
  2. 数据存储位置
    • Cookie:存储在客户端的浏览器中,并且会随着每次 HTTP 请求被发送到服务器端。
    • SessionStorage:存储在客户端的浏览器中,但数据仅在当前会话(当前页面或者标签页)有效,在关闭页面或者标签页时会被清除。
    • LocalStorage:存储在客户端的浏览器中,并且数据在不同的会话之间是共享的,除非手动清除。
  3. 数据生命周期
    • Cookie:可以设置过期时间,可以在设置的过期时间之前一直保存在客户端。
    • SessionStorage:仅在当前会话中有效,关闭页面或者标签页时会被清除。
    • LocalStorage:永久有效,除非被手动清除。
  4. 数据与服务器的通信
    • Cookie:会随着每次 HTTP 请求被发送到服务器端,因此可以用于在客户端和服务器之间传递数据。
    • SessionStorage 和 LocalStorage:数据仅在客户端存储,不会随着 HTTP 请求发送到服务器端。
  5. 访问权限
    • Cookie、SessionStorage 和 LocalStorage 的访问权限都受到同源策略的限制,即只能访问同源(协议、域名、端口号均相同)页面的存储数据。

浏览器渲染流程

解析html(生成dom树,cssom树)–> 样式计算(合并dom,cssom树)–> 布局(布局树)–> 分层(层次树)–> 生成绘制指令集(未执行, 渲染主线程工作结束)–> 分块(合成线程)–> 光栅化 gpu将每个块变成位图 就是确认每一个像素点的 rgb 信息 –> 绘制 (合成线程中执行)

为了提高解析效率,浏览器在开始解析前,会启动一个预解析的线程,率先下载 HTML 中的外部 CSS 文件和外部的 JS 文件。

如果主线程解析到 link 位置,此时外部的 CSS 文件还没有下载解析好,主线程不会等待,继续解析后续的 HTML。这是因为下载和解析 CSS 的工作是在预解析线程中进行的。这就是 CSS 不会阻塞 HTML 解析的根本原因。

这是因为 JS 代码的执行过程可能会修改当前的 DOM,所以 DOM 树的生成必须暂停。这就是 JS 会阻塞 HTML 解析的根本原因

由 Hexo 驱动 & 主题 Keep
本站由 提供部署服务
访客数 访问量