缓存分为强缓存和协商缓存,看下流程图
cache
缓存机制相关的字段都是在请求和响应头上
强缓存
强缓存,在缓存有效期内,客户端直接读取本地资源。
强缓存返回的状态码是 200
Expires
在 http1.0
中使用,表示资源失效的具体时间点 Expires:Sat, 09 Jun 2018 08:13:56 GMT
,若是访问器和本地时间不一致,可能就会出现问题,在现在 http1.1中换成了 max-age
,为了兼容也可以加上。
Cache-control
指定指令来实现缓存机制,多个指令间逗号分隔,常见的指令有以下几个,完整的可点击下文中的 mdn 连接查看
max-age
: 强缓存的有效时间,单位秒 max-age=30672000
no-cache
:使用缓存协商,先与服务器确认返回的响应是否被更改。
no-store
:直接禁止游览器缓存数据,每次用户请求该资源,都会向服务器发送一个请求,每次都会下载完整的资源,可用于关闭缓存。
public
:表明响应可以被任何对象(包括:发送请求的客户端,代理服务器,等等)缓存,即使是通常不可缓存的内容(例如,该响应没有max-age指令或Expires消息头)。
private
:表明响应只能被单个用户缓存,不能作为共享缓存(即代理服务器不能缓存它)。私有缓存可以缓存响应内容。
强缓存
强缓存,在缓存有效期内,客户端直接读取本地资源。
强缓存返回的状态码是 200
Expires
在 http1.0
中使用,表示资源失效的具体时间点 Expires:Sat, 09 Jun 2018 08:13:56 GMT
,若是访问器和本地时间不一致,可能就会出现问题,在现在 http1.1中换成了 max-age
,为了兼容也可以加上。
协商缓存
协商缓存,关键在于协商,在使用本地缓存之前,需要先跟服务器做个对比,服务器告知你的资源可用,是最新的,那就可以直接取本地资源,反之,服务器返回最新的资源给客户端,客户端收到后更新本地资源。
状态码:
-
若本地资源是最新的,那么返回 304 (考点!)
-
若比对后,需要从服务器获取最新资源,那就是正常的 200
Last-modified If-Modified-Since
采用资源最后修改时间来判断,单位精度秒
Last-Modified:服务器资源的最新更新时间 Tue, 14 Jan 2020 09:18:29 GMT
If-Modified-Since:客户端发起协商,把本地记录的文件更新时间传给服务器,服务器进行判断比较
这个判断方式是 http1.0 的产物,因为时间精度是秒,若文件的更新频率在秒级以内,就会出现文件不一致。
ETag If-None-Match
为了解决上面的那个问题, http1.1 加了这组标记
ETag:服务器根据内容生成唯一的字符串标识
If-None-Match:客户端发起协商,把本地记录的 hash 标识传给服务器,服务器进行判断比较。
若同时存在 Last-Modified
和 ETag
, ETag
的优先级更高。
浏览器缓存存放位置
browser-cache
可看到有两个来源:
memory cache
:内存中读取
disk cache
:硬盘中读取
内存当然要比硬盘读取快,为啥会有存放硬盘呢?
因为内存浏览器内存有限啊,所以浏览器会有一套机制,根据文件大小何使用频率存放不同的位置,具体的实现取决于浏览器厂商,不过这微小对用户是无感知的。
参考文档
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Cache-Control
了解 PWA 么
PWA(Progressive web apps,渐进式 Web 应用)运用现代的 Web API 以及传统的渐进式增强策略来创建跨平台 Web 应用程序。(来自 MDN)
先看看 PWA 有哪些核心技术,就知道它有哪些优势了
App Shell
App Shell 架构是构建 Progressive Web App 的一种方式,这种应用能可靠且即时地加载到您的用户屏幕上,与本机应用相似。
这个模型包含界面所需要的最小资源文件,如果离线缓存,可以确保重复访问都有快速响应的特性,页面可快速渲染,网络仅仅获取数据。
或者这么理解, App Shell 就类似于原生app,没网络也可以本地启动。
ServiceWork
PWA 的核心,上面说到缓存可以让页面尽快加载,但必须有网络的情况下才行,没网络下还想加载网页咋办?
ServiceWork 持久的离线缓存的能力就可以实现。
Service Worker 有以下功能和特性:
-
一个独立的 worker 线程,独立于当前网页进程,有自己独立的 worker context
-
一旦被 install,就永远存在,除非被手动 unregister
-
用到的时候可以直接唤醒,不用的时候自动睡眠
-
可编程拦截代理请求和返回,缓存文件,缓存的文件可以被网页进程取到(包括网络离线状态)
-
离线内容开发者可控
-
能向客户端推送消息
-
不能直接操作 DOM
-
必须在 HTTPS 环境下才能工作
-
异步实现,内部大都是通过 Promise 实现
js 是单线程的,ServiceWork 独立线程意味着不会阻塞js执行;可编程拦截代理请求和返回,可自定义文件缓存策略。
这些特点意味着开发者有足够的权限去操作缓存,让缓存做到优雅,效率达到极致
接下来核心是如何让设计缓存策略,
-
缓存优先,先查询缓存,若存在,直接返回,不存在,请求服务,更新缓存
-
服务端优先,不查询缓存,直接请求服务端,服务端失败才会查询缓存
-
稳定优先,先查询缓存,有就读取,同时请求服务端更新资源
推荐大家看看开源的 wordbox 封装的缓存策略,策略更加丰富。
代码不复杂,主要是声明周期、与js线程间通信、api调用,就不贴上来了。
参考文档:
https://lavas.baidu.com/pwa/offline-and-cache-loading/service-worker/service-worker-introduction
https://developer.mozilla.org/zh-CN/docs/Web/Progressive_web_apps
URL 输入到渲染的过程
-
域名解析,找到服务地址
-
构建 TCP 连接,若有 https,则多一层 TLS 握手,
-
特殊响应码处理 301 302
-
解析文档
-
构建 dom 树和 csscom
-
生成渲染树:从DOM树的根节点开始遍历每个可见节点,对于每个可见的节点,找到CSSOM树中对应的规则,并应用它们,根据每个可见节点以及其对应的样式,组合生成渲染树
-
Layout(回流):根据生成的渲染树,进行回流(Layout),得到节点的集合信息
-
Painting(重绘):根据渲染树及其回流得到的集合信息,得到节点的绝对像素。
-
绘制,在页面上展示,这一步还涉及到绘制层级、GPU相关的知识点
-
加载js脚本,加载完成解析js脚本
这是一个大致的流程,面试官会从中挑出其他点来接着问
重绘和回流(重排)
先看这图,html文档 和 css 渲染过程的图
layout
页面是采用流式布局来绘制,左到右,上到下,那么一个节点的空间属性若是发生了变化,那么会影响到其他节点的空间布局,需要重新收集节点信息,在进行绘制,这就是回流的过程。
重绘指的是对元素的外观做处理,比如颜色、背景、阴影等。
所以回流一定触发重绘。
触发回流的场景
获取位置信息或者修改几何属性,如下:
-
添加或删除可见的DOM元素
-
元素的位置发生变化
-
元素的尺寸发生变化(包括外边距、内边框、边框大小、高度和宽度等)
-
内容发生变化,比如文本变化或图片被另一个不同尺寸的图片所替代。
-
页面一开始渲染的时候(这肯定避免不了)
-
浏览器的窗口尺寸变化(因为回流是根据视口的大小来计算元素的位置和大小的)
-
获取位置信息,因为需要回流计算最新的值
// 获取位置信息相关属性
- offsetTop offsetLeft offsetWidth offsetHeight 相对于父级容器的偏移量
- scrollTop scrollLeft scrollWidth scrollHeight 相对于父级容器滚动上去的距离
- clientTop clientLeft clientWidth clientHeight 元素边框的厚度
- getComputedStyle()
- getBoundingClientRect
回流的优化
对树的局部甚至全局重新生成是非常耗性能的,所以要避免频繁触发回流
-
现代浏览器已经帮我们做了优化,采用队列存储多次的回流操作,然后批量执行,但获取布局信息例外,因为要获取到实时的数值,浏览器就必须要清空队列,立即执行回流。
-
编码上,避免连续多次修改,可通过合并修改,一次触发
-
对于大量不同的 dom 修改,可以先将其脱离文档流,比如使用绝对定位,或者 display:none ,在文档流外修改完成后再放回文档里中
-
通过节流和防抖控制触发频率
-
css3 硬件加速,transform、opacity、filters,开启后,会新建渲染层
开启GPU加速的方法
开启后,会将 dom 元素提升为独立的渲染层,它的变化不会再影响文档流中的布局。
-
transform: translateZ(0)
-
opacity
-
filters
-
Will-change
说下对 http 协议的了解
http 是建立在 TCP 上的应用层协议,超文本传送协议。
是单向的短链接,目前有 http1.0 http 1.1 http2.0
http1.0 :客户端的每次请求都要求建立一次单独的连接,在处理完本次请求后,就自动释放连接。
http1.1 :可以在一次连接中处理多个请求,并且多个请求可以重叠进行,不需要等待一个请求结束后再发送下一个请求
http2.0 :可支持多路复用,一个 tcp 可同时传输多个 http 请求,头部数据还做了压缩
面试官问这个一般是更关注对 tcp 的理解
tcp
tcp 是传输层协议,它的特点是:三次握手和四次挥手。
三次握手的目的是为了防止已经失效的连接请求报文段突然又传到服务端,而产生错误,所以要建立可靠的连接发送数据
三次握手建立连接过程:
-
客户端发送位码为syn=1,随机产生数据包到服务器,服务器由SYN=1知道客户端要求建立联机(客户端:我要连接你)
-
服务器收到请求后要确认联机信息,向A发送ack number=(客户端的seq+1),syn=1,ack=1,随机生成数据包(服务器:好的,你来连吧)
-
客户端收到后检查ack number是否正确,即第一次发送的seq number+1,以及位码ack是否为1,若正确,客户端会再发送ack number=(服务器的seq+1),ack=1,服务器收到后确认seq值与ack=1则连接建立成功。(客户端:好的,我来了)
四次挥手断开连接的过程:
-
客户端发送请求给服务端,申请主动断开连接,进入等待状态,不在往服务端发送数据,但是接收数据(客户端:我要断开连接了)
-
服务端收到后,告知客户端知道了,服务端进行等待状态,不在接收数据,但是可以继续发送数据(服务端:好,我知道了,但是要等一等)
-
客户端收到服务端的告知后,进入下一阶段的等待。(客户端:好,我等)
-
服务端完成剩余数据的发送后,告知客户端可以断开了,服务端不接收和读取数据(服务端:你可以断开了)
-
客户端收到后,告知服务端,已收到,然后释放链接(客户端:好的,我断开链接了)
-
服务端收到后,也释放链接
UDP
传输层的另外一个协议 UDP 称为用户数据报协议,无连接的传输协议。
UDP 是报文的搬运工,不需要建立完全可靠的链接,不保证数据的可靠性,因为协议控制项比较少,且报文头部简单,报文体积相对要小,速度上相比更快,实时性更高,比如电话会议、多媒体数据流等场景就采用 UDP
介绍下 https
http 报文传输过程中是明文的,可以通过抓包的方式看到报文内容,这就暴露一个安全问题,易被劫持篡改。
为了解决这个问题,就有了 TLS ,https = http + TLS
TLS:安全传输层协议,用于在两个通信应用程序之间提供保密性和数据完整性,该协议由两层组成:TLS 记录协议(TLS Record)和 TLS 握手协议(TLS Handshake)。
TLS 利用非对称加密演算来对通信方做身份认证,之后交换对称密钥作为会谈密钥(Session key),因此 https 分为两个阶段
-
通过非对称加解密确认对方身份是否合法,若合法生成会话密钥。(这一步是核心)
-
报文的在发送前,先用会话密钥进行对称加密,在传输。
TLS 握手
步骤如下:
-
客户端请求服务端建立SSL链接,服务端并向客户端发送一个随机数 randomC 和 CA 机构颁发的证书(注:CA相关下面详细介绍)
-
客户端对证书进行验证,验证通过后,生成一个随机数 randomS ,用公钥对 randomS 加密 ,同时用 randomS 生成一段签名,发送给服务端
-
服务端接收到后,用私钥对秘文解密,用解密后的 key 生成签名,并与客户端传来的签名进行比较,检验通过后,然后生成一个随机数 randomP ,并用私钥加密,还有随机数生成的 hash 值,一并发给客户端。
-
客户端用公钥解密,并校验 hash 值通过后,两端利用 randomC randomS randomP 通过一定的算法生成 session key,后续的报文将通过 session key 对称加密进行传输。
对前端来说,毕竟偏向于理论,所以建议大家根据步骤画一画流程图,更利于理解记忆。
CA 证书
上面第一步讲到 CA证书,假如没有证书验证这一环节,那么公钥在传输过程极有可能被中间人拦截,来个狸猫换太子,将服务端的公钥换成它自己的公钥,返回给客户端,这么一来,就完全起不到加密的作用了,也就是中间人攻击。
所以就需要一个验证的机制,保证公钥是来自服务端的,没有被篡改的,CA证书就出场了。
CA证书,是由 CA 机构颁发的一个凭证,里面关键的信息有,签名算法、签名hash算法、颁发者、有效期、公钥、指纹,这个两个算法就表示对称阶段和非对称阶段采用的算法,公钥就是服务端的公钥,在申请的时候,企业需要上传公钥给CA机构,重点是这个指纹,这个指纹是由 CA 机构通过私钥对一段签名加密生成的。
所以通过验证证书是否合法,就知道公钥是否被篡改,那么怎么验证合法呢?
自然是通过证书的指纹。
在浏览器和个人PC中,都预装了顶级的 CA 机构证书和公钥,所以浏览器获取到证书后,通过内置的公钥对指纹进行解密得到签名,然后浏览器也根据同样的规则生成一段签名,两段签名进行比较,验证通过,那么这个证书中公钥就是可信的。
那么这样一来是不是就可以完全避免了中间人攻击呢?
毕竟顶级的 CA 证书是内置的,还是有一种方式,大家是否还记得我们用,我们是可以用 Fiddle 对 https 进行抓包的,那 Fiddle 算不算中间人呢 ?
Fiddle 之所以能拦截成功是因为,我们在抓包之前,在我们自己手机安装一份来自 Fiddle 的证书,也就是客户端自己信任了第三方来源的证书,这么一来客户端自然能解析出 Fiddle 转发出来的报文啦。
所以只要不随意信任第三方证书,基本上是不会发生中间人攻击的。
什么情况会触发 options 请求
options 通常用于,在跨域请求前发起预检请求,以检测请求是否被服务器接受。
跨域请求中分为简单请求和预检请求两种,符合以下条件可视为简单请求:
-
使用的 HTTP method 是
GET POST HEAD
-
content-type 是
text/plain mutipart/form-data application/x-www-form-urlencode
三种之一 -
请求头只能包含这些
- Accept
- Accept-Language
- Content-Language
- Content-Type (需要注意额外的限制)
- DPR
- Downlink
- Save-Data
- Viewport-Width
- Width
除去简单请求外,其他请求就会先触发预检请求。
常见的,比如使用
-
content-Type 为 application/xml 或 text/xml 的 POST 请求
-
设置自定义头,比如 X-JSON、X-MENGXIANHUI 等
预检请求返回的头部报文中有
Access-Control-Allow-Origin
:服务器可接受的请求来源
Access-Control-Request-Method
:服务器实际请求所使用的 HTTP 方法
Access-Control-Request-Headers
:服务器实际请求所携带的自定义首部字段。
客户端基于从预检请求获得的信息来判断,是否继续执行跨域请求。
注意:跨域请求若想发送 cookie 信息,需要服务端设置 resp.setHeader(“Access-Control-Allow-Credentials”,“true”); 客户端设置 withCredentials: true
参考资料:
https://cloud.tencent.com/developer/news/397683
http有哪些常见请求头
直接看图
rquest-head
response-head
报文提交 Content-Type 的几种区别
Content-Type 字段来获知请求中的消息主体是用何种方式编码
Content-Type: application/json :json 字符串
Content-Type: application/x-www-form-urlencoded :& 将
key=value
进行拼接, jquery 默认使用这个
Content-Type: multipart/form-data :常用于文件上传
关于前端安全防范
主要有两类 XSS
CSRF
XSS
跨站脚本攻击,攻击者将一段可执行的代码注入到网页中,如链接、输入框,分为持久形和临时性的,持久性的是恶意代码被存储到数据库里,会造成持久的攻击;临时性的是仅在当前被工具页面上生效;
防范的方式是对与网页上获取的内容要做转义处理。
CSRF
跨站请求伪造,构造一个钓鱼网站,利用站点对浏览器的信任,从而欺骗用户,发起请求进行恶意操作。
用户在浏览器登录后,站点是信任浏览器的,但浏览器是没法知道请求是否是用户自愿发起的,站点信任后,所发起的请求浏览器都是信任的。
那么用户是已登录的情况下,钓鱼站点中发起跨域请求,跨域标签或者 form 表单,就会把用户的认证信息 cookies 带上,从而到达伪造用户身份进行攻击。
防范方式:
-
服务端校验 Referer ,但某些浏览器可能可以修改 Referer
-
随机 token ,每访问页面就生成一个 token ,页面中的请求把这个 token 带上,服务端对 token 进行校验。注意这个 token 不能存储在 cookie 中
关于 xss csrf 网上有很详细的介绍,不过核心原理还是比较简单的。
了解 CORB
第一次听到有点懵,因为是 CORS ,回来查了资料才明白。
CORB 是一种判断是否要在跨站资源数据到达页面之前阻断其到达当前站点进程中的算法,降低了敏感数据暴露的风险。是站点隔离的一种实现机制,针对跨域标签,保护站点资源。
当跨域请求回来的数据 MIME type
同跨域标签应有的 MIME
类型不匹配时,浏览器会启动 CORB
保护数据不被泄漏,被保护的数据类型只有 html xml json
。
MIME type
MIME 是一个互联网标准,扩展了电子邮件标准,使其可以支持更多的消息类型。常见 MIME 类型如:text/html text/plain image/png application/javascript ,用于标识返回消息属于哪一种文档类型。写法为 type/subtype。
在 HTTP 请求的响应头中,以 Content-Type: application/javascript; charset=UTF-8 的形式出现,MIME type 是 Content-Type 值的一部分
这篇文章写的非常详细,建议大家直接查看 Cross-Origin Read Blocking (CORB)
跨域的解决方案
主流的有一下几种
利用跨域标签 `image script` 发起 get 方法的跨域请求
- image 标签实现
var img = new Image;
img.onload = function() {
},
img.onerror = function() {
},
img.src = options.url;
- script 标签实现
这个就是常说的 JSONP 。script 会执行返回的字符串,那么可以通过约定一个参数,前端的参数指定一个全局的方法,服务端获取到全局方法后,构造一个执行函数的字符串,并把报文放入函数的参数中。浏览器接收到后以 application/javascript
的方式进行解析,就可以触发预设好的回调函数。
/* html */
let scr = document.createElement(‘script’);
scr.src = http://127.0.0.1:3500/xx?callback=cb
document.getElementsByTagName(‘head’)[0].appendChild(scr)
function cb(res){
console.log(‘into’);
console.log(res);
}
/* server */
let data = { name: ‘xiaoli’ }
var str = ctx.query.callback + ‘(’ + JSON.stringify(data) + ‘)’;
// ctx.query = {callback:‘cb’}
// str = ‘cb({“name”:“xiaoli”})’
ctx.body = str;
反向代理
常用 nginx 做反向代理,详细配置就不多说了
CORS
这就全是服务端的工作了,主要的三个参数
Access-Control-Allow-Origin
:服务器可接受的请求来源
Access-Control-Request-Method
:服务器实际请求所使用的 HTTP 方法
Access-Control-Request-Headers
:服务器实际请求所携带的自定义首部字段。
中间层 BFF 做转换
假如有 BFF 层的话,可以在这一层做一个中转,这个就得看项目架构是否有条件了。
html的meta里设置缓存和http请求头设置缓存有什么区别吗
html 的 meta 设置的缓存策略是对于当前文档有效,用于定义页面缓存。
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
body = str;
反向代理
常用 nginx 做反向代理,详细配置就不多说了
CORS
这就全是服务端的工作了,主要的三个参数
Access-Control-Allow-Origin
:服务器可接受的请求来源
Access-Control-Request-Method
:服务器实际请求所使用的 HTTP 方法
Access-Control-Request-Headers
:服务器实际请求所携带的自定义首部字段。
中间层 BFF 做转换
假如有 BFF 层的话,可以在这一层做一个中转,这个就得看项目架构是否有条件了。
html的meta里设置缓存和http请求头设置缓存有什么区别吗
html 的 meta 设置的缓存策略是对于当前文档有效,用于定义页面缓存。
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-HMquryly-1715832777989)]
[外链图片转存中…(img-oflaevTY-1715832777989)]
[外链图片转存中…(img-BVSCJsWU-1715832777989)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!