前言
先介绍下个人情况:2023届校招,两段实习经历,面试接近两个月时间,拿到了9个校招offer(都是sp及以上)。在这里给大家分享一下找实习和校招的面经,希望能对大家有所帮助!
浏览器
http和https
http: 超文本传输协议是互联网上应用最为广泛的一种网络协议,是一个客户端和服务器端请求和应答的标准(TCP),端口为80。
https: 是以安全为目标的HTTP通道,简单讲是HTTP的安全版,即HTTP下加入SSL层,HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL,端口为443,缺点:费时耗电、数据开销大、费用高。
ssl: SSL(Secure Sockets Layer 安全套接字协议),及其继任者传输层安全(Transport Layer Security,TLS)是为网络通信提供安全及数据完整性的一种安全协议。
http2.0 做了哪些改进? 3.0 呢?
http2.0 特性如下
- 二进制分帧传输
- 多路复用
- 头部压缩
- 服务器推送
- 请求优先级
Http3.0 相对于 Http2.0 是一种脱胎换骨的改变!
http 协议是应用层协议,都是建立在传输层之上的。我们也都知道传输层上面不只有 TCP 协议,还有另外一个强大的协议 UDP 协议,2.0 和 1.0 都是基于 TCP 的,因此都会有 TCP 带来的硬伤以及局限性。而 Http3.0 则是建立在 UDP 的基础上。所以其与 Http2.0 之间有质的不同。
http3.0 特性如下
3使用了Google率先推出的QUIC(quick UDP Internet Connection)。
- 连接迁移
- 无队头阻塞
- 自定义的拥塞控制
- 前向安全和前向纠错
细节请看这篇文章Http2.0 的一些思考以及 Http3.0 的优势
多路复用是什么?是如何实现的?
HTTP 2.0 的多路复用其实是 HTTP 1.1 中长链接的升级版本。
在 HTTP 1.1 中,一次链接成功后,只要该链接还没断开,那么 client 端可以在这么一个链接中有序地发起多个请求,并以此获得每个请求对应的响应数据。它的缺点是,一次请求与响应的交互必须要等待前面的请求交互完成,否则后面的只能等待,这个就是线头阻塞。下面举个例子:
请求A 和 请求B。A 先被发起,此时 server 端接收到了 A 请求,正在处理。同时 B 请求也发过来了。但是 A 请求还没被返回,此时 B 请求只能等待。
在 HTTP 2.0 中,一次链接成功后,只要链接还没断开,那么 client 端就可以在一个链接中并发地发起多个请求,每个请求及该请求的响应不需要等待其他的请求,某个请求任务耗时严重,不会影响到其它连接的正常执行。
简单回答就是:HTTP/2是基于二进制“帧”的协议,HTTP/1.1是基于“文本分割”解析的协议。 HTTP 1.1 基于串行文件传输数据,一次只能处理一个请求或响应,因为这种以分隔符分割消息的数据,在完成之前不能停止解析。 而 HTTP/2 引入二进制数据帧和流的概念,其中帧对数据进行顺序标识,这样浏览器收到数据之后,就可以按照序列对数据进行合并,而不会出现合并后数据错乱的情况。同样是因为有了序列,服务器就可以并行的传输数据。
https的过程
加密算法:
(1)证书加密
服务器在使用证书加密之前需要去证书颁发机构申请该服务器的证书,在HTTPS的请求过程服务器端将会把本服务器的证书发送给客户端,客户端进行证书验证,以此来验证服务器的身份。
(2)对称加密
HTTPS的请求中,客户端和服务器之间的通信的数据是通过对称加密算法进行加密的。对称加密,即在加密和解密的过程使用同一个私钥进行加密以及解密,而且对称加密算法是公开的,所以该私钥是不能够泄漏的,一旦泄漏,对称加密形同虚设。
上述私钥是可能泄漏的,原因是上述私钥是需要在网络中进行传输的。流程:在A端生成私钥,传递给B端(传递过程需要是安全的),后面A端使用该私钥加密,传递数据报文到B端,B端使用接受到的私钥解密。
加密过程:加密算法+明文+私钥------》密文
解密过程:解密算法+密文+私钥------》明文
适用场景:上述过程是不复杂的,对大量数据进行加密时,对称加密是适用的,速度快
(3)非对称加密算法
HTTPS的请求中也使用了非对称加密算法。非对称加密,加密和解密过程使用不同的密钥,一个公钥,对外公开,一个私钥,仅是解密端拥有。由于公钥和私钥是分开的,非对称加密算法安全级别高,加密密文长度有限制,适用于对少量数据进行加密,速度较慢。
case1:使用公钥加密,私钥解密
加密过程:加密算法+明文+共钥------》密文
解密过程:解密算法+密文+私钥------》明文
流程:A端向B端请求,B端返回公钥给A端,然后A端使用公钥加密,传递给数据报文给B端,B端使用自己的私钥进行解密。
case2:使用私钥加密,公钥解密
加密过程:加密算法+明文+私钥------》密文
解密过程:解密算法+密文+共钥------》明文
具体流程:
首先,数据正文一般数据量较大,适用于对称加密,因为对称加密速度快,适应于大量数据加密,但是安全级别低,其中对称加密的私钥需要在网络中传输,容易被盗取;
其次,正因为非对称机密私钥易被盗取,所以我们需要对这个私钥进行加密,而且安全级别要求高,所以这个可以用非对称加密进行加密,原因是对称加密的私钥数据量小,非对称加密可以提供高安全级别和高响应速度。
最后,由于非对称加密的公钥可以在网络中传输,如何保证公钥传送到给正确的一方,这个时候使用了证书来验证。证书不是保证公钥的安全性,而是验证正确的交互方。可以使用下图进行说明:
上述过程就是两次HTTP请求,其详细过程如下:
1.客户端向服务器发起HTTPS的请求,连接到服务器的443端口;
2.服务器将非对称加密的公钥传递给客户端,以证书的形式回传到客户端;
3.客户端接接收该公钥进行验证,就是验证2中证书,如果有问题,则HTTPS请求无法继续;如果没有问题,则上述 公钥是合格的。(第一次HTTP请求)客户端这个时候随机生成一个对称加密的密钥,成为client key,客户端密钥,用于对称加密数据的。使用前面的公钥对client key进行非对称加密;
4.进行二次HTTP请求,将加密之后的密钥传递给服务器;
5.服务器使用私钥进行解密,得到密钥,使用密钥对数据进行对称加密;
6.将对称加密的数据传递给客户端,客户端使用对称解密,得到服务器发送的数据,完成第二次HTTP请求。
keep-alive有什么用,http是无状态的,无状态怎么理解,如何有状态
HTTP无状态: 无状态是指协议对于事务处理没有记忆能力,服务器不知道客户端是什么状态。从另一方面讲,打开 一个服务器上的网页和你之前打开这个服务器上的网页之间没有任何联系。
容易犯的误区:
1、HTTP是一个无状态的面向连接的协议,无状态不代表HTTP不能保持TCP 连接,更不能代表HTTP使用的是UDP协议(无连接)
2、从HTTP/1.1起,默认都开启了Keep-Alive,保持连接特性,简单地说,当一个网页打开完成后,客户端和服务器之间用于传输HTTP 数据的TCP连接不会关闭,如果客户端再次访问这个服务器上的网页,会继续使用这一条已经建立的连接
3、Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间
TCP和UDP区别
TCP/IP协议:TCP/IP协议参考模型共有4层
区别:
(1)TCP是面向连接的,udp是无连接的即发送数据前不需要先建立链接。
(2)TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付。 并且因为tcp可靠,面向连接,不会丢失数据因此适合大数据量的交换。
(3)TCP是面向字节流,UDP面向报文,并且网络出现拥塞会使得发送速率降低(因此会出现丢包,对实时的应用比如IP电话和视频会议等)。
(4)TCP只能是1对1的,UDP支持1对1,1对多。
(5)TCP的首部较大为20字节,而UDP只有8字节。
TCP三次握手和四次分手
SYN:代表请求创建连接,所以在三次握手中前两次要SYN=1,表示这两次用于建立连接,至于第三次什么用,在疑问三里解答。
FIN:表示请求关闭连接,在四次分手时,我们发现FIN发了两遍。这是因为TCP的连接是双向的,所以一次FIN只能关闭一个方向。
ACK:代表确认接受,从上面可以发现,不管是三次握手还是四次分手,在回应的时候都会加上ACK=1,表示消息接收到了,并且在建立连接以后的发送数据时,都需加上ACK=1,来表示数据接收成功。
seq:序列号,什么意思呢?当发送一个数据时,数据是被拆成多个数据包来发送,序列号就是对每个数据包进行编号,这样接受方才能对数据包进行再次拼接。
初始序列号是随机生成的,这样不一样的数据拆包解包就不会连接错了。(例如:两个数据都被拆成1,2,3和一个数据是1,2,3一个是101,102,103,很明显后者不会连接错误)
ack: 这个代表下一个数据包的编号,这也就是为什么第二请求时,ack是seq+1
在创建连接时
1.客户端首先要SYN=1,表示要创建连接
2.服务端接收到后,要告诉客户端:我接受到了!所以加个ACK=1,就变成了ACK=1,SYN=1
3.理论上这时就创建连接成功了,但是要防止一个意外(避免重复连接,两次握手会导致服务端不知道客户端是否接受到自己的请求,浪费服务器资源),所以客户端要再发一个消息给服务端确认一下,这时只需要ACK=1就行了。
三次握手原因: 如果一个连接请求在网络中跑的慢,超时了,这时客户端会重发请求,但是这个跑的慢的请求最后还是跑到了,然后服务端就接收了两个连接请求,然后全部回应就会创建两个连接,浪费资源!
如果加了第三次客户端确认,客户端在接受到一个服务端连接确认请求后,后面再接收到的连接确认请求就可以抛弃不管了。
在四次分手时,
1.首先客户端请求关闭客户端到服务端方向的连接,这时客户端就要发送一个FIN=1,表示要关闭一个方向的连接(见上面四次分手的图)
2.服务端接收到后是需要确认一下的,所以返回了一个ACK=1
3.这时只关闭了一个方向,另一个方向也需要关闭,所以服务端也向客户端发了一个FIN=1 ACK=1
4.客户端接收到后发送ACK=1,表示接收成功
四次(分)挥手原因: 当服务端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉客户端,"你发的FIN报文我收到了"。只有等到我服务端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四次挥手。
http头部信息
1、通用头部
通用头域包含请求和响应消息都支持的头域。
Request URL:请求的URL地址
Request Method: 请求方法,get/post/put/……
Status Code:状态码,200 为请求成功
Remote Address:路由地址
2、请求头部
1)Accept: 告诉WEB服务器自己接受什么介质类型,*/* 表示任何类型,type/* 表示该类型下的所有子类型;
2)Accept-Charset: 浏览器申明自己接收的字符集
Accept-Encoding:浏览器申明自己接收的编码方法,通常指定压缩方法,是否支持压缩,支持什么压缩方法 (gzip,deflate)
3)Accept-Language: 浏览器申明自己接收的语言。语言跟字符集的区别:中文是语言,中文有多种字符集,比如big5,gb2312,gbk等等。
4)Authorization: 当客户端接收到来自WEB服务器的 WWW-Authenticate 响应时,该头部来回应自己的身份验证信息给WEB服务器。
5)Connection:表示是否需要持久连接。close(告诉WEB服务器或者代理服务器,在完成本次请求的响应后,断开连接,
不要等待本次连接的后续请求了)。keep-alive(告诉WEB服务器或者代理服务器,在完成本次请求的响应后,保持连接,等待本次连接的后续请求)。
6)Referer:发送请求页面URL。浏览器向 WEB 服务器表明自己是从哪个 网页/URL 获得/点击 当前请求中的网址/URL。
7)User-Agent: 浏览器表明自己的身份(是哪种浏览器)。
8)Host: 发送请求页面所在域。
9)Cache-Control:浏览器应遵循的缓存机制。
no-cache(不要缓存的实体,要求现在从WEB服务器去取)
max-age:(只接受 Age 值小于 max-age 值,并且没有过期的对象)
max-stale:(可以接受过去的对象,但是过期时间必须小于 max-stale 值)
min-fresh:(接受其新鲜生命期大于其当前 Age 跟 min-fresh 值之和的缓存对象)
10)Pramga:主要使用 Pramga: no-cache,相当于 Cache-Control: no-cache。
11)Range:浏览器(比如 Flashget 多线程下载时)告诉 WEB 服务器自己想取对象的哪部分。
12)Form:一种请求头标,给定控制用户代理的人工用户的电子邮件地址。
13)Cookie:这是最重要的请求头信息之一
3、响应头部
1)Age:当代理服务器用自己缓存的实体去响应请求时,用该头部表明该实体从产生到现在经过多长时间了。
2)Accept-Ranges:WEB服务器表明自己是否接受获取其某个实体的一部分(比如文件的一部分)的请求。bytes:表示接受,none:表示不接受。
3) Cache-Control:服务器应遵循的缓存机制。
public(可以用 Cached 内容回应任何用户)
private(只能用缓存内容回应先前请求该内容的那个用户)
no-cache(可以缓存,但是只有在跟WEB服务器验证了其有效后,才能返回给客户端)
max-age:(本响应包含的对象的过期时间)
ALL: no-store(不允许缓存)
4) Connection: 是否需要持久连接
close(连接已经关闭)。
keepalive(连接保持着,在等待本次连接的后续请求)。
Keep-Alive:如果浏览器请求保持连接,则该头部表明希望 WEB 服务器保持连接多长时间(秒)。例如:Keep-Alive:300
5)Content-Encoding:WEB服务器表明自己使用了什么压缩方法(gzip,deflate)压缩响应中的对象。 例如:Content-Encoding:gzip
6)Content-Language:WEB 服务器告诉浏览器自己响应的对象的语言。
7)Content-Length:WEB 服务器告诉浏览器自己响应的对象的长度。例如:Content-Length: 26012
8)Content-Range:WEB 服务器表明该响应包含的部分对象为整个对象的哪个部分。例如:Content-Range: bytes 21010-47021/47022
9)Content-Type:WEB 服务器告诉浏览器自己响应的对象的类型。例如:Content-Type:application/xml
10)Expired:WEB服务器表明该实体将在什么时候过期,对于过期了的对象,只有在跟WEB服务器验证了其有效性后,才能用来响应客户请求。
11)Last-Modified:WEB 服务器认为对象的最后修改时间,比如文件的最后修改时间,动态页面的最后产生时间等等。
12)Location:WEB 服务器告诉浏览器,试图访问的对象已经被移到别的位置了,到该头部指定的位置去取。
13)Proxy-Authenticate: 代理服务器响应浏览器,要求其提供代理身份验证信息。
14)Server: WEB 服务器表明自己是什么软件及版本等信息。
15)Refresh:表示浏览器应该在多少时间之后刷新文档,以秒计。
HTTP的请求方法
什么时候会出现options
OPTIONS请求即预检请求,可用于检测服务器允许的http方法。当发起跨域请求时,由于安全原因,触发一定条件时浏览器会在正式请求之前自动先发起OPTIONS请求,即CORS预检请求,服务器若接受该跨域请求,浏览器才继续发起正式请求。
- 跨域调用,例如:调试时候很多情况都在跨越方式下调试
- 自定义头部
- 请求头的content-type参数:application/x-www-form-urlencoded,multipart/form-data,text/plain之外的格式
以上三种情况出现就会出现options请求了,说白就是为了服务器安全,例如:同源策略引发这个规则;
get和post请求
-
get在浏览器回退时是无害的,而post会再次请求
解释:无害的意思只是数据不变。幂等性,HTTP幂等方法,是指无论调用这个url多少次,都不会有不同的结果的HTTP方法。也就是不管你调用1次还是调用100次,1000次,结果都是一样的(前提是服务器端的数据没有被人为手动更改。比如说,你数据库中的数据被手动更改过,那两次调用的结果肯定是变化的)
-
get请求会被浏览器主动缓存,而post不会,除非手动设置
-
get请求参数会被完整保留在浏览历史记录里,而post中的参数不会被保留
-
get 请求在url中传送的参数有长度限制,而post没有
-
get参数通过url传递,post放在request body中
简单请求和复杂请求
简单请求: 满足以下两个条件的请求
- 请求方法是以下三种方法之一:
- HEAD
- GET
- POST
- HTTP的头信息不超出以下几种字段:
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
复杂请求: 非简单请求就是复杂请求。 复杂请求在正式请求前都会有预检请求,在浏览器中都能看到有OPTIONS请求,用于向服务器请求权限信息的。 axios 都是复杂请求,ajax 可以是简单请求
请求状态码
1开头: 信息,服务器收到请求,需要请求者继续执行操作
2开头: 成功,操作被成功接收并处理
200 | OK | 请求成功。一般用于GET与POST请求 |
---|---|---|
201 | Created | 已创建。成功请求并创建了新的资源 |
3开头: 这类状态码代表需要客户端采取进一步的操作才能完成请求。通常,这些状态码用来重定向,后续的请求地址(重定向目标)在本次响应的 Location 域中指明。
301状态码:
被请求的资源已永久移动到新位置。服务器返回此响应(对 GET 或 HEAD 请求的响应)时,会自动将请求者转到新位置。
302状态码:
请求的资源临时从不同的URI响应请求,但请求者应继续使用原有位置来进行以后的请求
304状态码:
自从上次请求后,请求的网页未修改过。服务器返回此响应时,不会返回网页内容。 如果网页自请求者上次请求后再也没有更改过,您应将服务器配置为返回此响应(称为 If-Modified-Since HTTP 标头)。
4开头: 客户端错误,请求包含语法错误或无法完成请求。
-
400 Bad Request:请求无效
-
产生原因:
前端提交数据的字段名称和字段类型与后台的实体没有保持一致
前端提交到后台的数据应该是json字符串类型,但是前端没有将对象JSON.stringify转化成字符串。 -
解决方法:
对照字段的名称,保持一致性
将obj对象通过JSON.stringify实现序列化
-
-
401状态码:请求要求身份验证。 对于需要登录的网页,服务器可能返回此响应。
-
403状态码:服务器已经理解请求,但是拒绝执行它。与401响应不同的是,身份验证并不能提供任何帮助,而且这个请求也不应该被重复提交。
-
404状态码:请求失败,请求所希望得到的资源未被在服务器上发现。没有信息能够告诉用户这个状况到底是暂时的还是永久的。假如服务器知道情况的话,应当使用410状态码来告知旧资源因为某些内部的配置机制问题,已经永久的不可用,而且没有任何可以跳转的地址。404这个状态码被广泛应用于当服务器不想揭示到底为何请求被拒绝或者没有其他适合的响应可用的情况下。
5开头: 服务器错误,服务器在处理请求的过程中发生了错误
500 | Internal Server Error | 服务器内部错误,无法完成请求 |
---|---|---|
501 | Not Implemented | 服务器不支持请求的功能,无法完成请求 |
浏览器的渲染
浏览器渲染过程:
- 解析HTML生成DOM树
- 解析CSS生成CSS规则树
- 将DOM树和CSS规则树合并生成渲染树
- 遍历渲染树开始布局,计算每一个节点的位置信息
- 渲染树中每个节点绘制到屏幕上。
回流与重绘
回流: 当节点需要更改自己的布局或者几何属性(宽高等)时,就产生了回流(重排)。 会导致回流的操作:
- 页面初始渲染
- 添加/删除可见DOM元素
- 改变元素位置
- 改变元素尺寸(宽、高、内外边距、边框等)
- 改变元素内容(文本或图片等)
- 改变窗口尺寸
重绘: 当节点需要更改外观而不会影响布局的。比如改变 color、background-color、visibility等就叫称为重绘 回流必定重绘,当重绘不一定回流。 渲染队列:
div.style.left = '10px';
div.style.top = '10px';
div.style.width = '10px';
div.style.height = '10px';
理论上会发生4次重排 ,但是实际上只会发生1次重排 ,因为我们现代的浏览器都有渲染队列的机制 ,当我改变了元素的一个样式会导致浏览器发生重排或重绘时 ,它会进入一个渲染队列 ,然后浏览器继续往下看,如果下面还有样式修改 ,那么同样入队 ,直到下面没有样式修改 ,浏览器会按照渲染队列批量执行来优化重排过程,一并修改样式 ,这样就把本该4次的重排优化为1次。
div.style.left = '10px';
console.log(div.offsetLeft);
div.style.top = '10px';
console.log(div.offsetTop);
div.style.width = '20px';
console.log(div.offsetWidth);
div.style.height = '20px';
console.log(div.offsetHeight);
Obviously not! 此时发生了4次重排!
offsetTop、offsetLeft、offsetWidth、offsetHeight
clientTop、clientLeft、clientWidth、clientHeight
scrollTop、scrollLeft、scrollWidth、scrollHeight
getComputedStyle()(IE中currentStyle)
这些会强制刷新队列要求样式修改任务立刻执行
同源和跨域
同源政策
同协议,域名,端口才能访问。
不同ip地址要跨域
为什么要同源:不同域下的DOM任意操作,ajax任意请求的话如果浏览了恶意网站那么就会泄漏这些隐私数据。
跨域
-
jsonp
原理:服务端返回一个定义好的js函数的调用,并且将服务器的数据以该函数参数的形式传递过来,这个方法需要前后端配合。 动态创建script标签,利用script标签不受同源政策约束来跨域获取数据(只能用get请求);
-
cors跨域(Cross-origin resource sharing)
前端正常请求附带Origin字段,服务端接收到后,更据自己的跨域规则,如果允许访问,响应头设置Access-Control-Allow-Origin字段。
-
html5的postMessage方法跨域
不能和服务端交换数据,只能在两个窗口(iframe)之间交换数据。A页面有B页面的引用。A用postMessage方法发送消息,B页面通过message事件监听并接受消息。
-
devServer 代理服务器(反向代理)
反向代理服务器位于用户与目标服务器之间,但是对于用户而言,反向代理服务器就相当于目标服务器,即用户直接访问反向代理服务器就可以获得目标服务器的资源。(客户不知道真正提供服务人的是谁)
module.exports = {
ddevServer: {
proxy: {
'/api': {
target: 'http://m.baidu.com',//目标地址
ws: true, //// 是否启用websockets
changeOrigin: true, //开启代理:在本地会创建一个虚拟服务端,然后发送请求的数据,并同时接收请求的数据,这样服务端和服务端进行数据的交互就不会有跨域问题
pathRewrite: {'^/api': '/'} //这里重写路径
}
}
}
}
从输入url到展示页面发生了什么
- 浏览器查找域名的IP地址(域名解析)
- 浏览器搜索自己的DNS缓存(维护一张域名与IP地址的对应表)
- 若没有,则搜索操作系统中的DNS缓存(维护一张域名与IP地址的对应表)
- 若没有,则搜索操作系统的hosts文件(Windows环境下,维护一张域名与IP地址的对应表,位置一般在 C:\Windows\System32\drivers\etc\hosts)
- 若没有,则操作系统将域名发送至 本地域名服务器,本地域名服务器 查询自己的DNS缓存,查找成功则返回结果,
递归查询是用户只向本地DNS服务器发出请求,若DNS服务器本身不能解析,则会向另外的DNS服务器发出查询请求,得到结果后转交给客户机。而迭代查询是本地dns服务器向根DNS服务器发出请求,而根DNS服务器只是给出下一级DNS服务器的地址,然后本地DNS服务器再向下一级DNS发送查询请求直至得到最终答案。
- 建立TCP链接
- 浏览器向WEB服务器发起Http请求
- 服务器端处理并返回
- 关闭TCP链接
- 浏览器解析资源
- 浏览器布局渲染
浏览器缓存
cookie
- 这类存储只能保存一段不超过4k的文本信息(在本地),通常是用来保存我们所浏览的网址之类的。用来记录一些用户相关的一些状态。cookie数据保存在客户端,
设置Cookie,自定义一个setcookie和getcookie函数利用document.cookie来设置和获取cookie
// 创建一个 cookie
Cookie cookie = new Cookie("username", "Jovan");
//设置 cookie过期时间
cookie.setMaxAge(7 * 24 * 60 * 60); // expires in 7 days
//添加到 response 中
response.addCookie(cookie);
- cookie 的缺点
- cookie体积过大会造成请求和响应速度变慢。
- 默认浏览器会任何请求都把cookie带上去,哪怕是静态资源。
- cookie可以在前后端修改,数据容易篡改或伪造。
- cookie对于敏感数据的保护基本是无效的。
- 有些用户是禁用掉cookie的
Session
- 经常使用session来记录客户端的登录状态。session数据保存在服务器端。
cookie和session的区别
- session 保存在服务器,客户端不知道其中的信息;cookie 保存在客户端,服务器能够知道其中的信息。
- session 中保存的是对象,cookie 中保存的是字符串。
- session 不能区分路径,同一个用户在访问一个网站期间,所有的session在任何地方都可以访问到。而 cookie 中如果设置了路径参数,那么同一个网站不同路径下的 cookie 互相是不可以访问的。
- cookie 不是很安全,本人可以分析存放在本地的 COOKIE 并进行 COOKIE欺骗
- session 会在一定时间内保存在服务器上。当访问增多,会占用你服务器的性能。考虑到减轻服务器性能方面,应该使用 COOKIE。
- 单个 cookie 保存的数据不能超过 4k ,很多浏览器都限制一个站点最多保存 20 个 cookie。
- session 是通过 cookie来工作的。(session id保存在cookie中)
Wbestorage
Web Storage的目的是为了克服由cookie带来的一些限制,当数据需要被严格控制在客户端上时,无须持续地将数据发回服务器。(存储在客户端,不与服务器交互) Web Storage又分为两种: sessionStorage 和localStorage,存储大小一般为5M
- localStorage - 没有时间限制的数据存储,生命周期是永久性的
- sessionStorage - 针对一个 session 进行数据存储。当用户关闭浏览器窗口后,数据会被删除。
知道cookie吗?cookie会有跨域问题吗?子域能获取到父域的cookie吗?
- cookie(储存在用户本地终端上的数据)
- 有跨域问题,发请求时携带withCredential
- cookie 一般都是由于用户访问页面而被创建的,可是并不是只有在创建 cookie 的页面才可以访问这个cookie。在默认情况下,出于安全方面的考虑,只有与创建 cookie 的页面处于同一个目录或在创建cookie页面的子目录下的网页才可以访问。(父域共享)
name 字段为一个cookie的名称。
value 字段为一个cookie的值。
domain字段为可以访问此cookie的域名。
非顶级域名,如二级域名或者三级域名,设置的cookie的domain只能为顶级域名或者二级域名或者三级域名本身,不能设置其他二级域名的cookie,否则cookie无法生成。
顶级域名只能设置domain为顶级域名,不能设置为二级域名或者三级域名,否则cookie无法生成。
二级域名能读取设置了domain为顶级域名或者自身的cookie,不能读取其他二级域名domain的cookie。所以要想cookie在多个二级域名中共享,需要设置domain为顶级域名,这样就可以在所有二级域名里面或者到这个cookie的值了。
顶级域名只能获取到domain设置为顶级域名的cookie,其他domain设置为二级域名的无法获取。
path字段为可以访问此cookie的页面路径。 比如domain是abc.com,path是/test,那么只有/test路径下的页面可以读取此cookie。
expires/Max-Age 字段为此cookie超时时间。若设置其值为一个时间,那么当到达此时间后,此cookie失效。不设置的话默认值是Session,意思是cookie会和session一起失效。当浏览器关闭(不是浏览器标签页,而是整个浏览器) 后,此cookie失效。
Size字段 此cookie大小。
http字段 cookie的httponly属性。若此属性为true,则只有在http请求头中会带有此cookie的信息,而不能通过document.cookie来访问此cookie。
secure字段 设置是否只能通过https来传递此条cookie
HTTP缓存机制
强制缓存:当发起HTTP请求时,只要当前时间在缓存有效期内,就不会向服务器进行请求,直接从客户端缓存中获得,返回的状态码是 200。 当缓存过期之后,才会真正想服务器发起请求重新获得资源。
- Expires是Web服务器响应消息头字段,在响应http请求时告诉浏览器在过期时间前浏览器可以直接从浏览器缓存取数据,而无需再次请求。
- Cache-Control与Expires的作用一致,都是指明当前资源的有效期,控制浏览器是否直接从浏览器缓存取数据还是重新发请求到服务器取数据。只不过Cache-Control的选择更多,设置更细致,如果同时设置的话,其优先级高于Expires。
- Public:所有内容都将被缓存,在响应头中设置
- Private:内容只缓存在0私有缓存中,在响应头中设置
- no-cache:所有内容都不会被缓存,在请求头和响应头中设置 no-cache 可以在本地缓存,可以在代理服务器缓存,但是这个缓存要服务器验证才可以使用(也就是跳过强制缓存、使用协商缓存)
- no-store 彻底得禁用缓冲,本地和代理服务器都不缓冲,每次都从服务器获取 永远都不要在客户端存储资源,每次永远都要从原始服务器获取资源
- no-store:所有内容都不会被缓存在缓存或Internet临时文件中,在响应头中设置
- must-revalidation/proxy-revalidation:如果缓存的内容失效,请求必须发送到服务 器/代理以进行重新验证,在请求头中设置
- max-age=xxx:缓存的内容将在xxx秒后失效,这个选项只在HTTP1.1中可用,和Last-Modified一起使用时优先级较高,在响应头中设置
协商缓存:在强制缓存失效后,浏览器携带缓存标识向服务器发起请求,服务端和最新资源做对比。 如果资源没更改,返回304(资源未修改),浏览器读取本地缓存。 如果资源有更改,返回200,返回最新的资源。 协商缓存的标识也是在响应报文的HTTP头中和请求结果一起返回给浏览器的,控制协商缓存的字段分别有:Last-Modified / If-Modified-Since和Etag / If-None-Match,其中Etag / If-None-Match的优先级比Last-Modified / If-Modified-Since高。
- Last-Modified字段一般用于表示一个服务器上的资源的最后修改时间,资源可以是静态资源,也可以是动态内容,通过这个最后修改时间可以判断当前请求的资源是否是最新的。 一般服务端会在响应头中返回一个Last-Modified字段,告诉浏览器这个页面的最后修改时间,如Last-Modified:Sat, 25 Feb 2012 12:55:04 GMT,浏览器再次请求时在请求头中增加If-Modified-Since:Sat, 25 Feb 2012 12:55:04 GMT字段,询问当前缓存的页面是否是最新。如果是最新的就返回304,告诉浏览器是最新的,服务器也不会传输最新的数据 。
- Etag(Entity Tag)字段是根据实体内容生成的一段hash字符串,可以标识资源的状态。这个字段的作用是让服务端给每个页面分配一个唯一的编号,然后通过这个编号来区分当前页面是否最新。这种方式比较灵活,但是当后端的服务器有多台时比较难以处理。Etag的优先级高于Last-Modified。
杂项
MVC和MVVM
MVC即Model-View-Controller模型-视图-控制器。
- 用户操作View(视图)传送指令到控制器
- 控制器(controller)完成逻辑业务后,要求模型(model)改变状态
- 模型(model)将更新的数据发送给视图(View),用户得到反馈。
MVC优缺点:
优点:实现了功能模块和显示模块的分离,同时它还提高了应用系统的可维护性、可扩展性。
缺点:不适合小型项目,增加系统结构和实现的复杂性
MVVM :即Model-view-viewmodel模型-视图-视图模型
视图(view)和模型(model)通过视图模型(viewmodel)交互。所以视图改变,数据源改变。数据源改变,视图改变。
webpack
webpack是一个对资源模块化和打包的工具。资源文件包括js,css,图片等。 loader 它就是一个转化器,将文件A转化为B。 对于plugin,它就是一个扩展器,也就是一个插件。 在webpack.config.js文件下配置打包参数
module.exports = {
entry:_dirname + '/app/main.js', //入口文件
output:{
path:_dirname + "/public",//打包后输出路径
filename: "bundle.js" //文件名
},
module:{
rules:[
{
test: /(\jsx|\js)$/, //所要处理的文件
use: {loader: "babel-loader"}, //要使用loader
exclude:/node-moduled/ //不要处理的文件
}
]
},
plugins:{
//插件
}
}
webpack打包产物是将css和js等文件打包成一个bundle.js的文件。 使用babel或css-loader就可以将es6转换为es5,和打包css了。 现在的前端网页功能丰富,JavaScript的复杂度增加和需要一大堆依赖包,所以打包前端页面后,减少了页面的请求。
babel原理
解析: 将代码字符串解析成抽象语法树。
变换: 将抽象语法树。根据变换后的抽象语法树再生成代码字符串。
外部引用模块区别
<script src="path/to/myModule.js" defer></script>
<script src="path/to/myModule.js" async></script>
defer
与async
的区别是:
defer
要等到整个页面在内存中正常渲染结束(DOM 结构完全生成,以及其他脚本执行完成),才会执行;
async
一旦下载完,渲染引擎就会中断渲染,执行这个脚本以后,再继续渲染。
一句话,defer
是“渲染完再执行”,async
是“下载完就执行”。另外,如果有多个defer
脚本,会按照它们在页面出现的顺序加载,而多个async
脚本是不能保证加载顺序的。
JS模块化
主要是封装一个类型的功能,方便其他文件调用。
- commonJs,导出是module.exports = {},导入require(‘module’);
- es6 导出export default={}, 导入import module from ‘module’
commonJs和es6模块的区别:
- CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
- CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。
- CommonJS 模块的
require()
是同步加载模块,ES6 模块的import
命令是异步加载,有一个独立的模块依赖的解析阶段。 - CommonJS 加载的是整个模块,即将所有的接口全部加载进来,ES6 可以单独加载其中的某个接口
- CommonJS 加载的是一个对象(即
module.exports
属性),该对象只有在脚本运行完才会生成。而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。
$(document).ready()和window.onload
- $(document).ready()是Dom结构绘制完毕就执行,不必等到加载完毕。 意思就是DOM树加载完毕,就执行,不必等到页面中图片或其他外部文件都加载完毕。
- window.onload等到页面内包括图片的所有元素加载完才能执行。
面向对象编程和函数式编程的区别
面向对象编程,这种编程是把问题看作由对象的属性与对象所进行的行为组成。基于对象的概念,以类作为对象的模板,以对象为中心,来思考并解决问题。
封装性:含有私有数据和公有数据,封装性能使数据更加安全依赖的就是类的特性。
继承性:子类继承父类的共有数据和函数等。
多态性:不同对象执行同一函数会形成多种行为。
函数式编程,顾名思义,这种编程是以函数思维做为核心,在这种思维的角度去思考问题。这种编程最重要的基础是λ演算,接受函数当作输入和输出。
函数是一等公民:函数与其他数据类型一样,处于平等地位,可以赋值给其他变量,也可以作为参数。
数据不可变:因为所有功能就是返回一个新的值,没有其他行为,尤其是不得修改外部变量的值。
闭包和惰性求值:惰性求值需要时再求值
函数柯里化:
简单的说就是柯里化是把一个多参数函数转换为一个嵌套的一元函数的过程
const addCurried = function(x){
return function(y){
return x + y;
}
}
// 使用方法
addCurried(4)(4)
// 8
重载和重写
重载就是在一个类里面,方法名字相同,而参数不同。 重写就是子类重写父类的方法
utf-8和Unicode区别
简单来说:
• Unicode 是「字符集」
• UTF-8 是「编码规则」
其中:
• 字符集:为每一个「字符」分配一个唯一的 ID(学名为码位 / 码点 / Code Point)
• 编码规则:将「码位」转换为字节序列的规则(编码/解码 可以理解为 加密/解密 的过程)
为什么要用单向数据流替代双向绑定?
- 单向绑定的优点是相应的可以带来单向数据流,这样做的好处是所有状态变化都可以被记录、跟踪,状态变化通过手动调用通知,源头易追溯,没有“暗箱操作”。同时组件数据只有唯一的入口和出口,使得程序更直观更容易理解,有利于应用的可维护性。
- 双向绑定缺点就是由于都是“暗箱操作”,我们无法追踪局部状态的变化(虽然大部分情况下我们并不关心),潜在的行为太多也增加了出错时 debug 的难度。同时由于组件数据变化来源入口变得可能不止一个。
做SEO(Search Engine Optimization)优化
- seo优化其实就是让搜索引擎更快的找到你的网址
- 根据浏览器搜索规则,来写相应的关键词,利于搜索引擎搜索。
- seo站内优化:301永久重定向,404页面,要求登录、强制使用cookies,域名好记短
- seo外优化包含:论坛博客发外链
设计模式
单例模式
保证一个类仅有一个实例,并提供一个访问它的全局访问点
let createLoginWindow = function(title) {
this.title= title;
}
let proxySingleton = (function() {
var instance;
return function(title) {
if (!instance) {
instance = new createLoginWindow(title);
}
return instance;
}
})();//立即执行后返回的是内层函数
let windowA = new proxySingleton('login1');
let windowB = new proxySingleton('login2');
console.log(windowA === windowB); // true
代理模式
代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。 当客户不方便直接访问一个 对象或者不满足需要的时候,提供一个替身对象 来控制对这个对象的访问,客户实际上访问的是 替身对象。 替身对象对请求做出一些处理之后, 再把请求转交给本体对象。 代理和本体的接口具有一致性,本体定义了关键功能,而代理是提供或拒绝对它的访问,或者在访问本体之前做一 些额外的事情。
迭代器模式
迭代器模式是指提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。
发布-订阅者模式
也称作观察者模式,定义了对象间的一种一对多的依赖关系,当一个对象的状态发 生改变时,所有依赖于它的对象都将得到通知
//发布订阅模式
class EventEmiter {
constructor() {
//维护一个对象
this.events = {}
}
on(eventName, callback) {
if (!this.events.hasOwnProperty(eventName)) {
this._events[eventName] = [];
}
this._events[eventName].push(callback);
}
emit(eventName, ...rest) {
if (this.events[eventName]) { //循环一次执行
this.events[eventName].forEach((item) => {
item.apply(this, rest)
});
}
else
console.log("未注册")
}
off(eventName, callback) {
if (this.events[eventName]) {
//当前数组和传递过来的callback相等则移除掉
this.events[eventName] =
this.events[eventName].filter(item => item !== callback);
}
}
once(eventName, callback) {
function one() {
//在one函数运行原来的函数,只有将one清空
callback.apply(this, arguments);
//先绑定 执行后再删除
this.off(eventName, one);
}
this.on(eventName, one);
//此时emit触发会执行此函数,会给这个函数传递rest参数
}
}
class Man extends EventEmiter { }
let man = new Man()
function findGirl() {
console.log('找新的女朋友')
}
function saveMoney() {
console.log('省钱')
}
// man.once('失恋', findGirl);
// man.on('失恋',findGirl) //失恋 ,绑定一个函数方法
// man.on('失恋', saveMoney)//失恋 ,绑定一个函数方法
// man.off('失恋', saveMoney); //移除一个函数方法
man.emit('失恋');
//绑定一次,触发多次,也只执行一次。触发后一次将数组中的哪一项删除掉下次触发就不会执行
工厂模式
在该模式下,代码将会根据具体的输入或其他既定规则,自行决定创建哪种类型的对象。简单点儿说就是,动态返回需要的实例对象
设计模式原则
1)单一职责原则:即一个类应该只负责一项职责。
2)里氏替换原则:子类型必须能够替换它们的父类型
3)依赖倒转原则:A:高层模块不应该依赖底层模块 B:抽象不应该依赖细节,细节应该依赖抽象
4)接口隔离原则:客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上
5)迪米特法则:系统中的类,尽量不要与其他类互相作用,减少类之间的耦合度
6)开闭原则:在一个系统中,对于扩展是开放的,对于修改是关闭的
websocket
WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。
WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
window.location 对象所包含的属性
url参数解析函数
function getQueryVariable(variable)
{
var query = window.location.search.slice(1);//去掉?问号
var vars = query.split("&");
for (var i=0;i<vars.length;i++) {
var pair = vars[i].split("=");
if(pair[0] == variable){return pair[1];}
}
return(false);
}
axios如何实现缓存请求,即已经向这个url发送过一次请求,下次直接读取缓存。(单例模式)
service .interceptors.request.use()请求拦截
service .interceptors.response.use()响应拦截
服务器端渲染(SSR=server side render)
页面上的内容是通过服务端渲染生成的,浏览器直接显示服务端返回的html就可以了
CDN
CDN是构建在网络之上的内容分发网络,通过中心平台的负载均衡、内容分发、调度等功能模块,使用户就近获取所需内容,降低网络拥塞,并提高用户访问响应速度和命中率。
进程与线程
进程是资源分配的最小单位,线程是CPU调度的最小单位
- 线程在进程下行进(单纯的车厢无法运行)
- 一个进程可以包含多个线程(一辆火车可以有多个车厢)
- 不同进程间数据很难共享(一辆火车上的乘客很难换到另外一辆火车,比如站点换乘)
- 同一进程下不同线程间数据很易共享(A车厢换到B车厢很容易)
- 进程要比线程消耗更多的计算机资源(采用多列火车相比多个车厢更耗资源)
- 进程间不会相互影响,一个线程挂掉将导致整个进程挂掉(一列火车不会影响到另外一列火车,但是如果一列火车上中间的一节车厢着火了,将影响到所有车厢)
进程间的通信有哪些方式
事务是什么
事务(Transaction)是并发控制的基本单位。所谓的事务,它是一个操作序列,这些操作要么都执行,要么都不执行,它是一个不可分割的工作单位。
webpack中Loader和Plugin的不同?
不同的作用
- Loader直译为"加载器"。Webpack将一切文件视为模块,但是webpack原生是只能解析js文件,如果想将其他文件也打包的话,就会用到
loader
。 所以Loader的作用是让webpack拥有了加载和解析_非JavaScript文件_的能力。 - Plugin直译为"插件"。Plugin可以扩展webpack的功能,让webpack具有更多的灵活性。 在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。
不同的用法
- Loader在
module.rules
中配置,也就是说他作为模块的解析规则而存在。 类型为数组,每一项都是一个Object
,里面描述了对于什么类型的文件(test
),使用什么加载(loader
)和使用的参数(options
) - Plugin在
plugins
中单独配置。 类型为数组,每一项是一个plugin
的实例,参数都通过构造函数传入。
webpack的构建流程是什么?从读取配置到输出文件这个过程尽量说全
Webpack 的运行流程是一个串行的过程,从启动到结束会依次执行以下流程:
- 初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数;
- 开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译;
- 确定入口:根据配置中的 entry 找出所有的入口文件;
- 编译模块:从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理;
- 完成模块编译:在经过第4步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系;
- 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会;
- 输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。
在以上过程中,Webpack 会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用 Webpack 提供的 API 改变 Webpack 的运行结果。
webpack的热更新是如何做到的?说明其原理?
关于webpack热模块更新的总结如下:
- 通过webpack-dev-server创建两个服务器:提供静态资源的服务(express)和Socket服务
- express server 负责直接提供静态资源的服务(打包后的资源直接被浏览器请求和解析)
- socket server 是一个 websocket 的长连接,双方可以通信
- 当 socket server 监听到对应的模块发生变化时,会生成两个文件.json(manifest文件)和.js文件(update chunk)
- 通过长连接,socket server 可以直接将这两个文件主动发送给客户端(浏览器)
- 浏览器拿到两个新的文件后,通过HMR runtime机制,加载这两个文件,并且针对修改的模块进行更新
如何利用webpack来优化前端性能?(提高性能和体验)
用webpack优化前端性能是指优化webpack的输出结果,让打包的最终结果在浏览器运行快速高效。
- 压缩代码。删除多余的代码、注释、简化代码的写法等等方式。可以利用webpack的
UglifyJsPlugin
和ParallelUglifyPlugin
来压缩JS文件, 利用cssnano
(css-loader?minimize)来压缩css - 利用CDN加速。在构建过程中,将引用的静态资源路径修改为CDN上对应的路径。可以利用webpack对于
output
参数和各loader的publicPath
参数来修改资源路径 - 删除死代码(Tree Shaking)。将代码中永远不会走到的片段删除掉。可以通过在启动webpack时追加参数
--optimize-minimize
来实现 - 提取公共代码。
如何提高webpack的构建速度?
- 多入口情况下,使用
CommonsChunkPlugin
来提取公共代码 - 通过
externals
配置来提取第三方库 - 使用
Happypack
实现多线程加速编译 - 使用
webpack-uglify-parallel
来提升uglifyPlugin
的压缩速度。 原理上webpack-uglify-parallel
采用了多核并行压缩来提升压缩速度 - 使用
Tree-shaking
和Scope Hoisting(作用于域提升,合并多次声明)
来剔除多余代码 - 限定加载器作用范围、缓存加载器执行结果(设置 babel 的 cacheDirectory 为true)、设置 noParse
- resolve 解析优化(当项目中出现 import ‘react’ 既不是绝对路径也不是相对路径时,指定好搜索的路径,可以不用过多的查询)
pushState 和 replaceState 能否触发 popSate 事件
答案是:不能
pushState 和 replaceState
HTML5 新接口,可以改变网址(存在跨域限制)而不刷新页面,这个强大的特性后来用到了单页面应用如:vue-router,react-router-dom 中。
注意:仅改变网址,网页不会真的跳转,也不会获取到新的内容,本质上网页还停留在原页面
window.history.pushState(state, title, targetURL);
@状态对象:传给目标路由的信息,可为空
@页面标题:目前所有浏览器都不支持,填空字符串即可
@可选url:目标url,不会检查url是否存在,且不能跨域。如不传该项,即给当前url添加data
window.history.replaceState(state, title, targetURL);
@类似于pushState,但是会直接替换掉当前url,而不会在history中留下记录
popstate 事件会在点击后退、前进按钮(或调用 history.back()、history.forward()、history.go()方法)时触发
注意:用 history.pushState()或者 history.replaceState()不会触发 popstate 事件
RAF 和 RIC 是什么
requestAnimationFrame: 告诉浏览器在下次重绘之前执行传入的回调函数(通常是操纵 dom,更新动画的函数);由于是每帧执行一次,那结果就是每秒的执行次数与浏览器屏幕刷新次数一样,通常是每秒 60 次。
requestIdleCallback:: 会在浏览器空闲时间执行回调,也就是允许开发人员在主事件循环中执行低优先级任务,而不影响一些延迟关键事件。如果有多个回调,会按照先进先出原则执行,但是当传入了 timeout,为了避免超时,有可能会打乱这个顺序。
这个题目可以深入去问浏览器每一帧的渲染流程 具体可以看看这篇 requestIdleCallback 和 requestAnimationFrame 详解
JS脚本会阻塞DOM构建,js脚本会不会影响css om树构建
JS能够操做DOM来修改DOM结构,能够操做CSSOM来修改节点样式,这就致使了浏览器在遇到<script>
标签时,DOM构建将暂停,直至脚本完成执行,而后继续构建DOM。若是脚本是外部的,会等待脚本下载完毕,再继续解析文档。如今能够在script标签上设置属性defer或者async。脚本解析会将脚本中改变DOM和CSS的地方分别解析出来,追加到DOM树和CSSOM规则树上。
如果 JavaScript 脚本还操作了 CSSOM ,而正好这个 CSSOM 还没有下载和构建,浏览器甚至会延迟脚本执行和构建 DOM ,直至完成其 CSSOM 的下载和构建。 显而易见,如果对 JavaScript 的执行位置运用不当,这将会严重影响渲染的速度。
总结:
1. CSS不会阻塞DOM树的解析,但是会影响 JavaScript的运行,
2. JavaScript 会阻止DOM树的解析,
3. 最终CSS(CSSOM)会影响DOM树的渲染,也可以说最终会影响布局树的生成(有的版本说是渲染树,差不多意思)
Web Worker
Web Worker 的作用,就是为 JavaScript 创造多线程环境,允许主线程创建 Worker 线程,将一些任务分配给后者运行。在主线程运行的同时,Worker 线程在后台运行,两者互不干扰。等到 Worker 线程完成计算任务,再把结果返回给主线程。这样的好处是,一些计算密集型或高延迟的任务,被 Worker 线程负担了,主线程(通常负责 UI 交互)就会很流畅,不会被阻塞或拖慢。
Worker 线程一旦新建成功,就会始终运行,不会被主线程上的活动(比如用户点击按钮、提交表单)打断。这样有利于随时响应主线程的通信。但是,这也造成了 Worker 比较耗费资源,不应该过度使用,而且一旦使用完毕,就应该关闭。
Web Worker 有以下几个使用注意点。
(1)同源限制
分配给 Worker 线程运行的脚本文件,必须与主线程的脚本文件同源。
(2)DOM 限制
Worker 线程所在的全局对象,与主线程不一样,无法读取主线程所在网页的 DOM 对象,也无法使用document、window、parent这些对象。但是,Worker 线程可以navigator对象和location对象。
(3)通信联系
Worker 线程和主线程不在同一个上下文环境,它们不能直接通信,必须通过消息完成。
(4)脚本限制
Worker 线程不能执行alert()方法和confirm()方法,但可以使用 XMLHttpRequest 对象发出 AJAX 请求。
(5)文件限制
Worker 线程无法读取本地文件,即不能打开本机的文件系统(file),它所加载的脚本,必须来自网络。
Service Worker
一个服务器与浏览器之间的中间人角色,如果网站中注册了service worker那么它可以拦截当前网站所有的请求,进行判断(需要编写相应的判断程序),如果需要向服务器发起请求的就转给服务器,如果可以直接使用缓存的就直接返回缓存不再转给服务器。从而大大提高浏览体验。
细碎的描述
- 基于web Worker(一个独立于JavaScript主线程的独立线程,在里面执行需要消耗大量资源的操作不会阻塞主线程)
- 本质上充当服务器与浏览器之间的代理服务器(可以拦截全站的请求,并作出相应的动作->由开发者指定的动作
- 在web worker的基础上增加了离线缓存的能力
- 创建有效的离线体验(将一些不常更新的内容缓存在浏览器,提高访问体验)
- 由事件驱动的,具有生命周期
- 可以访问catche和indexDB
- 支持推送
- 并且可以让开发者自己控制管理缓存的内容以及版本
//Service Worker 脚本通过 navigator.serviceWorker.register 方法注册到页面, 之后可以脱离页面在浏览器后台运行:
if (navigator.serviceWorker) {
navigator.serviceWorker.register('service-worker.js')
.then(function(registration) {
console.log('service worker 注册成功');
}).catch(function (err) {
console.log('servcie worker 注册失败');
});
}
处于安全原因, Service Worker 脚本的作用范围不能超出脚本文件所在的路径。比如地址是 "/sw-test/sw.js" 的脚本只能控制 "/sw-test/" 下的页面。
「回顾2022,展望2023,我正在参与2022年终总结征文大赛活动」