玩命加载中 . . .

备战大厂前端面试之浏览器篇


前端面试题系列文章:

浏览器

对浏览器内核的理解

主要分成两部分:渲染引擎和 JS 引擎。

  • 渲染引擎:负责取得网页的内容(HTML、XML、图像等等)、整理讯息(例如加入 CSS 等),以及计算网页的显示方式,然后渲染到用户的屏幕上。
  • JS 引擎:解析和执行 javascript 来实现逻辑和控制 DOM 进行交互。

最开始渲染引擎和 JS 引擎并没有区分的很明确,后来 JS 引擎越来越独立,内核就倾向于只指渲染引擎。

GET 和 POST 的区别

GETPOST 是HTTP协议中的两种常用的请求方法,它们在以下几个方面有所区别:

  • 参数传递位置:

GET请求的参数通常以查询字符串的形式附加在URL的末尾,例如:http://example.com/path?param1=value1&param2=value2 。参数可见,可以被书签保存和缓存,长度受URL长度限制。
POST请求的参数通常包含在请求体中,不会显示在URL中,因此对于传递敏感信息更为安全。参数长度理论上没有限制,但实际上会受到服务器和浏览器的限制。

  • 安全性:

GET 请求是幂等且安全的,即对服务器资源的请求不会产生副作用,不会修改、删除或创建资源。
POST 请求不是幂等的,可能会对服务器资源产生副作用,例如创建新的资源或更新已有资源。

  • 缓存:

GET 请求可以被缓存,因为相同的URL和参数会产生相同的结果。可以利用浏览器缓存来提高性能。
POST 请求默认不会被缓存,每次发送都会向服务器请求最新数据。

  • 可见性:

GET 请求的参数和URL可见,可以被用户或其他人看到。因此,不应将敏感信息放在URL中。
POST 请求的参数不会显示在URL中,对于传递敏感信息更为安全。

  • 用途:

GET 请求用于获取数据,一般用于数据的读取。
POST 请求用于提交数据,一般用于数据的创建、更新或删除。
需要根据具体的需求和场景来选择使用 GETPOST 方法。一般来说,如果是获取数据或不对服务器产生副作用的操作,可以使用 GET 请求;如果涉及敏感信息的传递、数据的提交或对服务器资源产生副作用的操作,应使用 POST 请求。

事件循环

首先,JavaScript是一门单线程的语言,意味着同一时间内只能做一件事,但是这并不意味着单线程就是阻塞,而实现单线程非阻塞的方法就是事件循环。

在JavaScript中,所有的任务都可以分为

  • 同步任务:立即执行的任务,同步任务一般会直接进入到主线程中执行
  • 异步任务:异步执行的任务,比如ajax网络请求,setTimeout定时函数等

在执行代码时,同步任务进入主线程,即主执行栈,异步任务进入任务队列,主线程内的任务执行完毕为空,会去任务队列读取对应的任务,推入主线程执行,不断循环,这个过程就叫事件循环。

总的来说,事件循环是 JS 中处理异步操作的机制,它负责协调和执行任务队列中的任务。事件循环分为宏任务(macrotask)和微任务(microtask)两种类型的任务。

宏任务和微任务

宏任务是一些需要在事件循环中进行处理的较大的任务单元,例如I/O操作、定时器回调等。它们被放置在事件循环的宏任务队列中,并按照先进先出的顺序执行。

宏任务有哪些

  • script
  • setTimeout
  • setInterval
  • setImmediate
  • Promise
  • I/O 操作的回调函数
  • UI render
  • 用户交互事件的回调函数

微任务是一些轻量级的任务单元,它们在宏任务执行结束后,但在浏览器渲染之前执行。它们被放置在事件循环的微任务队列中,并在宏任务执行结束后按照先进先出的顺序执行。

微任务有哪些

  • process.nextTick
  • Promise 的回调函数
  • async/await
  • MutationObserver(html5 新特性)

