第一步 URL解析
URI:URL+URN
- URI 统一资源标识符
- URL 统一资源定位符
- URN 统一资源名称
传输协议:
- HTTP 超文本传输协议
- HTTPS HTTP+SSL(加密证书) 相对于HTTP来讲会更加安全
- FTP 文件的传输,例如:FTP上传工具、FTP资源共享
HTTP明文传输 HTTPS密文传输 SSL整数中定义了加密解密的方式
域名:服务器有一个外网IP,基于外网IP,而域名就是给外网IP设置一个好记的名字
- 购买的是baidu.com顶级域名
- www.baidu.com一级域名
- img.baidu.com二级域名
- xx.img.baidu.com三级域名
端口号:区分同一台服务器上的不用服务(或项目)的值
- 取值范围:0~65535
- 默认值:我们自己不写端口号,浏览器默认加的 HTTP->80 HTTPS->443 FTP->21
编码问题:如果URL地址或者问号传参的信息中,出现中文或者某些特殊符号,传输中可能会乱码,此时需要客户端先进行编码,服务器再解码
- encodeURI/decodeURI 一般用于对整个URL的编码,它只编码中文和部分特殊符号,对URL地址中的有效符号不编码
- encodeURIComponent/decodeURIComponent 一般用于对URL问号传参的值进行部分编码,因为除了encodeURI可以编码的东西外,对于URL地址中的有效符号也能编码,所以不适合整个URL
- escape/unescape(服务器端没有这个) 不是所有后端语言都支持这个API,所以只用于客户端两个页面间数据传输的编译
let url = `http://www.xx.com/首页.html?lx=0&name=张三`
console.log(encodeURI(url));//只对中文进行编码
let url = `http://www.xx.com/?lx=0&name=${encodeURIComponent('张@三')}&form=${encodeURIComponent('http://www.wx.com/')}`
console.log(url);
console.log(escape('黑马@培训::/'));//:编码 /没有编码
第二步 缓存检查
“缓存”存储在哪?
- 虚拟内存(内存条):页面关闭,存储的东西会消失 临时缓存 Mermory Cache
- 物理内存(硬盘):持久存储 Disk Cache
(读内存的东西时间会更快)
关闭页面,内存里的就没了。
打开页面,查找disk cache中是否有匹配的,如有则使用,如没有则发送网络请求
普通刷新(F5):因TAB没关闭,所以mermory cache是可用的,会被优先使用,其次才是disk Cache
强制刷新(Ctrl+F5):浏览器不使用缓存,因此发送的请求头部均带有Cache-control:no-cache,服务器直接返回200和最新内容
关于静态资源文件的缓存有两种:强缓存、协商缓存 - 不论哪一种,都是服务器设置规则,客户端浏览器会自动配合完成,无需我们写代码
- 一般真实项目中,强缓存、协商缓存都会设置[也可以只设置一个],强缓存失效的情况下,再去走协商缓存的机制。
强缓存:Cache-Control(HTTP/1.1)、Expires(HTTP/1.0)服务器设置的响应头信息 - cache-control: max-age=2592000
- expires: Wed, 17 Aug 2022 06:14:14 GMT
- 不论是从服务器获取还是从缓存中读取,HTTP状态码都是200
浏览器对于强缓存的处理:根据第一次请求资源时返回的响应头来确定的- Expires:缓存过期时间,用来指定资源到期的时间HTTP/1.0
- Cache-Control:cache-control: max-age=2592000第一次拿到资源后的2592000秒内,再次发送请求,读取缓存中的信息(HTTP/1.1)
- 两者同时存在的话,Cache-Control优先级高于Expires
- 把请求结果和缓存标识存储到浏览器中
存在问题:如果本地缓存生效,但是服务器更新了这个资源,如何保证客户端可以获取最新的?
HTML页面资源“绝对不能”做强缓存,因为其是页面渲染的入口,所有其他资源也是在渲染解析HTML代码的时候,再去请求的
- 如果服务器某个资源更新,生成新的文件名,html中导入新的文件,webpack搞定
- 或者文件名一样,但是一更新,会在后面拼接一个新的时间戳
协商缓存:哪怕本地有缓存,也要去和服务器进行协商
第一次请求页面,本地没有缓存,向服务器发送请求,如果需要设置协商缓存,服务器会在响应头中返回:
Last-Modified(记录资源最后一次更新时间)/ETag(记录资源最后一次更新的标识);客户端浏览器看到标识后,把标识和资源都缓存起来
第二次请求,也会向服务器发送请求,并且在请求头中,基于If-Modified-Since/If-None-Match分别把存储的Last-Modified/ETag值传递给服务器,服务器获取标识后,和本地资源(最新更新时间/标识)作对比- 如果资源没有更新过,则时间/标识是一致的,服务器返回304;客户端发现是304,则从本地缓存中获取
- 如果资源更新了,则时间/标识是不一致的,服务器返回200和最新的内容以及最新的Last-Modified/ETag,客户端把最新的信息存储起来
好处:保证服务器资源一旦更新,就会发现。
真实项目开发:建议大家:HTML只设置协商缓存,其余资源强缓存和协商缓存都去设置(都是由服务器设置的)
强缓存没有失效,不会在请求头携带那些东西,协商缓存不会生效。
数据缓存:前端开发者,基于本地存储方案,对于不经常更新的数据请求,做的缓存处理
- 第一次从服务器获取数据,存储在本地[设置一个有效周期]
本地存储方案:受“源”和“浏览器”的限制- cookie
具备有效期,设置cookie的时候自己设置
最多只允许存储4kb
cookie和服务器:在非跨域请求下,如果本地有cookie,不论服务器是否需要,每一次发送请求,都默认把cookie基于请求头传递给服务器[好处:如果需要把cookie传递给服务器,比较省事;坏处:如果cookie比较多,每一次请求都携带大量的信息进行传递,把请求变慢]
cookie不稳定:清除历史记录,可能会清掉cookie,浏览器的无痕模式,不会记录cookie - localStorage
持久化存储,除非自己手动删除或者写在浏览器
最多允许存储5mb的内容
localStorage默认和服务器是没有关系,除非自己手动把它存的信息传递给服务器,否则就是客户端存储
localStorage相对比较稳定一些 - sessionStorage
会话存储,页面刷新存储的信息会在;页面关闭,存储的信息会消失 - 虚拟内存存储[例如:全局变量,vuex]
页面刷新或者关闭,存储的信息都会释放(内存释放了)
- cookie
- 以后再发送请求,我们校验本地是否有此数据的缓存(而且是否在有效期内)
- 如果没有:向服务器发送新的请求,获取最新的数据
- 如果有:直接从本地存储的数据中拿出来渲染
//设定具备有效期的localStorage存储方案
const storage = {
set(key, value) {
localStorage.setItem(key, JSON.stringify({
time:+new Date(),
value,
}));
},
get(key, cycle=60000) {
//默认1分钟
cycle = +cycle;
if(isNaN(cycle)) cycle = 6000;
let data = localStorage.getItem(key);
if(!data) return null;
let {time, value} = JSON.parse(data);
if((+new Date() - time) > cycle) {
//超过有效期 也不获取值
storage.remove(key);
return null;
}
//在有效期内,才获取值
return value;
},
remove(key) {
localStorage.removeItem(key);
}
}
第三步 DNS解析[域名解析]
浏览器地址栏输入域名,会去DNS服务器上找到对应的外网IP,然后基于服务器的外网IP,找到服务器
DNS解析本身也有缓存,DNS解析记录也会有缓存[谷歌浏览器大概记录1min]
前端优化:尽可能减少DNS解析需要的时间
- 正常:减少DNS解析的次数(也就是所有资源,尽可能都部署到,相同服务器的相同服务下)
- 真实:我们会把不同资源分不同的服务器部署[web服务器 图片服务器 数据服务器]
- 合理使用资源
- 提高并发数
导致域名解析的次数增加,DNS解析需要的时间也会增加
DNS Prefetch:DNS预解析,利用的异步性,在GUI渲染过程中,同时去解析域名
第四步 TCP的三次握手[让客户端和服务器端建立一个’稳定可靠’的传输通道]
- TCP:稳定可靠,因为经过三次握手确定传输的稳定性,消耗时间久
- UDP:快速传输,省略三次握手的步骤,直接进行传输,可能存在丢包的情况
第五步 数据传输
- HTTP请求报文:请求头、请求起始行、请求主体
- HTTP响应报文:响应头、响应起始行、相应主题
在控制台的NetWork中都可以看到
第六步 TCP的四次挥手[断开客户端和服务器端的链接通道]
- HTTP/1.1版本开始,默认了开启Connection:keep-alive长连接,当前请数据传输完成后,建立的通道不会被立即释放,下一次请求,继续基于这个通道传输,减少了TCP四挥三卧的规程,加快数据传输的速度
第七步 页面渲染
- DOM TREE
- CSSOM TREE
- RENDER TREE
- Layout [回流/重排]
- 分层
- painting
性能优化汇总
1.利用缓存
- 对于静态资源文件实现强缓存和协商缓存(扩展:文件有更新,如何保证及时刷新?)
- 对于不经常更新的接口数据采用本地存储做数据缓存(扩展:cookie / localStorage / vuex|redux 区别?)
2.DNS优化
- 分服务器部署,增加HTTP并发性(导致DNS解析变慢)
- DNS Prefetch
3.TCP的三次握手和四次挥手
- Connection:keep-alive
4.数据传输
- 减少数据传输的大小
- 内容或者数据压缩(webpack等)
- 服务器端一定要开启GZIP压缩(一般能压缩60%左右)
- 大批量数据分批次请求(例如:下拉刷新或者分页,保证首次加载请求数据少)
- 减少HTTP请求的次数
- 资源文件合并处理
- 字体图标
- 雪碧图CSS-Sprit
- 图片的BASE64
- …
5.CDN服务器“地域分布式”
6.采用HTTP2.0
- 网络优化是前端性能优化的中的重点内容,因为大部分的消耗都发生在网络层,尤其是第一次页面加载,如何减少等待时间很重要“减少白屏的效果和时间”
- LOADDING 人性化体验
- 骨架屏:客户端骨屏 + 服务器骨架屏
- 首屏数据由服务器渲染 SSR
- 其他屏的数据还是客户端渲染,在没有获取到数据之前,我们基于灰色的框框占位,拿到数据之后替换掉
- 图片延迟加载
- …