Skip to content

http 面试相关

HTTPS 的特点

HTTP 状态码 304 是多好还是少好

HTTP 协议的优点和缺点

HTTP 1.0 和 HTTP 11 之间有哪些区别?

TCP 的重传机制

对 keep-alive 的理解

说说网络模型

说一下 HTTP 3.0

TCP 的可靠传输机制

GET 和 POST 的请求的区别

数字证书是什么?

什么是 HTTPS 协议?

HTTP 1.1 和 HTTP 2.0 的区别

URL 有哪些组成部分

对 WebSocket 的理解

GET 方法 URL 长度限制的原因

DNS 完整的查询过程

HTTP2 的头部压缩算法是怎样的?

HTTP 和 HTTPS 协议的区别

即时通讯的实现: 短轮询、长轮询、SSE 和 WebSocket 间的区别?

TCP 的拥塞控制机制

对于这个问题,我喜欢这样回答

对于前面优化我们可以从这个方面去寻找优化的可能性与方案。如在我们访问一个页面的时候是下面这几个过程

输入 URL -> 判断是否存在当前 url 的缓存 -> 对域名进行 DNS 解析 -> 发送 TCP 链接 -> 发送当前页面地址的请求 -> 服务器返回数据进行响应

对于上面的流程我们可以对于

  1. 判断是否存在当前 url 的缓存

通过 nginx 等在服务器端对静态资源数据进行缓存配置

#缓存相应的文件(静态文件)
location ~ \.(gif|jpg|png|htm|html|css|js|flv|ico|swf)(.*) {
    proxy_pass http://cluster;         #如果没有缓存则通过proxy_pass转向请求
    proxy_redirect off;
    proxy_set_header Host $host;
    proxy_cache cache_one;
    proxy_cache_valid 200 302 1h;                              #对不同的HTTP状态码设置不同的缓存时间,h小时,d天数
    proxy_cache_valid 301 1d;
    proxy_cache_valid any 1m;
    expires 30d;                                         # 配置强缓存
}
  1. 对域名进行 DNS 解析

一般我们在优化的时候主要关注的是首页,所以这个没法优化,但是我们对于其他页面可以进行着一方面的优化

优化方法:检查页面是否添加了 DNS 预解析代码。

html
<link rel="dns-prefetch" href="//haitao.nosdn1.127.net" />

在移动端还可以使用 httpDNS(使用第三方 DNS 服务器直接获取当前域名对于的 IP 地址)、 域名收敛等手段优化 DNS 查询时间

  1. 发送 TCP 链接 -> 发送当前页面地址的请求 -> 服务器返回数据进行响应

下面就是建立 TCP 连接和请求获取对于的数据的过程,对于这个过程的优化就较多了

  • HTTP1.1 的长连接
Connection: Keep-alive
  • 长轮询、短轮询、websocket 的选择

  • HSTS

  • 对于我们一个页面访问数据的时候,浏览器会对同一个域名的连接请求最大并行数进行限制(一般为 6),那我们一方面就需要减少 http 的请求数

    • 对于 JS 或者 css 文件我们可以通过 webpack 打包构建工具配置 splitChunks 去将一些通过的脚本函数打包到一个或者多个 chunks 中去,这样我们就可以不要在页面中去加载什么 cookie、date、axios 等第三方工具类
    • 对于图片等资源特别是图标这些小图片我们最好使用雪碧图将这些小图片合并成一张图片,然后通过 background-position 去处理。当然这是我们常用的一些方式,对于那些使用频率很小,或者可能就只在一个页面中用到的小图片,那么我们使用这种方式反而适得其反,这时候建议使用内联的方式,通过 webpack 将小图片转换成 base64 格式
    • 对于接口数据,我们也是尽量在首页等重要页面进行接口定制,将多个接口合并成一个接口。减少接口的请求次数
  • 通过将不同的资源存放到不同的域名服务器下,突破浏览器连接请求最大并行数进行限制(域名发散),同时这个也有一个好处,也是我们对于 cookie 的优化

HTTP 是无状态的,那么对于我们网络用户登录等需要用户状态的操作怎么解决 HTTP 无状态的问题。这时候就可以通过 cookies,因为 cookies 信息会随请求在客户端和服务器之间传输。但是对于那些静态资源文件,其不需要获取用户的 cookie 信息,那么通过域名发散的方法,也可以减少静态资源请求的带宽数据

  • 我们还可以压缩合并 js 和 css 数据

    • 通过 webpack 中的 minimizer 将我们的 js 和 css 进行混合压缩等,可以减少 js 和 css 的体积

    • 在 nginx 中开启 gzip(移除图片的 gzip 压缩)