为什么要有宏任务和微任务的区分呢?主要原因有两点:

  • 执行顺序和优先级:宏任务和微任务的执行顺序不同。宏任务在事件循环中处于较低优先级,每次只执行一个宏任务,因此在宏任务之间有可能存在较长的延迟。而微任务在宏任务执行结束后立即执行,因此它们能够更快地响应和处理结果。通过将一些高优先级的任务作为微任务,可以更及时地处理和更新相关的状态。
  • 防止阻塞和提高响应性:由于宏任务的执行可能会较为耗时,如果所有任务都作为宏任务执行,可能会导致事件循环被长时间占用,造成其他任务无法及时得到处理,从而影响页面的响应性能。通过将某些短小且高优先级的任务作为微任务执行,可以保证这些任务能够在尽可能短的时间内得到处理,提高页面的响应性和流畅度。

总结起来,宏任务和微任务的区分主要是为了控制任务的执行顺序、优先级和响应性能。通过合理地利用微任务和宏任务,我们可以更好地处理异步任务,提高应用程序的性能和用户体验。

浏览器中的事件循环

基本流程:

  1. 执行同步任务:首先执行当前的同步代码,即按照顺序执行的 JavaScript 代码。

  2. 执行微任务队列:在执行完同步任务后,浏览器会检查微任务队列(microtask queue)中是否有微任务,如果有则按顺序执行所有的微任务。

  3. 执行宏任务队列:浏览器会从宏任务队列中选择一个任务执行。

每次只执行一个宏任务,执行完当前宏任务后,会检查是否需要执行 UI 渲染操作(重绘和回流),然后再执行下一个宏任务。

  1. 更新 UI 渲染:如果需要执行 UI 渲染操作,浏览器会根据 DOM 的变化进行重绘(repaint)和回流(reflow)。

  2. 检查是否有新的事件:在每个阶段结束后,浏览器会检查是否有新的事件被触发,例如用户的交互事件、定时器的到期等。

  3. 重复以上步骤:循环执行以上步骤,直到没有任何任务需要执行。

需要注意的是,事件循环中的微任务和宏任务的执行顺序是有区别的,微任务总是在宏任务之前执行。这意味着,即使宏任务队列中有很多任务等待执行,浏览器也会先处理完当前微任务队列中的所有任务,再执行下一个宏任务。

浏览器的事件循环机制使得 JavaScript 可以处理异步操作,保证了用户交互的响应性,并且可以实现高效的动画和渲染操作。了解浏览器事件循环的工作原理有助于编写高性能和响应式的 Web 应用程序。

node 中的事件循环

在 Node.js 中,事件循环是处理异步操作的核心机制。Node.js 的事件循环遵循事件驱动的模型,用于处理输入、执行回调函数和管理事件队列。

以下是 Node.js 的事件循环的基本流程:

  1. 执行同步任务:首先执行当前的同步代码,即在 JavaScript 代码中按照顺序执行的部分。

  2. 执行微任务队列:在执行同步任务之后,Node.js 会检查微任务队列(microtask queue)中是否有微任务(例如 Promise 的回调函数),如果有则按顺序执行所有的微任务。

  3. 执行事件循环阶段:Node.js 事件循环分为多个阶段,每个阶段都有对应的任务队列(如 timers 队列、I/O callbacks 队列等)。

  • timers 阶段:处理定时器任务,执行已经到期的定时器回调函数。
  • I/O callbacks 阶段:处理网络 I/O 相关的回调函数,如网络请求、文件 I/O 等。
  • idle, prepare 阶段:内部使用,一般不需要关注。
  • poll 阶段:等待新的 I/O 事件,处理 I/O 相关的回调函数。
  • check 阶段:执行 setImmediate() 的回调函数。
  • close callbacks 阶段:处理关闭事件的回调函数,如关闭数据库连接、服务器等。
  1. 检查是否有被触发的事件:在每个阶段结束后,Node.js 会检查是否有被触发的事件,如果有则立即执行对应事件的回调函数。

  2. 重复以上步骤:循环执行以上步骤,直到没有任何任务需要执行。

