前后端交互模型(一个面试题引发的思考总结)
客户端和服务端的交互
面试题: 当用户在地址栏中输入网址,到最后看到页面,中间都经历了什么? (引出前后端交互模型的内容)
客户端 =======> 服务端 (request请求阶段)
服务端 <======= 客户端 (responese响应阶段)
1.URL地址解析
2.DNS域名解析(DNS服务器)
3.和服务器建立TCP连接 (三次握手)
4.把客户端信息传递给服务器(发送HTTP请求)
5.服务器得到并处理请求(HTTP响应内容)
6.客户端渲染服务器返回的内容
7.和服务器端断开TCP连接(四次挥手)
**下面分别由这7个点来展开说明**
----------------------------------------------------------------
(补充说明)服务器接收到请求后:
1.根据端口号找到对应的项目
2.根据请求资源的路径名称找到资源文件
3.读取资源文件中的内容
4.把内容返回
一.URL地址解析
1.1 URI / URL / URN
URL: Uniform Resource Locator 统一资源定位符 (根据这个地址可以找到一个地方)
URN: Uniform Resource Name 统一资源名称 (一般看不到)
URI :Uniform Resource Identifier 统一资源标识符,URL和URN是URI的子集
1.2 一个完整的URL所包含的完整的内容
-
协议:http: // (传输协议就是,能够把客户端和服务器通信的信息,进行传输的工具,类似于快递小哥)
- http超文本传输协议
- https更加安全的http,一般涉及支付的网站都要采用https协议(加密传输)
- ftp 文件传输协议 (一般应用于把本地资源上传到服务器)
-
域名:www.xiaohuang.cn (不通过域名,直接用服务器的外网ip地址也能访问,域名就是一个方便记忆的名字)
- 顶级域名 :qq.com (买域名了时候,都是买顶级域名)
- 一级域名:www.qq.com
- 二级域名 :sports.qq.com
-
端口号(:80):端口号的取值范围0~65535 ,用端口号来区分同一个服务器上的不同项目
- http默认端口号:80
- https默认端口号: 443
- 如果项目采用的就是默认端口号,我们在书写地址的时候,不用加端口号,浏览器发送请求的时候会帮我们默认加上
-
请求资源l路径名称:stu/index.html
- 默认的路径或者名称(xxx.com/stu/ 不指定资源名,服务器会找默认的资源,一般默认资源名是default.html , index.html)
- 注意伪URL地址的处理:https://item.jd.com/100005297930.html =>其实是 https://item.jd.com/index.php?id=12344 , ( URL重写技术是为了增加SEO优化的,动态的网址一般不能被搜索引擎优化,所以我们要把动态网址静态化)
-
问号传参信息:?form=wx&lx=1
- 客户端想把信息传递给服务器,有很多种方式:1.URL地址问号传参 2.请求报文传输(请求头和请求主体)
- 也可以不同页面之间的信息交互,例如:从列表到详情
-
hash值:#zhenyu
- 也能充当信息传输的方式
- 锚点定位
- 基于hash实现路由管控(不同的hash值,展示不同的组件和模块)
1.3 URL中特殊字符编码和解码
请求地址中如果出现非有效UNICODE编码内容(空格,中文,特殊字符),现代版浏览器会默认进行编码
-
encodeURI / decodeURI
- 基于encodeURI编码,我们也可以基于decodeURI解码 (这两个都是window下的方法,可以直接调用,下面的方法同理)
-
encodeURIComponent / decodeURIComponent
- encodeURIComponent / decodeURIComponent它相对于encodeURI来说,不用于给整个URL编码,而是给URL部分信息进行编码(一般都是问号传值编码)
-
escape / unescape
- 这种方式一般只用于客户端页面之间自己的处理,例如:从列表跳转到详情,在比如如果我们在客户端中的cookie信息,如果是中文,我们也基于这种办法编码
-
....
二. DNS服务器域名解析
DNS就是域名解析服务器,这个服务器是归全世界的(在服务器上存贮着 域名 ,服务器外网ip的相关记录)
而我们发送请求的时候所谓的DNS解析,其实就是根据域名在DNS服务器上查找对应服务器的外网ip
拿阿里云举例:如何在云服务器上完成域名和外网ip的关联
进入域名管理界面 => 点击解析 => 点击添加记录 => 1.记录类型(ipv4就是外网ip)2.主机记录(www)3.解析路线(默认) 4.记录值 (服务器的具体ip地址) => 确定 (完成了在DNS域名服务器上添加了一个域名关联某个ip地址的记录)
2.0 钓鱼网站
我们常见的钓鱼网站就是,通过技术手段,模拟假DNS服务器,使用户访问某个官方的域名的时候,不是通过DNS服务的解析访问正常ip地址,而是黑客自身的ip地址,然后记录钓鱼网站用户输入的用户名和密码
2.1 DNS优化
-
DNS缓存(一般浏览器会在第一次解析后,默认建立缓存,事件很短,只有一分钟左右)
-
<head> <meta charset="UTF-8"> <!-- DNS预获取 --> <meta http-equiv="x-dns-prefetch-control" content="on"> <link rel="dns-prefetch" href="//static.360buyimg.com"> <link rel="dns-prefetch" href="//misc.360buyimg.com"> </head> DNS预获取:在页面加载开始的时候,就把当前页面中需要访问其他域名(服务器)的信息进行提前DNS解析,以后加载到具体内容部分就不同解析了
-
-
减少DNS解析次数(一个网站中我们需要发送请求的域名和服务器尽可能少即可)
三.建立TCP连接(三次握手)
用大白话来说:
- 第一次握手:有浏览器发起,告诉服务器我要发送请求了
- 第二次握手:有服务器发起,告诉浏览器我准备接受了,你赶紧发把
- 第三次握手: 由浏览器发送,告诉服务器,我马上就发了,准备接受把
具体:
- 第一次,浏览器发送:SYN=1 seq=J (这个J是自定义也可以是A。。)
- 第二次,服务器返回:SYN=1 ACK=1 ack = J+1 ,seq=K
- 第三次, 浏览器发送:ACK=1 ack=K+1
就是通过简单的三次握手,用代码的方式,表达能够相互通信 (就是在真正发送请求之前,临时验证能否正常通信,建立好通信通道)
四.发送HTTP请求
就是把需要发送的数据,通过建立的TCP通道,传输到服务器 (TCP类比通道,HTTP类比快递员)
4.1 HTTP请求报文
- 起始行
- 请求头 Headers(首部)
- 请求主体
4.2 强缓存 和 协商缓存
这部分另外单独分析
- 强缓存 (Cache-Control 和 Expires)
- 协商缓存 (Last - Modified 和 Etag)
五.服务器响应HTTP内容
一般来说,web服务器和数据服务器在同一个服务器的相同服务下(协议,域名,端口都一致),这种就是同源策略请求 =>AJAX , 但是在真实项目中往往两台服务器是分开的,此时是非同源策略请求 =>跨域
5.1 WEB(图片)服务器和数据服务器
- Tomcat
- Nginx
- Apache
- IIS
5.2 HTTP响应报文
2XX 成功:响应代码表明操作成功了。
3XX 重定向:客户端需要做些额外工作才能得到所需要的资源。它们通常用于GET请求。他们通常告诉客户端需要向另一个URI发送GET请求,才能得到所需的表示。那个URI就包含在Location响应报头里
4XX 客户端错误:这些响应代码表明客户端出现错误。不是认证信息有问题,就是表示格式或HTTP库本身有问题。客户端需要自行改正。
5XX 服务端错误: 这些代码意味着服务器处于不能执行客户端请求的状态,此时客户端应稍后重试。有时,服务器能够估计客户端应在多久之后重试。并把该信息放在Retry-After响应报头里。
- 响应状态码
- 200 ok: 成功
- 201 created: 一般应用于告诉服务器创建一个新文件,最后服务器创建成功后返回的状态码
- 204 no content:对于某些请求(例如:put或者delete),服务器不想处理,可以返回空内容,并且用204状态码告知
- 301 :永久重定向(永久转移) 让客户端使用另外一个url来请求资源
- 302 :临时转移,很早以前基本上用302来做,但是现在主要用307来处理这个事情,307的意思就是临时重定向=> 主要用于:服务器的负载均衡 等
- 304 :设置HTTP的协商缓存
- 400 :传递给服务器的参数错误
- 401:无权限访问
- 404: 请求地址错误
- 500:未知服务器错误
- 503:服务器超负荷
- 响应头(首部)
- 响应主体
六.客户端渲染页面
浏览器渲染页面的时候,遇到link / img /audio / video 等是异步去加载资源信息 (浏览器分配一个新的线程去加载,主线程继续向下渲染页面),如果遇到script或者@import,则让主线程去加载资源信息 (同步),加载完成信息之后,在去渲染页面
6.1 浏览器渲染页面的步骤
- 解析HTML,生成DOM树,解析CSS,生成CSSOM树
- 将DOM树结合CSSOM树结合,生成渲染树(Render Tree)
- Layout(回流):根据生成的渲染树,计算它们在设备视口(viewport)内的确切位置和大小,这个阶段是回流
- Painting(重绘): 根据渲染以及回流得到的几何信息,得到节点的绝对像素
- Display:将像素发送给GPU, 展示到页面上
6.2DOM的重绘与回流
- 重绘:元素样式(颜色)的改变(但宽高,大小,位置不变)
- 回流:元素的大小或者位置发生了变化(当页面布局和几何信息发生变化的时候),触发了重新布局,导致渲染树重新计算布局和渲染
- 注意:回流一定会触发重绘,而重绘不一定会回流
6.3 前端性能优化之:避免DOM的回流
- 放弃传统操作dom的时代,基于vue/react开始数据影响视图模式(因为操作dom必然是会触发回流)
- 分离读写操作(现代的浏览器都有渲染队列的机制)
- =>现代浏览器都有"队列机制":发现某一行要修改元素的样式,不立即渲染,而是看看下一行,如果下一行也会改变样式,则把修改样式的操作放到"渲染队列中"...一直到不是修改样式后的操作后,整体渲染一次,引发一次回流
- 当我们获取一些样式的时候,如box.offsetWidth(偏移量,盒子宽度等),会刷新渲染队列,所以我们在设置样式和获取样式的时候,应该集中书写,而不是交叉书写,做到读写分离
- 样式集中改变
- 缓存布局信息
- 当我们需要多次修改某个属性的时候,不应该获取到了之后直接修改,应该用一个变量存贮获取到的值,再来进行修改 (原理就是读写分离)
- 元素批量修改
- 案例说明:当我们需要往一个盒子中插入10个盒子的时候,错误:循环10次插入(引发回流重绘),正确:使用模板字符串,循环10次,将所有盒子集中到一个字符串中,一次性插入到dom树当中
- 使用文档碎片(存储文档的容器),先插入到文档碎片中,最后将文档碎片插入到页面当中
- 动画效果应用到position属性为absolute或fixed元素上(脱离文档流)
- css3硬件加速(GPU加速)
- 牺牲平滑度换取速度
- 避免table布局和使用css的javascript表达式
七. 断开连接(四次挥手)
四次挥手,是发生在信息的传输过程中,三次挥手,是发生在信息传出之前的
- 第一次挥手:由浏览器发起,发送给服务器,我请求报文发送完了,你准备关闭把
- 第二次挥手:由服务器发起,告诉浏览器,我接受完请求报文了,我准备关闭,你也准备把
- 第三次挥手:由服务器发起,告诉浏览器,我响应报文发送完毕,你准备关闭把
- 第四次挥手:由浏览器发起,告诉服务器,我响应报文接受完毕,我准备关闭,你也准备把
真实传输:
- 第一次:浏览器发出 FIN M
- 第二次: 服务器发出 ack M+1
- 第三次: 服务器再次发出 FIN K
- 第四次:浏览器发出 ACK=1 ack=K+1