前端面试题系列文章:
- 【1】备战前端实习面试之HTML篇
- 【2】备战前端实习面试之CSS篇
- 【3】备战前端实习面试之JavaScript篇
- 【4】备战前端实习面试之React篇
- 【5】备战前端实习面试之Vue篇
- 【6】备战前端实习面试之Node.js篇
- 【7】备战前端实习面试之浏览器篇
- 【8】备战前端实习面试之性能优化篇
- 【9】备战前端实习面试之计算机网络篇
- 【10】备战前端实习面试之手写代码篇
- 【11】备战前端实习面试之代码输出结果篇
浏览器
对浏览器内核的理解
主要分成两部分:渲染引擎和 JS 引擎。
- 渲染引擎:负责取得网页的内容(HTML、XML、图像等等)、整理讯息(例如加入 CSS 等),以及计算网页的显示方式,然后渲染到用户的屏幕上。
- JS 引擎:解析和执行 javascript 来实现逻辑和控制 DOM 进行交互。
最开始渲染引擎和 JS 引擎并没有区分的很明确,后来 JS 引擎越来越独立,内核就倾向于只指渲染引擎。
GET 和 POST 的区别
GET
和 POST
是HTTP协议中的两种常用的请求方法,它们在以下几个方面有所区别:
- 参数传递位置:
GET请求的参数通常以查询字符串的形式附加在URL的末尾,例如:http://example.com/path?param1=value1¶m2=value2
。参数可见,可以被书签保存和缓存,长度受URL长度限制。
POST请求的参数通常包含在请求体中,不会显示在URL中,因此对于传递敏感信息更为安全。参数长度理论上没有限制,但实际上会受到服务器和浏览器的限制。
- 安全性:
GET
请求是幂等且安全的,即对服务器资源的请求不会产生副作用,不会修改、删除或创建资源。POST
请求不是幂等的,可能会对服务器资源产生副作用,例如创建新的资源或更新已有资源。
- 缓存:
GET
请求可以被缓存,因为相同的URL和参数会产生相同的结果。可以利用浏览器缓存来提高性能。POST
请求默认不会被缓存,每次发送都会向服务器请求最新数据。
- 可见性:
GET
请求的参数和URL可见,可以被用户或其他人看到。因此,不应将敏感信息放在URL中。POST
请求的参数不会显示在URL中,对于传递敏感信息更为安全。
- 用途:
GET
请求用于获取数据,一般用于数据的读取。POST
请求用于提交数据,一般用于数据的创建、更新或删除。
需要根据具体的需求和场景来选择使用 GET
或 POST
方法。一般来说,如果是获取数据或不对服务器产生副作用的操作,可以使用 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 新特性)
为什么要有宏任务和微任务的区分呢?主要原因有两点:
- 执行顺序和优先级:宏任务和微任务的执行顺序不同。宏任务在事件循环中处于较低优先级,每次只执行一个宏任务,因此在宏任务之间有可能存在较长的延迟。而微任务在宏任务执行结束后立即执行,因此它们能够更快地响应和处理结果。通过将一些高优先级的任务作为微任务,可以更及时地处理和更新相关的状态。
- 防止阻塞和提高响应性:由于宏任务的执行可能会较为耗时,如果所有任务都作为宏任务执行,可能会导致事件循环被长时间占用,造成其他任务无法及时得到处理,从而影响页面的响应性能。通过将某些短小且高优先级的任务作为微任务执行,可以保证这些任务能够在尽可能短的时间内得到处理,提高页面的响应性和流畅度。
总结起来,宏任务和微任务的区分主要是为了控制任务的执行顺序、优先级和响应性能。通过合理地利用微任务和宏任务,我们可以更好地处理异步任务,提高应用程序的性能和用户体验。
浏览器中的事件循环
基本流程:
执行同步任务:首先执行当前的同步代码,即按照顺序执行的 JavaScript 代码。
执行微任务队列:在执行完同步任务后,浏览器会检查微任务队列(microtask queue)中是否有微任务,如果有则按顺序执行所有的微任务。
执行宏任务队列:浏览器会从宏任务队列中选择一个任务执行。
每次只执行一个宏任务,执行完当前宏任务后,会检查是否需要执行 UI 渲染操作(重绘和回流),然后再执行下一个宏任务。
更新 UI 渲染:如果需要执行 UI 渲染操作,浏览器会根据 DOM 的变化进行重绘(repaint)和回流(reflow)。
检查是否有新的事件:在每个阶段结束后,浏览器会检查是否有新的事件被触发,例如用户的交互事件、定时器的到期等。
重复以上步骤:循环执行以上步骤,直到没有任何任务需要执行。
需要注意的是,事件循环中的微任务和宏任务的执行顺序是有区别的,微任务总是在宏任务之前执行。这意味着,即使宏任务队列中有很多任务等待执行,浏览器也会先处理完当前微任务队列中的所有任务,再执行下一个宏任务。
浏览器的事件循环机制使得 JavaScript 可以处理异步操作,保证了用户交互的响应性,并且可以实现高效的动画和渲染操作。了解浏览器事件循环的工作原理有助于编写高性能和响应式的 Web 应用程序。
node 中的事件循环
在 Node.js 中,事件循环是处理异步操作的核心机制。Node.js 的事件循环遵循事件驱动的模型,用于处理输入、执行回调函数和管理事件队列。
以下是 Node.js 的事件循环的基本流程:
执行同步任务:首先执行当前的同步代码,即在 JavaScript 代码中按照顺序执行的部分。
执行微任务队列:在执行同步任务之后,Node.js 会检查微任务队列(microtask queue)中是否有微任务(例如 Promise 的回调函数),如果有则按顺序执行所有的微任务。
执行事件循环阶段: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 阶段:处理关闭事件的回调函数,如关闭数据库连接、服务器等。
检查是否有被触发的事件:在每个阶段结束后,Node.js 会检查是否有被触发的事件,如果有则立即执行对应事件的回调函数。
重复以上步骤:循环执行以上步骤,直到没有任何任务需要执行。
需要注意的是,在事件循环过程中,如果某个阶段的任务队列为空,Node.js 会等待,直到有任务被添加到队列中才会进入下一个阶段。
事件循环的机制使得 Node.js 可以高效处理大量的并发请求,同时能够响应用户的输入和事件。它提供了一种非阻塞、异步的编程模型,允许开发人员编写高效且可扩展的服务器端应用程序。
浏览器事件循环和 node 事件循环的区别
- 运行环境:浏览器事件循环运行在浏览器环境中,而 Node.js 的事件循环运行在服务器端的 Node.js 运行时环境中。
- 宏任务队列和微任务队列:浏览器事件循环有多个宏任务队列(如宏任务队列、消息队列、UI 渲染队列等),每个宏任务队列都有自己的微任务队列。而 Node.js 的事件循环只有一个宏任务队列和一个微任务队列
- 宏任务和微任务的执行时机:在浏览器事件循环中,每个宏任务执行完毕后,会立即执行该宏任务队列中的所有微任务。而在 Node.js 的事件循环中,每个宏任务执行完毕后,会执行完当前微任务队列中的所有微任务,然后才会执行下一个宏任务。
- I/O 操作的处理:浏览器事件循环通过浏览器提供的 API 来处理 I/O 操作,如定时器、网络请求等。而 Node.js 的事件循环通过 libuv 库来处理 I/O 操作,包括文件 I/O、网络 I/O 等。
- 事件触发机制:浏览器事件循环主要由用户交互事件、定时器和网络请求等触发。Node.js 的事件循环主要由 I/O 事件和定时器触发。
浏览器安全
xss 攻击
概念:xss 攻击指的是跨站脚本攻击,是一种代码注入攻击。攻击者通过在网站注入恶意脚本,使其在用户的浏览器上运行,从而盗取用户的信息,如 cookie 等。
本质:由于网站没有对恶意代码进行过滤,使其与正常的代码混合在一起了,但浏览器没有能力区分这些恶意代码,只能无条件执行。
特点:
- 通常难以从UI上感知(暗地执行脚本)
- 窃取用户的敏感信息(cookie/token)
- 绘制UI(比如弹窗),诱骗用户点击/填写表单
分类: xss 攻击可以分为反射型、存储型、DOM 型。
- 反射型指的是攻击者诱导用户访问带有恶意代码的 url ,服务端接收数据后处理,将带有恶意代码的数据发送回浏览器,浏览器解析这段带有 xss 代码的数据后会将其当做脚本执行,以此完成 xss 攻击。
- 存储型指的是恶意脚本会存储在服务器上,当浏览器请求数据时,这段恶意脚本会从服务器传回并执行。
- DOM 型指的是攻击者修改网页的 DOM 节点而形成的 xss 攻击。
反射型 XSS 攻击:
攻击步骤:
- 攻击者构造出特殊的 URL,其中包含恶意代码。
- 用户打开带有恶意代码的 URL 时,网站服务端将恶意代码从 URL 中取出,拼接在 HTML 中返回给浏览器。
- 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。
- 恶意代码执行攻击。
场景:网站搜索、跳转等。
防御:对url的查询参数进行转义后再输出到页面(encodeURIComponent(url)
)。
DOM 型 XSS 攻击:
攻击步骤:
- 攻击者构造恶意代码。
- 浏览器执行恶意代码,将其插入 DOM 节点中并执行;
- 恶意代码执行攻击。
防御:
- 对于
url
,使用encodeURIComponent
对输入内容进行转义 - 对于非
url
,可使用如下函数编码:function encodeHtml(str) { return str.replace(/"/g, '"') .replace(/'/g, ''') .replace(/</g, '<') .replace(/>/g, '>'); }
范畴:DOM 型 XSS 攻击中,取出和执行恶意代码由浏览器端完成,属于前端 JavaScript 自身的安全漏洞。
存储型 XSS 攻击:
攻击步骤:
- 攻击者将恶意代码提交到目标网站的数据库中;
- 所有用户在打开目标网站时,网站服务端将恶意代码从数据库中取出,拼接在 HTML 中返回给浏览器;
- 浏览器接收到响应后解析执行,同时也执行了恶意代码;
- 恶意代码发动攻击。
防御:
- 转义
- 前端数据传递给服务器之前,先转义/过滤(防范不了抓包修改数据的情况)
- 服务器接收到数据,在存储到数据库之前,进行转义/过滤
- 前端接收到服务器传递过来的数据,在展示到页面前,先进行转义/过滤
- 限制
- 在服务端使用 HTTP 的
Content-Security-Policy
头部来指定策略:Content-Security-Policy: default-src 'self'
;或者在前端设置meta
标签:<meta http-equiv="Content-Security-Policy" content="form-action 'self';">
。 - 限制输入内容的长度
- 限制输入的内容
- 在服务端使用 HTTP 的
- 其他
HTTP-only Cookie
: 禁止 JavaScript 读取某些敏感 Cookie,攻击者完成 XSS 注入后也无法窃取此Cookie。- 验证码:防止脚本冒充用户提交危险操作。
使用场景:常见于带有用户保存数据的网站功能,如论坛发帖、商品评论、用户私信等。
反射型 xss 与存储型 xss 的区别:
- 反射型作用范围小,仅局限于单体攻击对象;而存储型则是作用于操作到数据库的所有用户。
- 反射型的恶意代码存在 url 里,而存储型的恶意代码存储在数据库里。
DOM xss 与前两种 xss 的区别:
- DOM 型 xss 攻击中,取出和执行恶意代码都由浏览器完成,属于前端 JavaScript 自身的安全漏洞;而其他两种 xss 攻击都是属于服务端的安全漏洞。
csrf 攻击
概念:csrf 攻击指的是跨站请求伪造攻击,攻击者诱导用户进入一个第三方网站,然后该网站向被攻击网站发送跨站请求,如果用户在被攻击网站中保存了登录状态,攻击者就可以利用这个登录状态绕过后台的用户验证,伪装成用户执行操作。
攻击步骤:
- 受害者登录A站点,并保留了登录凭证(Cookie)。
- 攻击者诱导受害者访问了站点B。
- 站点B向站点A发送了一个请求,浏览器会默认携带站点A的Cookie信息。
- 站点A接收到请求后,对请求进行验证,并确认是受害者的凭证,误以为是无辜的受害者发送的请求。
- 站点A以受害者的名义执行了站点B的请求。
- 攻击完成,攻击者在受害者不知情的情况下,冒充受害者完成了攻击。
特点:
- 攻击通常在第三方网站发起
- 攻击利用受害者在被攻击网站的登录凭证,冒充受害者提交操作;并不会去获取cookie信息(cookie有同源策略)
- 跨站请求可以用各种方式:图片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 分为 简单请求 和 非简单请求
满足以下两个条件,就可以看做是简单请求:
- 请求方法是以下三种方法之一:
- GET
- POST
- HEAD
- HTTP 的头信息不超出以下几种字段:
- Accept
- Accept-Language
- Content-Language
- Content-Type:只限于三个值
application/x-www-form-urlencoded
、multipart/form-data
、text/plain
若不满足以上条件,就属于非简单请求了。
简单请求:
基本流程:浏览器直接发出 CORS 请求,会自动在头信息中增加一个 Origin
字段,表明本次请求来自哪个源(协议+域名+端口),浏览器根据这个字段的值来决定是否同意这次请求。
结果:
- 如果 Origin 指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段:
Access-Control-Allow-Origin
:必须。它的值要么是请求时 Origin 字段的值,要么是一个*
,表示接受任意域名的请求。Access-Control-Allow-Credentials
:可选。它的值是一个布尔值,表示是否允许发送 Cookie 。默认值为false
;若设为true
,即表示服务器明确许可 Cookie 可以包含在请求中。注意:
- 如果服务器将该字段设为
true
的话,开发者还需要在 AJAX 请求中打开withCredentials
属性:(否则即使服务器同意发送Cookie,浏览器也不会发送。或者,服务器要求设置Cookie,浏览器也不会处理。)var xhr = new XMLHttpRequest(); xhr.withCredentials = true;
- 如果要发送 cookie ,
Access-Control-Allow-Origin
就不能设为*
,必须指定明确的、与请求网页一致的域名。同时,cookie 依然遵循同源政策,且(跨源)原网页代码中的document.cookie
也无法读取服务器域名下的的 cookie 。 - 如果省略
withCredentials
设置,有的浏览器还是会一起发送 cookie ,这时可以显式关闭withCredentials
:xhr.withCredentials = false
。
- 如果服务器将该字段设为
Access-Control-Expose-Headers
:可选。CORS 请求时,XMLHttpRequest
对象的getResponseHeader()
方法只能拿到 6 个基本字段:Cache-Control
、Content-Language
、Content-Type
、Expires
、Last-Modified
、Pragma
。如果想拿到其他字段,就必须在Access-Control-Expose-Headers
里面指定。Content-Type
:响应给浏览器的资源的类型。
- 如果 Origin 指定的域名不在许可范围内,服务器会返回一个正常的 HTTP 回应,浏览器发现回应里没有
Access-Control-Allow-Origin
字段,就知道出错了,从而抛出一个错误。
非简单请求:
非简单请求是指那种对服务器有特殊要求的请求,比如请求方式是 PUT
或 DELETE
等,或者 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
为基础主域,就实现了跨域。
实现:
- 父窗口:
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>
- 子窗口:
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文件时,会执行以下操作:
- 发送网络请求:浏览器会发送网络请求去获取CSS文件。
- 下载CSS文件:浏览器会等待CSS文件完全下载完成。
- 解析CSS文件:浏览器会解析CSS文件中的样式规则。
- 构建渲染树:浏览器根据DOM树和解析后的CSS样式规则构建渲染树。
- 布局和绘制:浏览器根据渲染树进行布局和绘制,最终展示页面。
因此,如果CSS文件较大或网络连接较慢,CSS加载会耗费一定的时间,导致页面展示的延迟。这也是为什么在优化页面加载性能时,通常会采取一些策略,如使用内联CSS、压缩和合并CSS文件、使用浏览器缓存等来加快CSS加载速度,以减少页面展示的延迟。