需要注意的是,在事件循环过程中,如果某个阶段的任务队列为空,Node.js 会等待,直到有任务被添加到队列中才会进入下一个阶段。

事件循环的机制使得 Node.js 可以高效处理大量的并发请求,同时能够响应用户的输入和事件。它提供了一种非阻塞、异步的编程模型,允许开发人员编写高效且可扩展的服务器端应用程序。

浏览器事件循环和 node 事件循环的区别

  1. 运行环境:浏览器事件循环运行在浏览器环境中,而 Node.js 的事件循环运行在服务器端的 Node.js 运行时环境中。
  2. 宏任务队列和微任务队列:浏览器事件循环有多个宏任务队列(如宏任务队列、消息队列、UI 渲染队列等),每个宏任务队列都有自己的微任务队列。而 Node.js 的事件循环只有一个宏任务队列和一个微任务队列
  3. 宏任务和微任务的执行时机:在浏览器事件循环中,每个宏任务执行完毕后,会立即执行该宏任务队列中的所有微任务。而在 Node.js 的事件循环中,每个宏任务执行完毕后,会执行完当前微任务队列中的所有微任务,然后才会执行下一个宏任务。
  4. I/O 操作的处理:浏览器事件循环通过浏览器提供的 API 来处理 I/O 操作,如定时器、网络请求等。而 Node.js 的事件循环通过 libuv 库来处理 I/O 操作,包括文件 I/O、网络 I/O 等。
  5. 事件触发机制:浏览器事件循环主要由用户交互事件、定时器和网络请求等触发。Node.js 的事件循环主要由 I/O 事件和定时器触发。

浏览器安全

xss 攻击

概念:xss 攻击指的是跨站脚本攻击,是一种代码注入攻击。攻击者通过在网站注入恶意脚本,使其在用户的浏览器上运行,从而盗取用户的信息,如 cookie 等。

本质:由于网站没有对恶意代码进行过滤,使其与正常的代码混合在一起了,但浏览器没有能力区分这些恶意代码,只能无条件执行。

特点

  • 通常难以从UI上感知(暗地执行脚本)
  • 窃取用户的敏感信息(cookie/token)
  • 绘制UI(比如弹窗),诱骗用户点击/填写表单

分类: xss 攻击可以分为反射型、存储型、DOM 型。

  • 反射型指的是攻击者诱导用户访问带有恶意代码的 url ,服务端接收数据后处理,将带有恶意代码的数据发送回浏览器,浏览器解析这段带有 xss 代码的数据后会将其当做脚本执行,以此完成 xss 攻击。
  • 存储型指的是恶意脚本会存储在服务器上,当浏览器请求数据时,这段恶意脚本会从服务器传回并执行。
  • DOM 型指的是攻击者修改网页的 DOM 节点而形成的 xss 攻击。

反射型 XSS 攻击:

攻击步骤

  1. 攻击者构造出特殊的 URL,其中包含恶意代码。
  2. 用户打开带有恶意代码的 URL 时,网站服务端将恶意代码从 URL 中取出,拼接在 HTML 中返回给浏览器。
  3. 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。
  4. 恶意代码执行攻击。

场景:网站搜索、跳转等。

防御:对url的查询参数进行转义后再输出到页面(encodeURIComponent(url))。

DOM 型 XSS 攻击:

攻击步骤

  1. 攻击者构造恶意代码。
  2. 浏览器执行恶意代码,将其插入 DOM 节点中并执行;
  3. 恶意代码执行攻击。

