第一关 请求-文件、数据、连接
文件类的请求:加载HTMl、CSS
数据: ajax请求(基于HTTP,HTTP基于TCP),如何建立连接的(三次握手,为什么不是两次或者四次),socket长连接,beacon(在浏览关闭的时候也能保持连接),长连接方式(socket、websocket、HTTP只要不返回也可以实现长连接)。
协议
HTTP 与 TCP的区别
解释性的题(可以举例子)
假设现在有一个网站
http://www.baidu.com 这里的http是应用层协议,TCP是传输层协议,http是基于tcp传输实现的应用层连接。
http请求、发送、状态(状态码)
http版本:
http1.0:
http1.1:引入长连接
http2.0:二进制 | 多路复用 (没有并发的限制)| 包头压缩 | 服务端推送
http3.0: HTTP3基于UDP协议重新定义了连接,在QUIC层实现了无序、并发字节流的传输,解决了队头阻塞问题
https
https相对于http更加安全,但是性能损耗更发大
优化方法: 整合请求、长连接
白屏现象
上一个请求依赖前一个请求的返回值
ajax1 => ajax2 => ajax3
代码书写
回调 => 回调地狱 => Promise => async / await
可以从network面板中有个瀑布/时间线,如果其中ajax1请求失败或者超时了,就会产生阻塞,此时就会发生白屏现象
阻塞场景: 代码层面 网关层面(比如请求一个认证信息失败了) 没有使用CDN,服务器负载过多也会导致请求缓慢触发请求超时
当请求数据总是很大时,该怎么优化
单纯从网络协议层来优化的话,可以将内容压缩,例如图片上传的时候,可以使用Accept-Encoding进行压缩 => 期望返回内容被压缩,以减少网络流量,提升性能, 比如可以压缩图片的base64码
有三种压缩方式gzip deflate br
但是需要看服务端支不支持压缩或者服务端性能不足导致没有开启压缩,第三方Api需要手动开启压缩
所以Accept-Encoding
是一种请求性的并不是强制性的策略
如果服务端支持压缩也开启了压缩,响应头里具有Content-Encoding:gzip
请求头
1、请求头中的 Cookie (cookie是一种小型文本文件,用于存储用户信息,实现网站的登录、记录、分析等功能。) =>
cookie、localStorage、sessionStorage区别
特性 | Cookie | localStorage | sessionStorage |
---|---|---|---|
数据的生命期 | 一般由服务器生成,可设置失效时间。如果在浏览器端生成Cookie,默认是关闭浏览器后失效 | 除非被清除,否则永久保存 | 仅在当前会话下有效,关闭页面或浏览器后被清除 |
存放数据大小 | 4K左右 | 一般为5MB | 一般为5MB |
与服务器端通信 | 每次都会携带在HTTP头中,如果使用cookie保存过多数据会带来性能问题 | 仅在客户端(即浏览器)中保存,不参与和服务器的通信 | 仅在客户端(即浏览器)中保存,不参与和服务器的通信 |
易用性 | 需要程序员自己封装,源生的Cookie接口不友好 | 源生接口可以接受,亦可再次封装来对Object和Array有更好的支持 | 源生接口可以接受,亦可再次封装来对Object和Array有更好的支持 |
Cookie无法跨域,需要以一种非常不安全的形式使其支持跨域,例如将ifram中的cookie传出来,会非常不安全
跨tab通信(实质上就是进程之间怎么通信),cookie需要同源才支持,sessionStorage不能跨tab,但是localStorage凌驾于浏览器之上所以可以跨tab通信
其他存储方式还有indexDB(键值对数据库)、webSQL(完整的关系型数据库),Vuex之类的
场景题
登陆态以及自动登录,对cookie
小程序登录态(微信一键登录)
重定向(性能优化场景)
强缓存和协商缓存
last-modified Etag
请求控制 手写并发
class LimitPromise {constructor(max) {this._max = max || 6// 当前正在执行的数量this._count = 0// 等待的任务队列this._taskQueue = []// 单例模式-提供一个实例this.instance = null}run (caller) {// 主入口// 输入:外部要添加的请求,每个caller就是一次请求// 输出: 返回队列处理的Promisereturn new Promis((resolve, reject) => {// 创建处理任务const task = this._createTask(caller, resolve, reject)// 判断是否达到并发池上限if (this._count >= this._max) {this.taskQueue.push(task)} else {task()}})}_creteTask() {return () => {caller().then(res => {resolve(res)}).catch(err => {reject(err)}).finally(() => {this._count--if(this._taskQueue.length > 0) {const task = this._taskQueue.shift()task()}})this._count++}}static getInstance (max) {if(!this.instance) {this.instance = new LimitPromise(max)}return this.instance}
}
// 调用
const LimitPromis = LimitPromise.getInstance(3)const asyncTask = () => {return new Promise((resolve, reject) => {})
}for(let i = 0; i <= 7; i++) {LimitPromise.run(asyncTask).then(result => {// ...},error => {// ...})
}
总结: 请求数据很多的时候可以进行压缩、请求控制并发