#压缩配置#
gzip  on;           #打开gzip压缩功能
gzip_min_length 1k; #压缩阈值
gzip_buffers 4 16k; #buffer 不用修改
gzip_comp_level 2;  #压缩级别:1-10,数字越大压缩的越好,时间也越长
gzip_types text/plain application/x-javascript text/css application/xml text/javascript application/x-httpd-php  #压缩文件类型
gzip_vary off;      #跟Squid等缓存服务有关,on的话会在Header里增加 "Vary: Accept-Encoding"
gzip_disable "MSIE [1-6]\.";  #IE1-6版本不支持gzip压缩
  • 服务器发送响应环节,可以使用 Transfer-Encoding=chunked(bigPipe)

  • 减少 cookie 的体积(见上面的 cookie 的优化)

继续我们上面的页面访问的流程,下面是浏览器渲染的过程

解析 HTML 以构建 DOM 树 -> 解析 CSS 构建 CSSOM -> 将 DOM 树与 CSSOM 树合并,构建 Render 树 -> 布局 render 树 -> 绘制 render 树

  1. js 放在 body 最后,css 放在前面

遇到 style、link 标签,会下载解析样式表,同时构建 CSSOM tree,不会阻塞 html 的解析。但是遇到 script 标签,它会立即下载并执行得到的脚本,会阻塞 HTML 的解析。直到脚本里的同步代码部分(settimeout 等异步操作之外的代码)执行完之后,再接着解析接下来的 HTML。直到将整个 HTML 文档的最后一个标签解析完毕,DOM tree 生成完毕。然后 CSSOM tree 、render tree 生成,开始渲染。

问题: 上面所述将 DOM 树与 CSSOM 树合并,构建 Render 树 是发生在构建 DOM 数和 CSSOM 树之后,那么为什么需要将 js 放在 body 最后?反正你不管放在哪里在 JS 加载和执行完成之后都 DOM 树构建都不是完成的,也不会进行下一步的构建 render 树。

虽然我们构建 render 树是在构建 DOM 树和 CSSOM 树之后,但是浏览器会进行预渲染(first render),那么这时候我们肯定影响 render 树的 CSSOM 就应该放在前面(早加载早构建),对于影响 HTML 解析的 JS 就放在后面,这样就最大化利用预渲染功能

开始显示内容(白屏时间)->首屏内容加载完成(首屏时间)->用户可交互(DOMContentLoaded)->加载完成(load)

  1. ECMAScript 与 DOM 在浏览器是独立实现的,所以通过 JS 去访问 DOM 都是需要过桥费的,会有很大的性能损耗。 1.1 最小化 DOM 的访问次数,尽量在 JS 端处理
js
for (var i = 5000; i > 0; i--) {
  document.getElementById("id1").innerHTML += "a"
}
// 我们需要修改的地方就有
// 1. 不要频繁的访问 DOM,多次访问 DOM 缓存 DOM 对象。
var $id1 = document.getElementById("id1")
for (var i = 5000; i > 0; i--) {
  $id1.innerHTML += "a"
}
// 第二 不要频繁的去操作 DOM
var $id1 = document.getElementById("id1")
var str = ""
for (var i = 5000; i > 0; i--) {
  str += "a"
}
$id1.innerHTML = str

优化的方法 : 1.1.1 缓存常用的 DOM 1.1.2 innerHTML 与 createElement() 相差无几 1.1.3 复杂的 DOM 操作 使用 document.createDocumentFragement() 虚拟上下文 1.1.4 el.cloneNode() 克隆节点操作 完成后再 replaceChild()

  1. 如果需要多次访问某个 DOM 节点,请使用局部变量存储起来。

  2. 小心处理 HTML 集合概念(HTML 集合是实时的),所以

    我们常用的集合有哪些? 如
    NodeList , NamedNodeMap , HTMLCollection

    3.1 把集合的长度缓存到一个局部变量中。 3.2 如果需要经常操作集合,请拷贝到一个数组中。

js
// //  方法
//    document.getElementsByTagName() document.getElementsByClassName() document.getElementsByName()
// //    属性
//    document.images,document.links,
// //    元素的属性
//    el.attributes el.classList
//    例子:
// 1. 死循环
var allDivs = document.getElementsByTagName("div")
for (var i = 0; i < allDivs.length; i++) {
  document.body.appendChild(document.createElement("div"))
}

// 2. 因为集合是实时的,所以我们每次读取 length 的时候,都需要重新查询,所以读取集合的 length 比数组的 length 慢得多。
// 第一步: 缓存 length
var allDivs = document.getElementsByTagName("div"),
  divLength = addDivs.length
for (var i = divLength - 1; i >= 0; i--) {
  document.body.appendChild(document.createElement("div"))
}

// 当我们需要对集合的每一个元素进行操作的时候,特别是大数据量的时候,最好每次缓存其 DOM
var allDivs = document.getElementsByTagName("div"),
  divLength = addDivs.length
