GET与POST的区别
幂等性:幂等的意思是这个操作重复很多遍但是并不影响它的结果 关于幂等性 (必须说的) 由于GET是读,POST是写,所以
- GET是幂等的,POST不是幂等的。POST每次提交了之后 都会多一条结果。
- 浏览器打开网页会发送GET请求, 想要POST打开网页要用form标签
- GET打开的页面是无害的(刷新很多次都没有区别),POST打开页面刷新需要确认(比如支付页面刷新之前需要进行确认一下是否真的要刷新?)
- GET结果会被缓存,POST结果不会被缓存
- GET打开的页面可以被书签收藏,POST打开的不行 了解如何用post打开一个页面?
<form action=http://www.baidu.com method=”POST”target=”_blank”>
<input name=”w”/>
<button type=”submit”>提交</button>
</form>
点击了按钮之后就会新开页进行跳转了
关于请求参数(必答的点)
-
通常GET请求参数是放在url里面的,POST请求数据放在body(消息体)里面, (但是其实你GET放在body里面 post你放在url上也不会报错的,只是说可能后端不支持,拿不到参数,有些后端框架限制了,但是你这样做也不会报错,不过我们还是不会这样做,这是个不成文的规定吧,GET的参数放在url上post的参数放在Body中)
-
GET比POST更不安全,因为参数直接暴露在url上,所以不能用来传递敏感信息 (但是其实body也不是安全的, body也是明文的)
-
GET请求参数放在url里是有长度限制的,而post的请求参数放在Body里没有长度限制, (有个状态码叫414,就是请求过长) (但是其实协议中对url的长度是没有限制的,那么是谁限制了呢? 是浏览器和服务器为了自己实现的方便,很多浏览器或者服务器将Url的长度做了限制,是软件和服务器进行了限制, post的请求参数放在Body里面其实也是有长度限制的,可能有时候上传文件,但是因为文件过于巨大服务器不支持,这个长度的限制是可以在ngix里面配置支持最大的Body是多长,只不过一般我们很难超过这个限制,所以并没有说没有长度限制,但是面试可能就是回答通常的情况,就这样回答吧 ) 关于TCP 包(必答的点) GET产生一个TCP数据包,POST产生两个或以上TCP数据包 因为GET没有body,只需要发head, 而post又有head又有body
根据技术规格文档,GET和POST最大的区别是语义上的区别,但是面试官一般问的是实践过程中二者的区别,因此你需要了解服务器和浏览器对GET和POST的常见实现方法。
http 缓存有哪些方案
| 方案 | 强缓存 | 弱缓存(协商缓存)/内容协商 |
|---|---|---|
| http1.1 | Cache-Control: max-age=3600(响应参数) 只要在http响应头上写上max-age=3600,那么它就会自动地缓存1个小时,这一小时之内,如果请求了同样的数据,(url 参数等都一样),那么就直接不发请求了。 查看network那一列,你会发现这个时间段里的请求,size那一栏显示的是disk cache,这个就是从硬盘缓存中读取的,不是从服务器请求的 那如果时间过期了呢,比如3700秒的时候。那我们的缓存还能用吗?比如某个首页设置了一年的缓存,内容也都没有更新,我们设置的缓存是一年,那1年时间到了之后,这个缓存设置还能用吗?这个时候需要询问一下服务器了,这个首页的缓存数据我保存了一年了,时间已经到了,我还能继续用这个缓存吗?我需不需要更新,需不需要把现在保存的缓存数据删除呢? Etag: ABCXXX(响应参数) 在我们第一次请求这个文件的时候,服务器除了cache-control, 还会在Etag字段中存放这个文件的特征值,那么缓存时间到期之后,浏览器需要带上这个值给到服务器,服务器会将这个值与自己根据文件算出来的值进行比较是否一致。 所以在每次发送请求之前,浏览器都会对缓存进行一个是否过期的判断,如果还没过期的话,就完全使用缓存,不发请求,浏览器与服务器之间没有交互,如果过期了就把文件的Etag值发送给服务器 | If-None-Match: ABCxxx 如果缓存过期了,浏览器就会带上这个请求头,值就是一开始服务器给到的Etag值,发送给服务器。 响应状态码: 304 或200 1. 状态码为304时 说明文件没有被更改过,还可以继续用缓存 2.当返回的状态码是200,说明文件被更新了,并且服务器会返回了新的文件。 3.服务器没有返回Cache-Control或者Cache-Control: max-age=0; 那么浏览器就要把之前缓存的文件删除了。并且也不会对新的文件进行缓存 4. 服务器返回了Cache-Control: max-age=xxx, 那么需要用新返回的文件覆盖原来缓存的文件,接下来的xxx时间内,都用这个新的缓存文件 |
| http1.0 | Expires: Web, 21 Oct 2015 02:30:00 GMT(响应参数) 1.0版本的方案设置了一个过期时间和文件上一次修改的时间给到浏览器,这种方案的问题在于:如果用户本地时间错乱,比如更改了电脑的时间,那么可能缓存直接失效了,或者一直缓存着不更新。但是cache-control返回的是一个有效时间段,这个不会造成这种问题。 Last-Modified: Web, 21 Oct 2015 01:00:00 GMT(响应参数) 另外,服务器会给到一个Last-Modified为最后一次文件更新的时间,那么当文件过期的时候,浏览器会把Last-Modified这个返回给服务器放在If-Modified-Since: xxx,告诉服务器我现在缓存的这个文件上次的修改时间是xxx,我要不要进行更新呢? | If-Modified-Since: Web, 21 Oct 2015 07:28:00 GMT(请求参数) 响应状态码: 304 或200 这里可能出现的问题是,如果这个文件在很短时间内被多次修改,比如在1s内被改了10次?这个就很难区分。所以1.0的方案存在Bug.缓存的设置可以再ngix中设置,接口层也可以设置。文件的缓存时间按需求来定,首页是不能被缓存。 |
清除缓存的方式:
-
强制刷新: 按住shift +浏览器刷新按钮, 可以强制清除掉这些缓存。
-
更改url 可以把强缓存去掉。
-
在浏览器的设置—隐私设置与安全性—清除浏览器数据---选择缓存的图片和文件—确定
-
服务器主动更新文件,因为Index.html是不会设置缓存的,那么他引用的文件可能上一个版本是1.js, 更新了版本之后变成2.js了,即使1.js还在用户的硬盘里面存着,但是也不会用到他了。 在浏览器 勾选了disable-cache, 会把强缓存去掉,可以看到 在请求头上会增加一个请求头: Cache-Control: no-cache.
浏览器缓存的内容 有些是memory cache 有些是disk cache 这个是怎么决定的呢? memory cache: 从内存上读取的缓存 因为缓存时间比较短,所以放在内存上了。
http缓存更详细补充
DNS缓存(会用到操作系统和浏览器的缓存) 访问百度时,需要得到百度的IP地址, 在浏览器地址栏中输入网页地址,那么浏览器会查询缓存是否有对应的IP, 这里缓存时间大概在n分钟,没有的话浏览器会向操作系统进行询问(操作系统的缓存时间大概是1天, 操作系统里面会有个host文件,里面缓存域名对应的ip),有就返回,没有就会向电信/移动等询问,返回百度的ip地址,一直返回给到客户端,客户端就会向百度这个ip发起请求了。
Cache-Control
- public
- 公开内容,文件是公开的,那么中间所有的代理都可以对文件进行缓存
- private 只有用户的浏览器 和百度才能知道这个内容
- max-age=3600 3600内是有效的。
- must-revalidate 必须重新校验, 校验成功了可以继续用,校验失败的话 删除缓存。 这里的校验也叫做内容协商或者弱缓存
内容协商 主要是协商缓存过期之后,是否还能重用。详细看上方表格
禁用缓存 如果不加Cache-Control的话,浏览器还会缓存么? 会,有些请求还是会被浏览器缓存下来的。 比如说GET请求的响应一般会被缓存。 200,203:非权威信息, 206部分内容 300: 多选, 301: 永久重定向, 410:已迁移 以上状态码的返回结果会被缓存
301与302的区别?
- 301: 永久重定向,已经表明是永久了,那么浏览器当前可以把它缓存起来了,这样就不用每次都去请求了
- 302: 临时重定向,不会被缓存, 因为表明了是临时的,所以指不定什么时候又回来了呢。
如何手动地禁用缓存呢?
服务器端发起的禁用:
- Cache-Control: max-age = 0, must-revalidate.意思就是这个文件到浏览器之后当场失效,后面的must-revalidate意思是可以用于协商,就是你可以把这个文件放你的设备上,下次可以进行协商。
- Cache-Control: no-cache 与上面的意思一致,不缓存但可以用于协商。
- Cache-Control: no-store: 不缓存也不协商,完全不要把我的这个文件放在你的设备上(最严格的) 浏览器发起的禁用
- url后增加一个?v=122(一个随机数) 因为url变了,所以就会请求资源,只要url不一样就会跳过缓存
- 在请求头上新增 Cache-Control: no-cache, no-store, max-age=0; 你在浏览器上勾选disable-cache, 浏览器就会自动帮你加上Cache-Control: no-cache了。 关于Pragma: MDN已明确不推荐使用了。没有确切的规范。 是用来只兼容http1.0协议的缓存服务器的,那个时候http:1.1 中的Cache-Control还没有出来,但是现在都是http1.1了
http与https的区别
(目前如果是http的网站 chrome会标注一个不安全) https = https + SSL/TLS(完全层) 区别列表:
- HTTP是明文运输的,不安全;HTTPS是加密传输的,非常安全 什么是明文? 比如说我们在一个网站输入了账号密码,这些数据在传输的过程中,经过了路由器,电信运营商等,他们都可以看到你的账号和密码,很多人在使用免费wifi的时候会被盗号,因为只要你是http传输的,就毫无安全可言,密码都可以被窃取。比如说发现你访问了qq的域名,那么它就把所以password的都拷贝过来,然后一个个去试就能试出是哪个密码了。 什么是加密? 加密之后的就不是明文的了,中间的服务商等也没办法解密的,所以也就不知道你的密码是什么了。
- HTTP使用的是80端口,HTTPS使用的是443端口
- HTTP较快,HTTPS较慢 加密需要时间,所以会慢一点。
- HTTPS证书一般需要购买(但也有免费的),HTTP不需要证书
TCP三次握手和四次挥手(Transmission Control Protocol)
建立TCP连接的时候客户端与服务器会经历三次握手
比如我们要跟百度服务器之间进行通信,首先要知道它的IP, 然后接着要建立一个通信的渠道,就是tcp通道。可以理解为我们和百度之间有个隐形的道路,这个道路上可以发送数据,一般我们发送的就是基于http协议的数据,包括了请求和响应。
- TCP可以理解为传输层,http理解为应用层
- 3次握手 就是为了确定a 可以向b 发送信息 b可以接收信息,b也可以向a发送信息,a可以接收信息
A和B是两个独立的IP, 所以我们需要确定对方是否可以发送和接收 tcp的内容,所以就有了三次握手:
- 浏览器向服务器发送TCP数据 SYN(seq=x) (Synchronize Sequence Numbers 同步 序列号) 我要跟你同步 序列号需要对应的 以确定数据是否丢失 SYN就是同步信息的同步 x y 一般是从0开始
- 服务器向浏览器发送TCP数据 ACK(seq=x+1) SYN(y) (acknowledge的缩写:知道了, 我也要跟你同步)
- 浏览器向服务器发送TCP数据 ACK(seq=y+1) (知道了) 问题: 为什么需要第三步,如果不发的话会怎么样? 因为服务端给客户端发了SYN之后,客户端如果没有回应的话,服务端就不确定客户端是否可以接收数据,也有可能这个时候 客户端已经挂断了,所以没有答应,那这个时候服务端就会判定为这次的连接是失败的。 3次握手成功了之后,就开始建立TCP连接了,如果握手失败的话,就不会建立连接了。 建立成功之后就开始传输http内容。
关闭TCP连接时,服务器与客户端之间会经历4次挥手
关闭的x, y 一般都不是0
- 浏览器向服务器发送TCP数据 FIN(seq=x) (finish 结束了 完成了)
- 服务器向浏览器发送TCP数据 ACK(seq=x+1)
- 服务器向浏览器发送TCP数据 FIN(seq=y)
- 浏览器向服务器发送TCP数据 ACK(seq=y+1) 为什么2,3步骤不合并起来呢? 2,3中间服务器可能还有其他事情要做,只是说客户端不再发送请求了,但是还能接收请求,所以服务端可能还有数据要发送,然后服务端发送完了之后,再发FIN告知客户端,不再发送数据了,完事了,这个时候双方既不发送请求也不响应了,所以客户端发送ACK表示知道了,至此就可以各自关闭tcp连接了。
同源策略和跨域
-
同源策略是什么? 如果两个URL的协议,端口和域名都一致的话,则这两个URL是同源的。 www.baidu.com 与 www.baidu.com/ssss 这两个就是同源的, 端口不写的话默认是80,如果是https协议的话 默认是443
-
同源策略怎么做? 只要在浏览器里面打开,那么这个页面就默认遵守同源策略。 但是你在postman中打开没有跨域的限制,发送请求可以成功。
-
优点 保护用户的隐私安全,数据安全。 比如你在浏览器里面登录了支付宝,那么这个时候浏览器里面保存了你的支付宝页面的登录认证信息,比如是放在cookie里面 那如果你在另外一个网站写了一个请求,请求了支付宝页面的数据,如果没有同源的限制的话,你的支付宝信息就会被拿走用于请求支付宝页面的数据,支付宝发现你有登录信息传过来,那就会把支付宝数据传给你了。那你的信息就被窃取了。
所以浏览器作为一个公开的可以访问各种网站的工具,有必要保证用户的隐私安全,所以他做了限制: 你这个域名就只能访问你这个域名的数据,不能访问其他域名的数据。
-
缺点 如果前端想要访问另一个域名的后端接口会被浏览器阻止其获取响应。请求会被发送出去,但是响应会被拦截 (浏览器有个扩展插件,Moesif CORS, 这个扩展被启用之后,浏览器就没有这个同源策略了,你的浏览器会变得非常的不安全,不要使用)
-
如何实现跨域?跨域就是为了突破同源策略 JSONP
- 甲站点利用script标签可以跨域的特性,向乙站点发送get请求
- 乙站点的后端改造js文件的内容,将数据传进回调函数
- 甲站点通过回调函数拿到乙站点的数据 JSONP优点:改动比较小,只需要后端改动一下js文件的内容。 JSONP缺点:1. 用户认证缺失,后端改了之后,所有人都可以访问这个内容,不安全,如果要解决这个问题的话,需要加token; 且只能发送get请求
JSONP大体实现: 客户端会写一个callBack函数,用于接收回传的数据 function callBackFn(res) { console.log(‘res==’, res) //这里是回传的数据 } 然后通过script标签去获取js文件
然后上面这个文件是存在于服务端的,服务端会根据请求链接,将数据给包装好写入这个文件,并且在文件的最后执行一下这个回调函数,callBackFn(res), 并且把包装好的数据放到函数的参数中,那么当浏览器下载完这个文件之后开始解析执行,就会执行到这段callBackFn(res) 也就可以在上面的回调函数中拿到返回的数据了。
CORS(跨源资源共享cross origin resource sharing) 只要同时满足以下两大条件,就是简单请求:
- 请求方法是以下三种方法之一: GET POST HEAD
- HTTP头部信息不能超出以下几种字段: Accept, Accept-Language Content-Langeage Last-Event-ID Content-Type: 仅限于三个值 application/x-www-form-urlencoded, multipart/form-data, text/plain 只要不同时满足以上两大条件的就是复杂请求 对于简单的请求 需要在响应头上增加 Access-Cotrol-Allow-Origin: http://甲站点即可。 因为浏览器会发现甲乙之间已经约定了可以互相访问。
对于复杂请求
-
首先是在响应OPTIONS请求的时候增加响应头 Access-Control-Allow-Origin: https://甲站点 Access-Control-Allow-Method: 可以允许的请求方法,如POST GET PUT DELETE等 Access-Control-Allow-Headers: 如x-Custom-header等可以被允许添加的header
-
然后在响应正常请求的时候增加响应头: Access-Control-Allow-Origin: https://甲站点
-
如果需要附带身份信息的话 js需要在AJAX里设置xhr.withCredentials = true; 这样就会把cookie带入到请求中了。
Nginx代理/Node.js做代理 前端请求后端a,后端a请求另一个域名的后端b, 然后后端a拿到b给的数据之后返回给前端。 比如KYZ前端访问百度的一个接口,是会产生跨域的,那么可以KYZ前端访问KYZ的后端,然后由KYZ的后端去访问百度的后端拿到数据之后,再吐给KYZ的前端,但是代价就是要花很多额外的流量,浪费很多资源,如果前端可以直接访问百度后端的话更好。 而且这种方案的改造成本很大,要改nginx, 或者可以用nodejs做中间层
Session Cookie LocalStorage SessionStorage的区别
Cookie vs LocalStorage
- Cookie 会被发送到服务器,而LocalStorage不会
- Cookie一般大小最大4k , LocalStorage 可以用5Mb甚至10Mb,(各个浏览器不同) cookie 很早就出现了,而localStorage是在h5才出现,所以在html5之前如果前端有需求把要把信息存在浏览器中的话,没有别的选择,只能用cookie,这也导致了很多不必要的信息都得往服务器端发送。后来html5出来之后,有了localStorage,专门用来处理这种需求,并且容量很大。而且LocalStorage不会被发送到服务器,只需要保存在浏览器就好了。 如果超过5mb,这个时候localStorage.setItem(‘xxx’, 123) set不进去了,要报错。 try { localStorage.setItem(‘xxx’, 123) }catch(err){ alert(‘请清空’) } 前端最好不要操作cookie cookie其实是给服务器用的。 看一下KYZ的代码,为啥会要写cookie?。。。 cookie可以设置过期时间 Cookies.set(‘name’, ‘value’, { expires: 7})
怎么让localStorage一段时间过后过期?
localStorage.setItem(‘xxx’, 123) 再给这个字段存一个当前的时间戳localStorage.setItem(‘xxx_expires’, 1234567) 在读取这个字段的时候,也要读取他存储时的那个时间戳,然后判断是否过期了,过期了的话就删除这个,反正看你怎么存就行,只要把当时存的时候的时间给记录下来就好。
补充Cookie相关知识:
- 用户访问 www.baidu.com 域名,服务器在浏览器写入一个cookie, 所属域名默认为当前域名,生效路径默认未根路径 /, 如果生效路径被写成 /aaa的话,那么这个Cookie只有在访问www.baidu.com/aaa 及其子路径时,才有效,
- 浏览器在访问某个路径之前,就会找出对该域名和路径有效且还未过期的Cookie一起发送给服务器
- 一级域名不能读取二级域名设置的Cookie, 比如mydomain.com不能读取
- subdomain.mydomain.com设置的cookie, 但是设置Cookie时,(不管是一级域名设置的还是二级域名设置的),明确将domain属性设置为一级域名,那么这个域名下的各级域名可以共享这个Cookie.
- 可以看到kyz的cookie, domain设置为.paic.com.cn, 路径默认设置为/
LocalStorage vs SessionStorage
- LocaStorage 一般不会过期,除非用户手动清除
- SessionStorage 在会话结束时过期(如关闭浏览器之后,具体由浏览器自行决定,),会话结束,sessionStorage中的东西全部清空
Cookie vs Session
- Cookie 存在浏览器的文件里,Session存在服务器的文件里
- Session是基于Cookie实现的,具体做法就是把SessionID存在Cookie里面。
- Session一般失效时间过短,Cookie失效时间较长
- Session可存储数据远高于Cookie,但是访问量过多的话,会占用过多的服务器资源
- 存取值的类型不同,Cookie只支持存字符串数据,Session可以存任意数据类型 其他的区别自己再找文章看看。
补充Session相关知识:(www.ruanyifeng.com/blog/2018/0…
- session就是保存在服务器中的一段用户信息与加密字符串(sessionId)映射关系的数据。 这里保存的用户信息可能有用户角色,登录时间等。 流程大概是: 浏览器访问服务器,服务器在浏览器端写入sessionId, 下次浏览器访问的服务器的时候就会自动带上这个sessionId, 然后服务器通过sessionId去查找之前保存的数据,由此得到用户信息。 扩展性不好,单机当然没有问题,如果是服务器集群,或者是跨域的服务导向架构,就要求 session 数据共享,每台服务器都能够读取 session
SessionId是Cookie和Session之间的桥梁,大部分系统也是根据此原理来验证用户登录状态的。
关于Token
- 访问资源接口时所需要的凭证
- 简单token组成:uid(用户唯一的身份标识),time(当前的时间戳),sign:签名,
关于JWT: (Json web Token)
- 服务器不用保存session信息,而是把信息都发给客户端,客户端下次拿着这些信息来请求。这样比较容易实现扩展。
- JWT包括header ,payload, signature三部分 payload 主要是需要传递的数据,header里面会存了使用的加密算法
- 客户端收到服务器发送的JWT可以保存在Cookie或者localStorage中,此后的每次请求都要带上JWT, 放在cookie中可以实现每次自动带上,但是不能实现跨域,所以可以放在请求头的Authorization中。
- JWT的使用: 登录成功之后存到localStorage中,然后在请求头的header中每次请求都得带上,这里应该是要公共的地方做配置
HTTP/1.1与HTTP/2的区别
- HTTP2使用的是二进制传输,将header和body分成帧来传输,HTTP/1.1使用的是字符串传输
- HTTP/2支持多路复用,HTTP/1.1不支持。多路复用简单来说就是一个TCP连接从单车道(不是单行道)变成几百个双向通行的车道 也就是一个tcp连接可以给上百个http请求来用,但是http/1.1的话 可能就要开上百个tcp连接了。
- HTTP/2可以压缩head, 但是HTTP/1.1不行,HTTP/1.1只能压缩body 比如有些cookie存储的东西很多,那么HTTP/2可以对其进行压缩,使得它的体积减小很多。
- HTTP/2支持服务器推送,但是HTTP/1.1不支持(不过实际上没多少人用) 更详细内容查看下方的多路复用
HTTP请求方法
GET POST HEAD, OPTIONS, PUT, DELETE, TRACE, CONNECT HTTP状态码(10个以上): 200 301 302 304 404 500
浏览器渲染原理 浏览器通过http请求从服务器获取到资源之后,如何把这些资源最终变成文本和图像展示在界面上呢?
渲染引擎,JS解释器,网络模块
html的解析过程:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="./xxx.css">
</style>
</head>
<body>
<div>hhhh</div>
<script src="./xxx.js"></script>
</body>
</html>
下载html文件 解析html文件 下载css文件 解析css
html完全下载完再开始解析html 还是边下载边解析呢? 这个看各自浏览器各自操作的,没有规范。
我们这儿讨论边下载边解析的过程:
-
一行行地解析,遇到link标签时,就通知网络模块下载css文件
-
解析html的结果是会构建出一棵Dom 树,并不是直接去渲染的
-
下载css的过程中会继续解析html的下一行, 这两个会同时进行
-
那么下载css和解析css是否是同时进行呢?这个也是看各自浏览器的情况,也可能是下载完之后再解析,也可能是边下载边解析
-
css解析完之后构建一棵cssom树,与dom树结构类似,两者是两棵独立的树
-
html继续解析遇到了script标签,这个时候就要去下载js文件了,等js文件下载完了之后,就会立即执行js文件。
-
从js开始下载到js执行完毕这一段时间当中,html的解析是被阻塞的。html的解析会暂停直到js执行完毕,就继续后面的html的解析。也就是说:js的下载和执行会阻塞html的解析
-
为什么js的下载和执行会阻塞html的解析?
因为执行js的过程可能会修改到DOM树,比如 使用document.write(), 那如果我们直接继续解析html而不关注js的执行结果的话,就可能会解析出一棵错误的DOM树了。所以就必须等待js执行完毕。
为什么下载也会呢,理论上可以不阻塞的。以前的浏览器功能没有那么强大,解析html的时候是一行一行的,只有解析到了script标签的时候,才会开始下载js, 那一旦开始下载了就必须等到他执行完毕再继续解析。
那现在的浏览器有的做过优化,会提前开始下载js或者css,比如拿到html文件的时候,全部扫一遍,将js文件 css文件等提前开始下载,然后等解析到script标签的时候,就直接执行js文件,缩短时间。
面试的时候可能分开问,可能一起问。
async 和defer
defer:
-
解析到script标签的时候,发现其带了defer, 浏览器会继续解析html, 不会停下来,js文件会在html文件解析完了之后再执行,执行完毕之后触发DOM Ready事件。
-
如果html在js的下载执行之前就解析完了,那么DOM ready 事件也是要等到js的下载和执行完毕之后才会出现的。
-
defer 让js的下载和解析与html的解析可以同时进行
-
它可以保证js的执行是在html的解析之后,Dom Ready 之前。
-
如果页面中有两个defer的话,他们的下载是可以同时进行的,但是执行顺序是按照书写的顺序,靠前的先执行,全部执行完毕了之后再触发DOM Ready事件。 async:
-
解析到script标签 发现其带了async, 这个时候会去下载js文件,html的解析也是继续执行,不会被阻塞,
-
js下载完毕之后会立即执行。执行的过程中html停止解析,会被阻塞。
-
DOM ready事件会在html解析完毕之后立即触发,而不是等到js执行完毕。
-
所以如果是加了async的,js的下载执行和html的解析会完全不会相互影响,属于两条不同的线。所以这个时候 你无法预测是dom ready先执行呢 还是js先执行。
-
如果页面中有两个async 的话,都是下载完之后立即执行,不用按顺序执行,只要一下载完就会执行。async 使得他们的关系非常松散,所以一般不怎么用async。什么时候用async? 这个js 跟页面毫无关系,比如说只是做一些单纯的计算。 type=”module” 几乎与defer一致。 实际工作中一般只用defer async用的很少
-
css的下载和解析会阻塞js的执行 css的下载和解析过程中是不能执行css的,Js的执行要等css下载解析完之后才能进行。 这是因为js需要读取css的解析结果。 比如在css里面写了h1的高度是300px, 然后js里面它需要获取h1的高度,那么他必须等css解析完才能获取到正确的数据。 页面渲染
页面渲染过程 DOM树 + CSSSOM树 渲染树 -- 布局 –- 绘制 – 合成 渲染树 就是有节点 并且节点的样式都有 布局 就是计算节点的高度 大小 等 绘制当前页的画面 绘制完成之后 进行合成
重绘和重排 CSS Triggers 列出了 哪些属性会引起重绘和重排。 transform 只会触发重新合成。所以这个也是transform写的动画性能更好的原因。
Web性能优化
web性能优化到底在优化什么? 用户体验,从用户按下回车可能: 到有内容出现时 到DOM ready事件发生时 即 DOM Content Loaded(这个代表着html都解析完,js也执行完了 但是并不保证图片已经加载完) 这个指标更容易测量:因为这指标 页面上的js已经执行了,并且他又不会去等待动态的内容的加载,且浏览器有原生的数据可以提供。 到页面可交互时 (js执行了,并且把所有的事件都绑定了,用户点击按钮可用) 到onLoad事件发生时(所有的图片加载完,所有的样式加载完才会触发)比较晚 不会作为指标 到动态资源加载完整时,(包括懒加载的一些内容,更晚了)
如何对web性能进行优化?
-
DNS预解析(可稍微提高点性能,但是用的不多) 比如有多个js文件,但是放在不同的域名下的,那么我们可以把这几个不同的域名的解析提前同时去进行解析,然后再依次下载执行Js 方法1. 在index.html的里写 <link ref=”dns-prefetch”href=a.com 这是比较常见的 前端可控的方式 方法2:在index.html的响应头里写link: a.com; rel=dns-prefetch
-
TCP连接复用 在之前,我们的过程是开启tcp连接 http 请求+响应 关闭tcp连接 再发第二次请求的话 又要开启一次tcp连接。所以这个过程是很浪费时间。 怎么做呢? 在请求头里增加Connection: keep-alive 浏览器告诉服务器 不要关掉tcp连接,我后面还要请求的。 服务器在响应头上加 Connection: keep-alive 表示 同意连接复用。
那么两次请求的间隔多久可以认为是可以连接复用的? 服务器可以设置响应头: KeepAlive: timeout = 5 意思是说如果5秒之后还没发起第二次请求,那么这个tcp连接就要关闭了 浏览器也可以设置KeepAlive: timeout = 5 意思是说5秒后我还没发请求的话,你就可以关闭了,不过最终还是以服务器的能力为准。 如果使用的是HTTP/1.1的话,浏览器会自动在请求头上增加Connections: keep-alive. 服务器的话,nginx 可以配置上这个头,如果是Node 的话 可以在响应头上增加这个头。 3. 并行化连接 连接复用是串行的请求,就是开了一个tcp连接虽然不关闭他,但是里面的请求需要一个一个地发出去,下一个请求需要等上一个请求响应结束之后才能再发出去 并行连接就是同时开启多个tcp连接,然后就可以同时发出多个http请求,不需要进行等待了。 那连接复用是不是就没有用了呢?并不是,因为并行连接是有数量限制的,当并行到浏览器的个数上限时,比如说是5个,就不能再发出请求了。这个时候,后面的请求就是等目前的请求中哪个结束了之后,就复用那个Tcp链接继续发出http请求。
不同浏览器的请求上限个数不同并且跟域名有关,比如请求baidu.com的资源,最多只能发6个请求出去,想同时发12个的话,那就向另外一个域名发送6个。
(所以最好将我们的 css文件 js文件等拆成多个 6个以内,可以并行的下载解析, ajax请求也可以并行发出) 在网页的控制面板 Network下有一个ConnectionId 可以查看哪些链接是复用了同一个tcp连接,ID一致的就是用了同一个连接
- HTTP管道化 这个一般不用,有bug,做了解即可 设计初衷是,建立一个TCP连接,在这一个TCP连接中并行发出多个http请求 但是Bug 在于,http的响应回来只有状态码,响应数据等,并不知道这个响应是属于哪个请求的。 所以如果想要请求与响应一一对应的话,必须响应的顺序与请求的顺序一一对应,这样才能不会出错。但是这样如果中间有个请求的响应非常慢的话,后面的响应就必须得等前面的好了才可以响应,又造成了依赖,所以这个虽然说减少了tcp连接,但是还是会造成响应是串行的,这样的话,还不如并行化连接。所以管道话没有浏览器默认开启,效果并不好。 所以如果想要解决上面的问题,我们需要升级到HTTP/2.0
HTTP/2 多路复用
- http/1.1 是基于字符串的 请求方和响应发送的都是人类能看懂的字符, 而http/2 是基于帧 也可以说基于二进制,其实都是二进制,只不过把人类能看懂的二进制叫做了字符串,如果一个二进制人类直接无法理解的话,就说是二进制
- http/2 的帧 前面9个字节(每个字节8位,共72位)表示了4个信息 length:长度, Type:类型, Flags:标记位, StreamID:流ID, 前面的是固定写死的,剩下的字节是payload 就是数据,这个数据可以很大,
- payload大概是 :method=GET, :scheme=https, :path=/resource, accept=image/jpeg
- http/2的规定所有的请求头是小写的了。如果数据很多的话,就会变成多个帧
HTTP/2请求与响应的方式
- 引入了帧(Frame)的概念,每一帧包含了Length + Type + Flags + StreamID + Payload五个部分,前四个部分是固定的长度,为9字节,第五部分Payload的最大长度为16Kb到16Mb,具体最大长度由终端自行决定
- 保留了请求与响应的概念。请求头和响应头会被发送方压缩后,分成几个连续的Frame传输(frame之间是有顺序的),头部字段会出现在这些Frame的 payload中,接收方拼合这些Frame后,解压缩即可得到真正的数据,包括了请求或响应头。这些帧上会标示出这一帧是否是结束了
- 引入了流(stream)的概念,一个stream由双向传输的连续且有序的Frame组成,一个TCP连接可以同时包含多个Stream(比如100个), 一个Stream只用于一次请求和响应,Stream之间互不影响。
- 服务端可以先发响应,客户端拿到响应结果可以保存,之后就不需要再发对应的请求了。(但是实际上用的很少)
- 头部字段全部改为小写了不允许出现大写,比如accept:text/html
- 引入了伪头部字段的概念,出现在头部字段的前面,必须以冒号开头,比如:method=GET 主要要明白帧和流的概念 qq.com可以看http2的例子: 在network下调出Protocol 可以看到每个请求发出的是用的h2的就是用的http2,http2和http1.1 是可以混用的。 ConnectionID 相同的 就是说明用的同一个tcp连接。
- server push: 服务器推送,实际工作中用到的少但是也要了解一下, 首先是客户端会发送一个请求,第一个请求肯定还是是有客户端发起的。比如你向baidu.com首页发起了请求,那么他同时给你推送index.html, 1.css,2.css, 1.js, 因为服务端知道接下来你是需要这些资源的,所以一并推送给你。你就不需要再给我发送请求了。 那么要做成这种推送需要程序员在后端进行配置的,不然服务器也不知道需要推送哪些内容。 如何配置呢?非常的繁琐。 方法1:需要更改nginx的配置,比如: location / { root /usr/share/nginx/html; index index.html index.html; http2_push /style.css; http2_push /example.png; ….. } 如果文件名字改掉了还得再改nginx的配置。如果每次发版的话,都要去改。 方法2 也是麻烦 需要更改nginx location / { … http2_push_preload on; //在nginx中增加这行 } 然后在index.html响应头里加(后端程序员需要做的) Link: </1.css>; rel=preload;as=style 但是我们的静态文件基本都是webapack打包后带hash值的文件,每次更改很繁琐,所以最终这个特性几乎没有人用。
浏览器工具栏面板
Network工具面板字段说明: waterfall: Request Send: 请求发出的时间 Waiting 就是等待后端响应的时间 Content Download 是下载响应内容的时间 Waterfall里面有一根蓝色的线和一根红色的线 蓝色的线: DOMContentLoaded 也就是DOM Ready事件被发出。 红色的线: Load 主要资源被下载。红色左边就是依赖的主要资源,右边的就是一些不够重要的资源,也可能是动态加载的。 蓝色的线比较重要。
左下角的 xx requestes: 一共发了多少个请求 xx transferred 总共的传输量 xx resources: 页面加载的资源量,一般大于上面的传输量,因为可能有些东西不是从网络传输的,比如页面上的插件等 finished: 目前总共用到的时间,包括所有的时间 DOMContentLoaded: DOM Ready 的时间 Load: 首页加载的资源的量的时间 一般 DOMContentLoaded < Load < finished
Performance面板说明:
点击performance 把body的dom结构删除,然后点击录制,再等左上角的loading按钮不再旋转 就是刷新完成了, 这个时候点击停止录制。 Rendering面板查看页面的渲染 可以知道哪些部分被重新绘制了
coverage面板(查看代码使用率) 点击右边的三个点,然后more Tools, 调出coverage 然后点击面板上的loading按钮,转圈的那个,就可以出来这个页面上 这个可以统计当前页面的代码使用率,哪些代码用了 哪些没有用。 Unused Bytes: 就是没有使用的字节数 点一下其中的一条数据,就可以看到源代码,上面会显示红色的就是没有使用的,蓝色的就是使用的。如果再极限情况下 要删代码的情况下 是比较有用的。 Lighthouse面板 生成建议优化的报告 选择你想要的各个方面,比如关于性能的performance, 关于SEO的等,勾选上, 然后点击Generate Report ,等他结束了之后,就会生成一个关于页面的建议报告。
First Contentful Paint: 第一个内容的绘制时间 Speed Index: Largest Contentful Paint 最大的内容绘制时间 Time to Interactive 可交互的时间 Total Blocking Time 阻塞时间 0 比较好