防御

  • 对于 url ,使用 encodeURIComponent 对输入内容进行转义
  • 对于非 url ,可使用如下函数编码:
    function encodeHtml(str) {
        return str.replace(/"/g, '"')
                .replace(/'/g, ''')
                .replace(/</g, '&lt;')
                .replace(/>/g, '&gt;');
    }

范畴:DOM 型 XSS 攻击中,取出和执行恶意代码由浏览器端完成,属于前端 JavaScript 自身的安全漏洞。

存储型 XSS 攻击:

攻击步骤

  1. 攻击者将恶意代码提交到目标网站的数据库中;
  2. 所有用户在打开目标网站时,网站服务端将恶意代码从数据库中取出,拼接在 HTML 中返回给浏览器;
  3. 浏览器接收到响应后解析执行,同时也执行了恶意代码;
  4. 恶意代码发动攻击。

防御

  • 转义
    1. 前端数据传递给服务器之前,先转义/过滤(防范不了抓包修改数据的情况)
    2. 服务器接收到数据,在存储到数据库之前,进行转义/过滤
    3. 前端接收到服务器传递过来的数据,在展示到页面前,先进行转义/过滤
  • 限制
    1. 在服务端使用 HTTP 的 Content-Security-Policy 头部来指定策略:Content-Security-Policy: default-src 'self';或者在前端设置 meta 标签:<meta http-equiv="Content-Security-Policy" content="form-action 'self';">

      参考:Content-Security-Policy

    2. 限制输入内容的长度
    3. 限制输入的内容
  • 其他
    1. HTTP-only Cookie: 禁止 JavaScript 读取某些敏感 Cookie,攻击者完成 XSS 注入后也无法窃取此Cookie。
    2. 验证码:防止脚本冒充用户提交危险操作。

使用场景:常见于带有用户保存数据的网站功能,如论坛发帖、商品评论、用户私信等。

反射型 xss 与存储型 xss 的区别:

  • 反射型作用范围小,仅局限于单体攻击对象;而存储型则是作用于操作到数据库的所有用户。
  • 反射型的恶意代码存在 url 里,而存储型的恶意代码存储在数据库里。

DOM xss 与前两种 xss 的区别:

  • DOM 型 xss 攻击中,取出和执行恶意代码都由浏览器完成,属于前端 JavaScript 自身的安全漏洞;而其他两种 xss 攻击都是属于服务端的安全漏洞。

csrf 攻击

概念:csrf 攻击指的是跨站请求伪造攻击,攻击者诱导用户进入一个第三方网站,然后该网站向被攻击网站发送跨站请求,如果用户在被攻击网站中保存了登录状态,攻击者就可以利用这个登录状态绕过后台的用户验证,伪装成用户执行操作。

攻击步骤

  1. 受害者登录A站点,并保留了登录凭证(Cookie)。
  2. 攻击者诱导受害者访问了站点B。
  3. 站点B向站点A发送了一个请求,浏览器会默认携带站点A的Cookie信息。
  4. 站点A接收到请求后,对请求进行验证,并确认是受害者的凭证,误以为是无辜的受害者发送的请求。
  5. 站点A以受害者的名义执行了站点B的请求。
  6. 攻击完成,攻击者在受害者不知情的情况下,冒充受害者完成了攻击。

特点

  1. 攻击通常在第三方网站发起
  2. 攻击利用受害者在被攻击网站的登录凭证,冒充受害者提交操作;并不会去获取cookie信息(cookie有同源策略)
  3. 跨站请求可以用各种方式:图片URL、超链接、CORS、Form提交等等(来源不明的链接,不要点击)

常见的 csrf 攻击有三种

  • GET 类型的 csrf 攻击:比如在网站中的一个 img 标签里构建一个请求,在用户打开这个网页时就会自动提交。
  • POST 类型的 csrf 攻击:比如构建一个表单,然后隐藏它,在用户进入页面时,就会自动提交这个表单。
  • 链接类型的 csrf 攻击:比如在 a 标签的 href 属性里构建一个请求,然后诱导用户去点击。

防御

  • 添加验证码
    • 体验不好
  • 检测 Referer 判断请求的来源
    • 不安全,Referer 可以被更改
  • 使用加密 Token
    • 服务端给用户生成一个token,加密后传递给用户
    • 用户在提交请求时,需要携带这个token
    • 服务端验证token是否正确
  • Cookie 添加 Samesite 属性
    • 部署简单,并能有效防御CSRF攻击,但是存在兼容性问题。

为了更好的防御CSRF攻击,我们可以组合使用以上防御手段。

什么是网络劫持

网络劫持分为两种:

(1)DNS 劫持:(输入京东被强制跳转到淘宝就属于 DNS 劫持)

  • DNS 强制解析:通过修改运营商的本地 DNS 记录,来引导用户流量到缓存服务器
  • 302跳转的方式:通过监控网络出口的流量,分析判断哪些内容是可以进行劫持处理的

(2)HTTP 劫持:(访问⾕歌但是⼀直有贪玩蓝月的⼴告),由于http明⽂传输,运营商会修改你的http响应内容(即加⼴告)

DNS劫持由于涉嫌违法,已经被监管起来,现在很少会有DNS劫持,⽽http劫持依然⾮常盛⾏,最有效的办法就是全站HTTPS,将HTTP加密,这使得运营商⽆法获取明⽂,就⽆法劫持你的响应内容。

跨域

跨域问题(Cross-Origin Resource Sharing,CORS)是由浏览器的同源策略引起的。同源策略是一种安全机制,限制了一个源(域名、协议和端口)下的文档或脚本如何与不同源的资源进行交互。

同源策略要求,只有当两个页面具有相同的协议、域名和端口号时,它们才被认为是同源的。如果两个页面的任意一个要访问另一个页面的资源,而它们不满足同源策略的要求,就会发生跨域问题。

跨域问题出现的主要原因是为了保护用户的隐私和安全。如果浏览器没有跨域限制,恶意网站就可以通过脚本访问其他网站上的用户数据,导致安全风险。

注意:跨域并不是请求发不出去,服务端也能收到请求并正常返回结果,只是结果被浏览器拦截了。

解决跨域的九种方法

JSONP

原理:利用 <script> 标签没有跨域限制的漏洞,网页可以得到从其他来源动态产生的 JSON 数据, JSONP 请求一定需要对方的服务器做支持才可以

优缺点:JSONP 简单且兼容性好,可用于解决主流浏览器的跨域数据访问的问题;但仅支持 get 方法,因为 script 脚本的请求方式是 GET。

实现流程:

  • 创建一个 <script> 并载入页面中,src 是跨域的 api 接口地址,但后面需要带上一个标记有回调函数的请求参数,如 http://10.92.191.223:3000/test/?callback=handleCallback。后端接受到请求后需要进行特殊的处理,将回调函数名和数据拼接成一个函数调用的形式返回给前端,如 handleCallback({"status": "success", "message": "跨域成功"})。因为是 script 脚本,所以前端请求到这个脚本后会立即执行这个脚本内容,即调用这个回调函数。
  • 声明一个回调函数,其函数名(如 show )当做参数值,
  • 创建一个 <script> 标签,把要跨域的 api 数据接口地址赋值给 script 的 src 属性,同时还要在这个地址中向服务器传递该函数名(如 ?callback=show
  • 将创建的回调函数传递给跨域请求数据的服务器
  • 服务器收到请求后进行处理:把传递进来的函数名和要响应回去的数据拼接成一个字符串
  • 服务器把准备的数据通通过 HTTP 协议返回给客户端,客户端再调用执行之前声明的回调函数,对返回的数据进行操作

代码实现:

function jsonp(url, callback) {
  let script = document.createElement('script');
  let fn = Symbol();
  window[fn] = function(response) {
    try{
      callback(response);
    } finally {
      delete window[fn];
      document.body.removeChild(script);
    }
  }
  script.type = 'text/javascript';
  if (url.indexOf('?') === -1) {
    url += `?callback=${fn}`;
  } else {
    url += `callback=${fn}`;
  }
  script.src = url;
  document.body.appendChild(script);
}

CORS

概念:CORS 即跨域资源共享,当一个资源从与该资源本身所在的服务器不同的域、协议或端口请求一个资源时,会发起一个跨域 HTTP 请求,它使用额外的 HTTP 头来告诉浏览器,让运行在一个 origin 上的 web 应用被准许访问来自不同源服务器上的指定的资源。

类别:CORS 分为 简单请求非简单请求

满足以下两个条件,就可以看做是简单请求:

  1. 请求方法是以下三种方法之一:
  • GET
  • POST
  • HEAD
  1. HTTP 的头信息不超出以下几种字段:
  • Accept
  • Accept-Language
  • Content-Language
  • Content-Type:只限于三个值application/x-www-form-urlencodedmultipart/form-datatext/plain

若不满足以上条件,就属于非简单请求了。

简单请求:

基本流程:浏览器直接发出 CORS 请求,会自动在头信息中增加一个 Origin 字段,表明本次请求来自哪个源(协议+域名+端口),浏览器根据这个字段的值来决定是否同意这次请求。

结果

  1. 如果 Origin 指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段:
  • Access-Control-Allow-Origin:必须。它的值要么是请求时 Origin 字段的值,要么是一个 * ,表示接受任意域名的请求。
  • Access-Control-Allow-Credentials:可选。它的值是一个布尔值,表示是否允许发送 Cookie 。默认值为 false ;若设为 true ,即表示服务器明确许可 Cookie 可以包含在请求中。

    注意

    1. 如果服务器将该字段设为 true 的话,开发者还需要在 AJAX 请求中打开 withCredentials 属性:(否则即使服务器同意发送Cookie,浏览器也不会发送。或者,服务器要求设置Cookie,浏览器也不会处理。)
      var xhr = new XMLHttpRequest();
      xhr.withCredentials = true;
    2. 如果要发送 cookie ,Access-Control-Allow-Origin 就不能设为 * ,必须指定明确的、与请求网页一致的域名。同时,cookie 依然遵循同源政策,且(跨源)原网页代码中的 document.cookie 也无法读取服务器域名下的的 cookie 。
    3. 如果省略 withCredentials 设置,有的浏览器还是会一起发送 cookie ,这时可以显式关闭 withCredentialsxhr.withCredentials = false
  • Access-Control-Expose-Headers:可选。CORS 请求时,XMLHttpRequest 对象的 getResponseHeader() 方法只能拿到 6 个基本字段:Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma 。如果想拿到其他字段,就必须在 Access-Control-Expose-Headers 里面指定。
  • Content-Type:响应给浏览器的资源的类型。
  1. 如果 Origin 指定的域名不在许可范围内,服务器会返回一个正常的 HTTP 回应,浏览器发现回应里没有 Access-Control-Allow-Origin 字段,就知道出错了,从而抛出一个错误。

非简单请求:

非简单请求是指那种对服务器有特殊要求的请求,比如请求方式是 PUTDELETE 等,或者 Content-Type 字段的类型是 application/json 等。

基本流程:非简单请求的 CORS 请求,会在正式通信之前,增加一次 HTTP 查询请求,也叫预检请求。过程是这样的:浏览器先询问服务器,当前网页的域名是否在服务器的许可名单之中,以及可以使用哪些 HTTP 请求方式和头信息字段,只有得到肯定答复,浏览器才会发出正式的 HTTP 请求,否则就报错。

预检请求

  • 请求方法是 OPTIONS ,表示这个请求是用来询问的。
  • 请求字段:
    • Origin:表示请求来自哪个源。
    • Access-Control-Request-Mothod:必须。列出浏览器的 CORS 请求所有会用到的 HTTP 方法。
    • Access-Control-Request-Headers:该字段是一个逗号分隔的字符串,指定浏览器 CORS 请求会额外发送的头信息字段。
  • 判断是否同意请求:服务器收到浏览器的预检请求,检查了头信息的三个字段后,确认允许跨源请求,就可以做出回应。如果返回的头信息中有 Access-Control-Allow-Origin 这个字段就代表允许跨域请求;如果没有,就代表不同意这个预检请求,就会报错。
  • 同意请求要回应的字段:
    • Access-Control-Allow-Origin: 允许跨域的源地址。只要服务器通过了预检请求,在以后每次的 CORS 请求都会自带一个 Origin 头信息字段,服务器的回应,也都会有一个 Access-Control-Allow-Origin 头信息字段
    • Access-Control-Allow-Methods: 必需。它的值是逗号分隔的一个字符串,表明服务器支持的所有跨域请求的方法。这是为了避免多次”预检”请求。
    • Access-Control-Allow-Headers: 如果浏览器请求包括 Access-Control-Request-Headers 字段,则该字段是必需的。服务器支持的所有头信息字段。
    • Access-Control-Allow-Credentials: 表示是否允许发送 Cookie 。
    • Access-Control-Max-Age: 可选。用来指定本次预检请求的有效期,单位为秒。
  • 否定请求回应:
    • 返回一个正常的 HTTP 回应,但是没有任何 CORS 相关的头信息字段。这时,浏览器就会认定服务器不同意预检请求。

正式请求:其实就是简单请求的流程。

所以,非简单请求 = 预检请求 + 简单请求 。

nginx 反向代理

原理:本质和 CORS 跨域原理一样,通过配置文件设置请求响应头如 Access-Control-Allow-Origin… 等字段实现跨域。

实现:通过 Nginx 配置一个代理服务器(域名与跨域页面的域名相同,端口不同)做跳板机,反向代理访问被跨域页面的域名接口,并且可以顺便修改 cookie 中 domain 信息,方便当前域c ookie 写入,实现跨域访问。

nginx具体配置

server {
    listen       81;
    server_name  www.domain1.com;
    location / {
        proxy_pass   http://www.domain2.com:8080;  #反向代理
        proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名
        index  index.html index.htm;
        // 当用webpack-dev-server等中间件代理接口访问nignx时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用
        add_header Access-Control-Allow-Origin http://www.domain1.com;  // 当前端只跨域不带cookie时,可为*
        add_header Access-Control-Allow-Credentials true;
    }
}

node 中间件

websocket

document.domain + iframe

原理:两个页面都通过 js 强制设置 document.domain 为基础主域,就实现了跨域。

实现

  1. 父窗口:https://domain.com/a.html
<iframe id="iframe" src="http://child.domain.com/b.html"></iframe>
<script>
    document.domain = 'domain.com';
    var user = 'admin';
</script>
  1. 子窗口:https://child.domain.com/b.html
<script>
    document.domain = 'domain.com';
    // 获取父窗口中变量
    console.log('get js data from parent ---> ' + window.parent.user);
</script>

location.hash + iframe跨域

实现原理:a 想要与 b 跨域相互通信,需通过中间页 c 来实现。 三个页面,不同域之间利用 iframe 的 location.hash 传值,相同域之间直接 js 访问来通信。

浏览器事件机制

什么是事件?什么是事件模型?

事件是用户操作网页时发生的交互动作,比如 click/move ,事件除了用户触发的动作外,还可以是文档加载、窗口滚动和大小调整。事件被封装成一个 event 对象,包含了该事件发生时的所有相关信息以及可以对事件进行的操作。

现代浏览器一共有三种事件模型:

  • DOM0 级事件模型,这种模型不会传播,所以没有事件流的概念,但现在有的浏览器支持以冒泡的方式实现,它可以在网页中直接定义监听函数,也可以通过 js 属性来指定监听函数。直接在 DOM 对象上注册事件名称,就是 DOM0 写法。
  • IE 事件模型,该事件共有两个过程,分别是事件处理阶段和事件冒泡阶段。事件处理阶段会首先执行目标元素绑定的监听事件,然后执行事件冒泡阶段。
  • DOM2 级事件模型,该事件共有三个过程,分别是事件捕获阶段、事件处理阶段、事件冒泡阶段。

对事件委托的理解

事件委托本质上是利用了 浏览器事件冒泡 的机制。因为事件在冒泡过程中会上传到父节点,父节点可以通过事件对象获取到目标节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件,这种方式称为事件委托(事件代理)。

通过事件委托,我们可以将事件处理程序集中绑定在父元素上,而不必为每个子元素都绑定事件处理程序。这样做的好处包括:

  • 减少内存消耗:不需要为每个子元素都绑定事件处理程序,减少了内存占用。
  • 动态元素支持:对于后续添加到父元素中的子元素,无需手动绑定事件,它们会自动继承父元素的事件处理程序。
  • 简化代码:通过将事件处理程序绑定到父元素上,可以减少重复的代码。

进程和线程

进程的概念:进程描述了 CPU 在运行指令及加载和保存上下文所需的时间,放在应用上来说就代表了一个程序。

线程的概念:线程是进程中的更小单位,描述了执行一段指令所需的时间。

进程和线程的区别

  • 进程可以看做是独立应用,但线程不行
  • 资源:进程是 CPU 资源分配

Chrome 打开一个页面需要启动多少进程?分别有哪些进程

浏览器从关闭状态进行启动,然后新开 1 个页面至少需要 1 个网络进程、1 个浏览器进程、1 个 GPU 进程以及 1 个渲染进程,共 4 个进程;后续再新开标签页,浏览器、网络进程、GPU进程是共享的,不会重新启动,如果2个页面属于同一站点的话,并且从a页面中打开的b页面,那么他们也会共用一个渲染进程,否则新开一个渲染进程。

  • 浏览器进程:主要负责界面显示、用户交互、子进程管理,同时提供存储等功能。
  • 渲染进程:核心任务是将 HTML、CSS 和 JS 转换为用户可以与之交互的网页。默认情况下,Chrome 会为每个 Tab 标签创建一个渲染进程。出于安全考虑,渲染进程都是运行在沙箱模式下。
  • GPU 进程:实现 3D CSS 的效果。
  • 网络进程:主要负责页面的网络资源加载。之前是作为一个模块运行在浏览器进程里的,后来被独立出来,成为一个单独的进程。
  • 插件进程:主要负责插件的运行。因为插件易崩溃,所以需要通过插件进程来隔离,以保证插件的崩溃不会对浏览器和页面造成影响。

CSS加载会阻塞页面展示吗

。在浏览器渲染页面的过程中,遇到外部CSS文件的加载会触发阻塞行为,即浏览器会等待CSS文件完全加载和解析后再进行页面的渲染。这是因为CSS文件对于页面的样式和布局起到重要的作用,浏览器需要先获取CSS文件并解析其中的样式规则,然后根据这些规则来确定元素的样式和布局信息,最终进行页面的渲染。

当浏览器遇到外部CSS文件时,会执行以下操作:

  1. 发送网络请求:浏览器会发送网络请求去获取CSS文件。
  2. 下载CSS文件:浏览器会等待CSS文件完全下载完成。
  3. 解析CSS文件:浏览器会解析CSS文件中的样式规则。
  4. 构建渲染树:浏览器根据DOM树和解析后的CSS样式规则构建渲染树。
  5. 布局和绘制:浏览器根据渲染树进行布局和绘制,最终展示页面。

因此,如果CSS文件较大或网络连接较慢,CSS加载会耗费一定的时间,导致页面展示的延迟。这也是为什么在优化页面加载性能时,通常会采取一些策略,如使用内联CSS、压缩和合并CSS文件、使用浏览器缓存等来加快CSS加载速度,以减少页面展示的延迟。


文章作者: hcyety
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 hcyety !
评论
  目录