输入URL地址到浏览器完成渲染的整个过程
简单版
1.浏览器地址栏输入URL地址,回车
2.查找缓存:浏览器先查看浏览器缓存-系统缓存-路由缓存中是否有该地址页面,如果有则显示页面内容。如果没有则进行下一步。
3.DNS域名解析:浏览器向DNS服务器发起请求,解析该URL中的域名对应的IP地址。DNS服务器是基于UDP的,因此会用到UDP协议
。
4.建立TCP连接:解析出IP地址后,根据IP地址和默认80端口,和服务器建立TCP连接
5.发起HTTP请求:浏览器发起读取文件的HTTP请求,,该请求报文作为TCP三次握手的第三次数据发送给服务器
6.服务器响应请求并返回结果:服务器对浏览器请求做出响应,并把对应的html文件发送给浏览器
7.关闭TCP连接:通过四次挥手释放TCP连接
8.浏览器渲染:客户端(浏览器)解析HTML内容并渲染出来,浏览器接收到数据包后的解析流程为
-
构建DOM树:词法分析然后解析成DOM树(dom tree),是由dom元素及属性节点组成,树的根是document对象
-
构建CSS规则树:生成CSS规则树(CSS Rule Tree)
-
构建render树:Web浏览器将DOM和CSSOM结合,并构建出渲染树(render tree)
-
布局(Layout):计算出每个节点在屏幕中的位置
-
绘制(Painting):即遍历render树,并使用UI后端层绘制每个节点。
9,JS引擎解析过程:调用JS引擎执行JS代码(JS的解释阶段,预处理阶段,执行阶段生成执行上下文,VO,作用域链、回收机制等等)
高级版
-
浏览器输入URL 输入URL,按下回车,就进入
应用层
往下走,浏览器会开一个线程处理,对URL解析
。 -
应用层DNS解析域名
DNS解析就是对域名或IP解析
- 首先查看浏览器DNS缓存
- 没有就查询计算机本地DNS缓存
- 还没有就询问递归式DNS服务器
-
应用层客户端发送HTTP请求 IP地址有了,
客户端就会发送一个HTTP请求
,HTTP请求分为请求报头和请求主体- 请求报头包含:请求方法(POST,GET等),协议版本号,请求头部方法(Accept、Cache-Control...)
- 请求主体:客户端发送给服务器或者服务器返回给客户端的内容
-
传输层TCP传输报文 客户端在传输层开始和服务器通过
三次握手建立TCP/IP连接
-
网络层IP协议查询MAC地址 网络层IP协议会查询MAC地址进行数据包的传输
-
数据到达数据链路层 找到对方的MAC地址后,会将数据发送到数据链路层传输,到此客户端发送请求阶段就结束了
-
服务器接收数据 服务器在数据链路层接收到数据包,在通过相反的方式将数据一层一层的还原回应用层 请求到后台服务器,会有验证(安全验证、跨域验证等),验证未通过就直接返回相应的HTTP报文,验证通过,就执行对应的操作 如果浏览器访问过,且缓存上有对应的资源,就会与服务器最后修改的时间对比,一致就返回304,告诉浏览器可使用本地缓存
-
服务器响应请求 服务器接收到客户端发送的HTTP请求后,会查找客户端请求的资源,并返回响应报文。
-
服务器返回相应文件 请求成功后,服务器会返回相应的网页,浏览器接收到响应成功的报文后便开始下载网页,网络通信结束
-
解析HTML构建DOM Tree
-
解析CSS构建CSSOM Tree
-
构建渲染树(Render Tree) 将DOM树和CSSOM树合并成渲染树,渲染树只包含渲染网页所需的节点,然后用于计算每个可见元素的布局,并输出给绘制流程,将像素渲染在屏幕上。
-
布局
-
绘制
-
合成
简洁回答
-
首先,客户端浏览器输入URL,由于是域名,应用层DNS开始解析域名
-
接着,应用层客户端发送一个HTTP请求,把拿到的应用层HTTP请求报文数据分割编号,为了方便安全的传输,传输层会通过TCP三次握手建立TCP/IP链接
-
建立连接后
网络层
IP协议会查询服务器MAC地址
也就是物理地址进行数据包的传输 -
找到对方的
MAC地址
后,将数据发送到数据链路层
传输,到此客户端发送请求阶段结束 -
接收端的服务器在
数据链路层
接收到数据包,再通过相反的方式将数据一层一层的还原回应用层
-
服务器接收到客户端发送的HTTP请求后,会查找客户端请求的资源,并返回响应报文
-
请求成功后,服务器会返回相应的网页,浏览器接收到响应成功的报文后便开始下载网页,至此,网络通信结束
-
浏览器拿到网页文件后,首先根据顶部定义的DTD类型进行对应解析方式,网页解析会交给内部GUI渲染线程处理
-
接着构建DOM树和CSSOM树,过程中,如果遇到节点是 JS ,就会调用
JS引擎
对 JS代码进行解释执行,此时由于JS引擎
和GUI渲染线程
互斥,GUI渲染线程
会被挂起,渲染过程停止,如果 JS 代码的运行中对DOM树进行了修改,那么DOM构建要从新开始,然后DOM树和CSSOM树构建为渲染树 -
然后进入布局阶段,计算渲染树节点在设备视口内的确切位置和大小
-
再接着将渲染树中每个节点转换成屏幕上的实际像素,也就是绘制阶段
-
最后的合成阶段浏览器会将各层信息发送给GPU,GPU将各层合成,显示在屏幕上
详细解析
针对第二项解析
1.HTTP缓存:强缓存 2.HTTP缓存:协商缓存
针对三次握手
- 1.第一次握手:
客户端发送syn包(Seq=x)到服务器,并进入SYN_SEND状态,等待服务器确认;(由浏览器发起,告诉服务器我要发送请求了)
- 2. 第二次握手:
服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(Seq=y),即SYN+ACK包,此时服务器进入SYN_RECV状态;(由服务器发起,告诉浏览器我准备接受了,你赶快发送)
- 3. 第三次握手:
客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。(由浏览器发送,告诉服务器,我马上发,准备接受)
为啥需要三次握手??
三次握手”的目的是为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。
三次握手过程中可以携带数据么?
第一次、第二次握手不可以携带数据,因为一握、二握还没有建立连接,会让服务器容易受到攻击。 三次握手,客户端处于ESTABLISHED(已建立连接状态),可以携带数据
发送HTTP请求
发送HTTP请求的过程就是构建HTTP请求报文并通过TCP协议中发送到服务器指定端口 请求报文由请求行,请求报头,请求正文组成
服务器处理请求并返回HTTP报文
状态码、响应报头、响应报文
浏览器解析渲染页面
1.解析HTML形成DOM树 2.解析CSS形成CSSOM树 3.合并DOM树和CSSOM树形成渲染树 4.浏览器开始渲染并绘制页面(回流和重绘)
针对四次挥手
1.第一次挥手:
客户端发送一个FIN,用来关闭客户端到服务端的数据传送,客户端进入FIN_WAIT_1(终止等待1)状态。
2.第二次挥手:
服务端收到FIN包后,发送一个ACK给客户端,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),服务端进入CLOSE-WAIT状态。客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文。
3.第三次挥手:
服务端发送一个FIN,用来关闭服务端到客户端的数据传送,服务端就进入了LAST_ACK(最后确认)状态,等待客户端的确认。
4.第四次挥手:
客户端收到FIN后,客户端进入TIME_WAIT状态,接着发送一个ACK给服务器,确认序号为收到序号+1,服务端进入CLOSED状态,完成四次挥手。
简单理解记忆:(FIN:finish,ACK:acknowledge)
- 客户端------FIN------》服务端
- 服务端------ACK------》客户端
- 服务端------FIN------》客户端
- 客户端------ACK------》服务端
浏览器兼容问题
不同的浏览器,内核和实现方式不同,同一份页面在显示和功能上会存在差异或者错误的情况。
常见的兼容性问题
1、盒模型差异
盒模型是指元素的尺寸由内容、内边距、边框和外边距组成。在标准盒模型中,元素的宽度和高度只包括内容的部分,而在IE盒模型中,元素的宽度和高度还包括内边距和边框的部分。为了解决这个问题,可以使用CSS的box-sizing属性来指定盒模型的类型,将其设置为border-box即可。box-sizing:border-box
2、浮动布局问题
在使用浮动布局时,可能会出现浮动元素高度塌陷或者浮动元素重叠的问题。为了解决这个问题,可以使用清除浮动的方法,例如在浮动元素的父容器中添加一个带有clear:both样式的空元素。
<div class="clearfix"></div>
<style>
.clearfix{
clear:both
}
</style>
3、文字换行问题
不同浏览器对于文字的换行方式可能存在差异,导致文字在不同浏览器中显示不一致。为了解决这个问题,可以使用CSS的word-wrap属性来控制文字的换行方式。word-wrap:break-word
4、行内元素间隙问题
在某些浏览器中,行内元素之间可能存在一些间隙,导致布局不一致。为了解决这个问题,可以使用CSS的font-size: 0属性来消除行内元素之间的间隙。font-size:0
5、CSS3属性兼容问题
CSS3中的一些新属性在不同浏览器中的支持程度可能不一样,导致样式的显示效果不一致。为了解决这个问题,可以使用CSS前缀来指定不同浏览器的私有属性。
-webkit-border-radius:5px;
-moz-border-radius:5px;
border-radius:5px
浏览器跨域
原因
浏览器的同源策略
,即浏览器规定必须协议
、域名
、端口
保持一致时,才算同源。
如何解决跨域
1、跨域资源共享
(CORS)
浏览器将 CORS请求分成两类:简单请求
、非简单请求
简单请求(浏览器直接发出CORS请求【在头信息中增加一个Origin字段】)
满足两个条件,就是简单请求
- 请求方法是三种方法之一:head、get、post
- 2、http的头部信息不超出以下几种字段:
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type
非简单请求(先发出一个预检请求,预检请求方法是OPTIONS,[用来获知服务器是否允许该实际请求])
满足条件:
- 请求方法是put、delete
- Content-Type字段的类型是application/json
2、jsonp
【利用script标签跨域特性,动态创建script标签设置src属性为跨域的url,服务端返回响应数据,通过回调函数返回给客户端】
- 前端代码:[前端动态创建一个`<script>`标签,并将它的`src`属性设置为需要访问的 **API 地址**,同时在 URL 中**携带**一个`callback`参数,参数值为一个客户端定义的回调函数名称,例如`handleResponse`]
<script>
function handleResponse(response) {
console.log(response); // 处理响应数据
}
var script = document.createElement('script');
script.src = 'https://example.com/api?callback=handleResponse';
document.head.appendChild(script);
</script>
- 后端:[根据前端传入的`callback`参数,生成一个包含 JSON 数据的响应,并将回调函数名称和 JSON 数据作为参数包裹在一个函数调用中返回给前端。]
//例如,如果客户端传入的回调函数名称为`handleResponse`,那么后端返回内容则为
handleResponse({"name":"John","age":30})
3、nginx反向代理
4、postMessage跨域
5、设置代理服务器
【在同源策略限制下,可以通过在同域名下的服务器上设置一个代理服务器,将客户端请求转发到目标服务器,再将相应的结果返回给客户端。客户端只需要与代理服务器通信,而不是直接与目标服务器通信,间接实现了跨域请求。】
// App.vue
getName(){
axios.get('http://localhost:8080/name').then(res=>{})
}
// vue.config.js
const {defineConfig} = require('@vue/cli-service')
module.exports = defineConfig({
devServer:{
proxy:''
}
})
两个域名之间的跨域通信(Hash跨域)
背景
假设我们有两个域名为 a.com 和 b.com 的网站,它们需要进行跨域通信,a.com 的页面需要向 b.com 发送一个消息,让 b.com 的页面展示该消息。
解决方案
在 a.com 的页面中,使用JavaScript代码改变 b.com 页面的URL,将一个特定的hash值传递给 b.com 页面
var message = 'Hello, b.com!';
var url = 'http://b.com/#' + encodeURIComponent(message);
window.location.href = url;
在 b.com 的页面中,使用JavaScript代码监听URL的hash值变化,如果发现hash值发生了改变,就获取传递过来的消息,并展示出来。
window.onhashchange = function() {
var message = decodeURIComponent(location.hash.substr(1));
alert(message);
}
浏览器存储数据
Cookie
Cookie
主要被用于用户身份的验证
,以及用户数据的持久性
,主要是为了使无状态的
HTTP在某些特殊场景变的有状态
,主要体现在我们发送网络请求时,Cookie会被请求头携带
,并可以被服务端进行接收
,服务端就可以通过Cookie中存储的一些信息进行验证。
- Cookie的存储大小只有4kb,这是每个HTTP请求都会携带Cookie,所以当我们的存储量过大时会使HTTP请求变慢;
Cookie的一些特性
- Cookie
存储在客户端
,但是服务端和客户端
都可以对其进行读取
、设置
、删除
操作; - Cookie分为
会话Cookie
和持久性Cookie
,前者是没有设置过期时间
,只会在当前会话生命周期内存在,也就是说关掉当前页面,cookie就没了,后者则是设置了过期时间
,只有超过过期时间使时,才会清除; - Cookie是以
文本文件
的格式进行存储的,查看读取十分方便,所以说最好不要
放重要信息
,否则可能会被窃取;
跨域请求中如何携带cookie?
- 在
前端请求
的时候设置request对象的属性withCredentials为true
;- XMLHttpRequest.withCredentials属性是一个布尔值,表示跨域请求时,用户信息是否会包含在请求中,默认为false。
<button id="button">同源请求</button>
<button id="cross-button">跨域请求</button>
const button = document.querySelector("#button");
const crossButton = document.querySelector("#cross-button");
button.onclick = function () {
axios.get("http://localhost:8000/user", {}).then((res) => {})
}
crossButton.onclick = function () {
axios({
withCredentials:true,
method:"get",
url:'http://localhost:8003/anotherService'
}).then((res)=>{})
}
- 服务端在
response
的header
中配置"Access-Control-Allow-Origin", "http://xxx:${port}"
; - 服务端在
response
的header
中配置"Access-Control-Allow-Credentials", "true"
Storage
indexedDB
浏览器安全性
XSS攻击
XSS攻击
就是跨站脚本攻击
,指那些通过在HTML文档中嵌入js脚本
的方式,从而获取到用户的私密信息的操作,它主要有三种,分别是存储型
、反射型
、文档型
。
存储型
:一般指一些输入框没有做js脚本的校验
,导致黑客输入了一些恶意脚本
,然后上传至服务器
,然后客户端拿到这些数据又进行了执行;反射型
:一般指将恶意脚本
作为网络请求的参数
发送给服务器,服务器解析之后拼接到HTML文档中
发送给客户端,客户端进行执行了这些恶意脚本;DOM-based型
:比如一些由用户手动输入然后生成DOM
的网站,攻击者将恶意脚本
注入到页面中,用户输入内容时,恶意脚本篡改内容,最终执行了恶意脚本,达到了攻击的效果。
如何防止XSS攻击
- 我们可以通过
严格校验用户输入内容
,过滤
一些可能发生风险的输入内容,对一些有特殊含义的字符
进行转义
; 利用HttpOnly
,不允许通过浏览器访问cooike,以阻止XSS攻击窃取cookie中的敏感信息;使用CSP(浏览器安全策略)
:只允许当前页面加载指定的
外部资源,可以通过设置http header中的Content-Security-Policy
或者meta标签中的http-equiv属性
。
CSRF
CSRF
就是跨站请求伪造
,黑客诱导用户点击链接进入第三方站点,然后通过用户的cookie信息发起而已请求;
如何防止CSRF攻击
设置samesite
:通过设置cookie的samesite属性
,阻止
向第三方站点携带cookie信息;服务端验证额外信息
:比如可以给客户端token,然后每次发起请求时让客户端携带token
;阻止第三方页面发起请求
:可以让服务端阻止第三方网站请求接口;
点击劫持
点击劫持
是一种视觉欺骗
手段,攻击者将被攻击者的网站
通过iframe
嵌入到自己的网页中,并且将iframe设置透明
,然后放出一个按钮诱导用户点击
;
如何预防点击劫持
X-FRAME-OPTIONS
是一个 HTTP
响应头,在现代浏览器有一个很好的支持。这个 HTTP
响应头 就是为了防御用iframe
嵌套的点击劫持攻击。
该响应头有三个值可选,分别是
DENY
,表示页面不允许通过iframe
的方式展示SAMEORIGIN
,表示页面可以在相同域名下通过iframe
的方式展示ALLOW-FROM
,表示页面可以在指定来源的iframe
中展示
SQL注入
SQL注入
就是利用前端输入框校验漏洞
以及后端SQL语句漏洞
达到攻击目的,比如攻击者可以在输入框输入SQL命令,然后发送给服务端,服务端将SQL命令
作为前端参数进行解析执行,可能会执行一些非自己意愿的SQL语句。
如何预防SQL注入
-
前端严格对输入框内容进行校验,将一些
特殊含义的字符
进行过滤
; -
服务端对
前端参数
也要严格进行校验
,防止一些非法参数的混入;