cacheEl = null
var name = ""
for (var i = divLength - 1; i >= 0; i--) {
  cacheEl = allDivs[i]
  name = cacheEl.nodeName
  name = cacheEl.nodeType
  name = cacheEl.nodeValue
}
  1. 尽量使用速度更快的 API,如 querySelector() querySelectorAll()。 不要 document.getElementById("id").getElementsByTagName('li')这种。
  2. 留意重绘与重排。批量修改样式时,离线操作 DOM,使用缓存,并减少访问布局信息的次数。

什么是重排 当浏览器中的元素几何属性(宽高),位置发生了改变,浏览器需要使受影响的元素失效,重新构造渲染树。 什么是重绘 当元素的颜色,背景色等不影响宽高、位置的属性改变了,需要浏览器重新绘制一下这个元素就行。

重排产生情况

  • 添加或者删除可见的 DOM 元素。
  • 元素的位置发生改变。
  • 元素的尺寸发生改变(外边距,边框,内边距,内容,宽高)
  • 元素内容发生了修改。 文本修改 图片修改
  • 页面渲染初始化
  • 浏览器的窗口尺寸改变

浏览器通过队列话的方式去批量执行重排,但有些会强制刷新队列,并立即执行。(获取布局信息) offsetTop,offsetLeft,offsetWidth,offsetHeight scrollTop,scrollLeft,scrollWidth,scrollHeight clientTop,clientLeft,clientWidth,clientHeight getComputedStyle() currentStyle

怎么优化

  • 样式的处理 通过 cssText 批量处理 style, className 批量处理 class
  • 操作 DOM
  • 使元素脱离文档流-> 处理元素 -> 添加回文档流 display:none; 文档片段 document.createDocumentFragment() cloneNode
  • 获取布局信息

不建议 el.offsetTop = el.offsetTop + 2 + 'px';

建议 var top = el.offsetTop; el.offsetTop = top + 1 + 'px';

  1. 动画中使用绝对定位,使用拖放代理

如我们常有的展开与折叠元素,这个就有大量的重排 解决方式 先让其脱离文档流,然后在添加入文档流中

  1. 使用事件委托来减少事件处理器的数量

前端如何做性能监控、异常监控?

性能监控,异常监控,基本在小公司,是没有实践基础的,可是在差不多的大厂中,他们会关注这个问题。 首先是性能监控,应该从这么几个维度来说:一个是 http 的方面,在后端 log 日志,流入 kafka,然后在 kafka 消费数据,可以准确的监控到哪些接口有异常?异常率是多少? 另一个方面,是前端的 Performance 的 api,在用户的实时使用的过程中,就会产生数据,这样就能实现页面性能监控。 前端异常监控,首先要明白什么是异常,html、css 这些东西,无非就是一个展示的问题,还不至于让页面白屏的事情发生,所谓的异常监控,其实就是 js 的异常监控。在前端领域,window.onerror 是进行 js 异常的监听事件。并且要知道,它在 IE 中,是不支持的,所以 IE 的监控,要使用 try catch 的方式进行捕获,比如我们可能还要注意到,遇到异步的时候,这个如何做 try catch 的异常捕获。 最后一个是前端 sdk 埋点,直接开发一个 js 文件,统计用户的 UV/PV 分析等等,比如用户的转化率之类的,这一块个人没有什么特别的实践,各位可以在网上百度看看。

前端安全方面

sql 注入,要理解 sql 注入的场景,它的原理是什么,当前的数据库的解决方案是什么? xss 攻击,常见的攻击场景,什么类型的网站容易被 xss 攻击,整个流程的原理是什么? csrf 攻击,其实就是一个钓鱼网站,要理解为什么会收到攻击,应该采取什么策略进行防御。 cookie 安全,要理解为什么用 token,优势等。 密码安全,主要是用户登陆,用户数据提交,加密,存入数据库的一整个流程。 其次,其实还有 http 和 https 的问题等等。

http、https、http1.0、1.1、2.0、3.0 的区别

http 这一块,其实是一个非常复杂的体系,要深挖的东西特别多。 http 进行非对称加密,得到 https,这个过程是怎么样的?什么是 CA 证书?整个网站进行验证的流程是什么? http 各个版本的区别是什么?解决了哪些问题?比如头部缩减的优化,那你了解这个优化的具体策略吗?缩减了什么?又增加了什么?要深挖细节。 http 的底层协议?tcp/ip 协议的三次握手,四次挥手,具体是怎么通信的?什么叫满启动?甚至延伸到整个网络协议的领域,什么是 socket?udp 是干什么的?dns 解析?ftp?以及不常用的其他协议? 如果再进行扩展,计算机网络的 7 层结构?每一层做了什么事情?计算机组成原理,如何解析我们的代码等等。