主讲:云隐
一、回到那道面试题
面试题: 从
url输入到页面展示发生了什么?
从输入开始
输入的是 url;
-
URL:url- 资源定位符 -
url的三种形式:-
以
http开头:http://www.zhaowa.com,http协议;-
追问:
http与TCP?-
http是 应用层 的网络协议,TCP是 传输层 的协议; -
关联:
http是基于TCP实现连接,http在发起请求之前,首先会建立一个对接服务器的通道,并且发送请求,发送请求结束之后会断开连接,其中的 建立、发送、断开 是TCP连接;-
http是如何实现连接的?http在发送请求的时候,是依赖TCP建立起来的连接通道,并且发送请求,请求完成之后会断开这个连接; -
优化点: 首先要知道
http的版本:1.0、1.1、2.0-
UDP vs TCP区别:UDP是保障速度,面向连接的,TCP是面向确认的; -
http从1.1版本开始增加了keep-alive,作用是 保持TCP的连续畅通,不用反复地建立连接;- 在
1.1版本中,如果同时发送10条请求,在浏览器的瀑布流中可以观察到,一次性只会同时并发6条,如果其中有一条结束了,会立即补充一条,直到全部请求完成,说明了1.1版本中不会复用TCP通路,浏览器同时并发请求几条,就会建立几条TCP连接,而Chrome浏览器限定了最大的并发数量,就是6条;
- 在
-
1.1和2.0版本的区别:2.0多条并发请求复用同一条通路(复用通路,无并发限制);- 在
2.0版本中,如果同时发送10条请求,那么这10条的请求的开始时间是一致的;
- 在
-
-
-
差异点:
http是无状态的连接,TCP是有状态的;- 优化点: 使用
socket连接,socket本质上不是一个协议,是封装化的TCP。让我们的应用,更加方便地使用调用
- 优化点: 使用
-
-
-
以
https开头:https://www.zhaowa.com,https协议;- 追问:
http与https?https是什么:http + SSl(TLS),位于TCP协议与各种应用层协议之间;- 实现原理:原理图;
https多次连接:1、导致网络请求加载时间延长;2、增加开销和功耗;- 优化点: 合并请求、长连接
- 使用中间层(
node层)处理合并请求,或者在前端代码层把相同类型的请求合并起来; - 注意点:中间层在整合请求时,对于异常的处理怎么解决;
- 使用中间层(
- 优化点: 合并请求、长连接
- 追问:
-
以
file开头:file:///C:/Users/class/zhaowa/document,本地文件目录地址,只在本机打开;
-
域名解析
-
域名解析的顺序:
- 浏览器缓存中:浏览器中会缓存
DNS一段时间; - 系统缓存:系统中找缓存,系统中没有就去找
HOST文件; - 路由器缓存:各级路由器缓存域名信息;
- 运营商地方站点的缓存信息【到这里,基本上就可以找到了】;
CDN存在的阶段,CDN服务提供商(partner)会在这个阶段部署很多CDN节点;
- 根域名服务器;
- 浏览器缓存中:浏览器中会缓存
-
优化:
CDN - Content Delivery Network- 为同一个主机配置多个 IP 地址;
LB- 负载均衡;
当聊到 CDN 的时候,大多数时候会被问到缓存:缓存 => 各级缓存 => 浏览器区分缓存(协商缓存和强缓存)。
web 服务器
常用服务器:apache、ngnix 等等;
-
接收请求 => 传递给服务端代码;
-
通过反向代理 => 传递给其他服务器;
-
不同域名 => 指向相同
ip的服务器 => 通过ngnix域名解析 => 引导到不同的服务监听端口;
二、服务涉及到网络优化
手写并发 - QPS
面试题:【并发优化】10 个请求,由于后台或者业务需求只能同时执行三个
分析:
- 输入:
promise数组、limit参数; - 存储:
reqpool- 并发池; - 思路:塞入 + 执行;
function qpsLimit(requestPipe, limitMax = 3) {
debugger;
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);
console.log(_finish);
_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,避免reflowdisplay: none; => reflowvisibility: hidden; => repaint
四、脚本执行时 - JS
垃圾回收:mark & sweep 方案 => 触达标记,锁定清空、未触达直接抹掉。
// 内存分配:申明变量、函数、对象
// 内存使用:读写内存
// 内存释放
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;
}
// 内存泄露
// 1、莫名其妙的全局变量
function foo() {
bar1 = '';
this.bar2 = '';
}
// 2、未清理的定时器
setInterval(() => {}, 1000);
// 3、使用后的闭包
function zhaowa() {
let _no = 1;
return () => {
return _no++;
};
}
五、打包配置优化
- 懒加载:非必要不加载;
- 按需引入:非必要不引入;
- 抽离公共:相同项目合并公用;