**本文已参加[新人创作礼]活动,一起开启掘金创作之路。
**
前言
本系列主要是前端高阶课程的笔记整理,所以在讲述知识点的同时会延伸映射一些面试题,内容之间有点跳跃,但是全文的知识点还是循序渐进的。
今天主要围绕那道非常经典的面试题展开--从输入URL到页面展示经历了什么
在这里置个臀,先放一张今天文章的轴图
输入URL
或许你会愿意看回我的上一篇的文章,这样你就能更清楚知道URL和URI 的区别。
url :资源定位符
作为url,通常有三种形式(当然不是说只有这三种)
http:honggege.com //http协议 [1]追问:http 与TCP有什么关系或者区别
https://honggege.com
//file///C:Users/class/honggege/document //本地文件目录地址,只在本地打开不涉及网络
面试题 :
- http与TCP之间的关系
Q: 首先直面回答面试官问题
1.http是传输层协议,而TCP是应用层的
http -- 传输层 TCP -- 应用层 2.两者的关联: http是基于TCP实现连接的
3.两者的差异:http是无状态连接,而TCP是有状态的
- TCP的三次握手四次挥手
Q:可以看看这篇文章《(建议收藏)TCP协议灵魂之问,巩固你的网路底层基础》
还记得我们今天的主题是什么吗,性能优化,那么我们如何从以上这几点进行性能优化呢
优化
1.TCP有1.0 1.1 2.0 版本,我们优先使用2.0版本
2.根据自身项目需要,选择UDP 协议(面向连接传输,保障速度) 或者TCP协议(面向连接确认,更准确),当然这个是后端大佬的事
3.打开TCP的keep-alive -- 保持TCP 的连续畅通,不用反复地建立连接(只在TCP1.1协议中有)
2.0比较特殊,是因为2.0可多条并发请求复用同一条通路,即复用通路,无并发限制,而1.1不可以
4.使用socket连接. socket是封装化的TCP。让我们的应用,更加方便地使用调用
追问: http和https
1.HTTPS = http + SSL(TLS) => 位于TCP协议与各种应用层协议之间
2.实现原理,可看回文章开头的原理图
你也可以通过这一篇文章再进一步了解https --<看完这篇 HTTPS,和面试官扯皮就没问题>
3.https多次连接 :导致网络请求加载时间延长;增加开销和功耗
优化
合并请求和长连接
域名解析
1.浏览器缓存中-首先会去我们的浏览器缓存中去找我们的DNS信息 -- 浏览器中会缓存DNS信息一段时间
2.系统缓存-如果没有找到,会从系统中找缓存,如果系统中没找到,就会去HOST中找,而我们平时说的"改HOST"就是在改我们服务器的ip跟我们实际请求的服务地址的映射
3.如果系统缓存也没有的话,我们会在路由器缓存去找 -- 各级路由器缓存域名信息
4.运营商地方站点的缓存信息 (80%都能在这层面找到了)
5.实在没有就会知道根域名服务器上了
优化
CDN-- Content Delivery Network
1.为同一个主机配置多个IP地址
2.LB --负载均衡
面试官跟你聊CDN的时候,其实是在跟你聊缓存
在现实中的一些开发场景;用户在打开页面的时候有时候打开的是一个老的页面,那是因为我们在部署静态资源的时候,我们会放在不同的机器上,用户因为负载均衡的规则去命中,去分配到不同的机子,导致有些用户没有更新到所有的点,所以才会说叫用户去清除缓存,重新加载一遍,问题就在于此.
web服务器
解析完域名之后,终于到了web端服务器
在这个阶段主要有这些知识点
// apache、ngnix
// 1. 接收请求 => 传递给服务端代码
// 2. 通过反向代理 => 传递给其他服务器
// 3. 不同域名 => 指向相同ip的服务器 => ngnix域名解析 => 引导到不同的服务监听端口
服务器 涉及到网络优化
面试题: 手写并发 -QPS(涉及后端知识,五年经验题)
// 面试:并发优化 10个请求,由于后台或者业务需求只能同时执行三个
还是那句话,在遇到手写代码面试题时,分析思路更重要,在写代码钱可以先把思路先写下来
输入条件: promise数组 、limit数组
//存储;reqpool -并发池
思路:塞入 + 执行 ,(塞入:往并发池里面添加,直到超出我们限制的数量;执行:当数量到了,并发池满了我们就执行方法)
function qpsLimit(requestPipe, limitMax = 3) {
let reqPool = []
let reqMap = new Map()
// 往并发池里塞入promise
const add = () => {
let _req = requestPipe.shift()
reqPool.push(_req)
}
// 执行实际请求
const run = () => {
if(requestPipe.length === 0) return
// 池子满了发车后,直接race
let _finish = Promise.race(reqPool)
_finish.then(res => {
// 做一个id整理
let _done = reqPool.indexOf(_finish)
reqPool.splice(_done, 1)
add()
})
run()
}
while(reqPool.length < limitMax) {
add()
}
run()
}
浏览器渲染时
浏览器执行顺序 主线:HTML => DOM + CSSOM => renderTree + js => layout => paint 支线: repaint(重绘) - 改变文本、颜色等 展示 reflow(重排或回流) - 元素几何尺寸变了
=> 优化点:减少repaint,避免reflow
reflow => display: none => reflow; visibility:hidden; => repaint
这就是为什么我们平时在说性能优化时说"减少对DOM的操作",其实就是减少repaint
脚本执行时 - JS
mark & sweep => 触达标记,锁定清空、未触达直接抹掉(垃圾回收机制:js大部分都帮我们做了)
// 内存分配:申明变量、函数、对象
// 内存使用:读写内存
// 内存释放
const zhaowa = {
js: {
performance: 'good',
teacher: '云隐'
}
}
// 建立引用关系
const _obj = zhaowa
// 引用源给替换掉了 - 暂未gc
zhaowa = 'best'
// 深入层级做引用 - 暂未gc
const _class = _obj.js
// 引用方替换 - 暂未gc
_obj = 'over'
// gc 完成
_class = null
// => 1. 对象层级,宜平不宜深 2. 深层引用最好深拷贝,或者用完直接销毁 3. 避免循环引用
function traverseTree(node1, node2) {
node1.parent = node2;
node2.children = node1;
}
// 内存泄露
// 莫名其妙的全局变量
function foo() {
bar1 = ''
this.bar2 = ''
}
// 未清理的定时器
setInterval(() => {
}, 1000)
// 使用后的闭包
function zhaowa() {
const _no = 1
return {
number: _no
}
}
打包配置优化
// 1. 懒加载 - 非必要不加载
// 2. 按需引入 - 非必要不引入
// 3. 抽离公共 - 相同项目合并公用