浏览器相关【笔记】
浏览器内置对象详解
什么是浏览器对象模型
BOM
:Browser Object Model
(浏览器对象模型),浏览器模型提供了独立于内容的、可以与浏览器窗口进行滑动的对象结构,就是浏览器提供的 API- 其主要对象有:
1、window 对象——BOM 的核心,是 js 访问浏览器的接口,也是 ES 规定的 Global 对象
2、location 对象:提供当前窗口中的加载的文档有关的信息和一些导航功能。既是 window 对象属 性,也是 document 的对象属性
3、navigation 对象:获取浏览器的系统信息
4、screen 对象:用来表示浏览器窗口外部的显示器的信息等
5、history 对象:保存用户上网的历史信息
Window 对象
windows 对象是整个浏览器对象模型的核心,其扮演着既是接口又是全局对象的角色
- window 对象的属性和方法
属性 | 说明 |
---|---|
alert() | 系统警告对话框,接收字符串参数并显示 |
confirm() | 系统确认对话框,可提供确认或取消两种事件 |
prompt() | 提示对话框,可对用户展示确认、取消事件外,还可提供文本域 |
open() | 可导航至特定的 url,又可打开一个新的浏览器窗口window.open(要加载的url, 窗口目标, 一个特定字符串, 一个新页面是否取代浏览器历史记录中当前加载页面的布尔值) |
onerror() | 事件处理程序,当未捕获的异常传播到调用栈上时就会调用它,并把错误消息输出到浏览器的 JavaScript 控制上。window.onerror(描述错误的一条消息, 字符串--存放引发错误的JavaScript代码所在的文档url, 文档中发生错误的行数) |
setTimeout() | 超时调用——在指定的时间过后执行代码window.setTimeout(function(){...}, 毫秒) |
setInterval() | 间歇调用——每隔指定的时间就执行一次window.setInterval(function(){...}, 毫秒) |
- 应用场景:
1、确认窗口位置及大小
获取窗口位置的属性与方法
属性 | 说明 | 兼容性 |
---|---|---|
screenLeft | 窗口相对于屏幕左边 的位置 | 适用于IE、Safari、Chrome |
screenTop | 窗口相对于屏幕上边 的位置 | 适用于IE、Safari、Chrome |
screenX | 窗口相对于屏幕左边 的位置 | 适用于Firefox |
screenY | 窗口相对于屏幕上边 的位置 | 适用于Firefox |
moveBy(x,y) | 接收的是在水平和垂直方向上移动的像素数 | 全兼容 |
moveTo(x,y) | 接收的是新位置的x和y坐标值 | 全兼容 |
跨浏览器获取窗口左边和上边位置
var leftPos = (typeof window.screenLeft == 'number') ? window.screenLeft : window.screenX
var topPos = (typeof window.screenTop == 'number') ? window.screenTop : window.screenY
窗口大小属性与方法
属性 | 说明 |
---|---|
innerWidth innerHeight | IE9+、Safari、Firefox、Opera: 该容器中页面视图区的大小 Chrome: 返回视口大小 移动设备: 返回可见视口(即屏幕上可见页面区域的大小) 移动IE浏览器: 不支持该属性,当移动IE浏览器将布局视口的信息保存至document.body.clientWidth 与document.body.clientHeight 中 |
outerWidth outerHeight | IE9+、Safari、Firefox: 返回浏览器窗口本身的尺寸 Opera: 返回页面视图容器的大小 Chrome: 返回视口大小 |
resizeTo(width, height) | 接收浏览器窗口的新宽度与新高度 |
resizeBy(width, height) | 接收新窗口与原窗口的宽度与高度之差 |
2、导航和打开窗口
属性 | 说明 |
---|---|
open(url,[target,string,boolean]) | url: 要加载的URL target: 窗口目标 string: 特定的字符串,以逗号分隔的字符串表示新窗口显示的特性 boolean: 表示新页面是否取代浏览器历史记录中当前加载页面的布尔值 |
3、定时器
setTimeout()
与setInterval()
都是由于 JavaScript 的语言特性所产生的,由于 JavaScript 是一个单线程的解释器,因此一定时间内只能执行一段代码,为了控制要执行的代码,便有了一个 JavaScript 队列。这些任务会按照将他们添加到队列的顺序执行。setTimeout()
和setInterval()
的第二个参数会告诉 JavaScript 再过多长时间把当前任务添加到队列中。如果队列是空的,那么添加的代码会立即执行;如果队列不是空的,那么它就要等前面的代码执行完了之后再执行。- 两者在被调用之后,都会返回一个数值 ID,这个 ID 可以用来取消对该方法的调用
下面是一段倒计时的代码
<!-- 使用setInterval()来执行倒计时 -->
var num = 10
var interval = setInterval(function(){
num --
console.log(num)
if(num == 0) {
clearInterval(interval)
console.log('Done')
}
}, 1000)
<!-- 使用setTimeout()来执行倒计时 -->
var num = 10
var timeout = function(){
num--
console.log(num)
if(num > 0){
setTimeout(timeout, 1000)
}else {
console.log('Done')
}
}
setTimeout(timeout,1000)
上面使用两种方法都可以进行倒计时,但是使用 `setInterval()` 方法的时候,再不加干涉的情况下,该方法会一直执行到页面的卸载,所以一般情况下`serInterval()`比较消耗性能。然后`setTimeout()`方法可以通过调用自身完成间歇调用的功能。所以说,在一般情况下使用`setTimeout()`来完成超时与间歇调用。
Location 对象
提供当前窗口中的加载的文档有关的信息和一些导航功能。既是
window
对象属性,也是document
的对象属性
location
对象的主要属性:
属性名 | 例子 | 说明 |
---|---|---|
hash | " #host " | 返回 url 中的 hash(#后字符>=0) |
host | " juejin.im:80 " | 服务器名称+端口(如果有) |
hostname | " juejin.im " | 只含服务器名称 |
href | " https://juejin.im/book/5a7bfe… " | 当前加载页面的完整的 url |
pathname | " /book/5a7bfe595188257a7349b52a " | 返回 url 的的目录和(或)文件名 |
port | " 8080 " | url 的端口号,如果不存在则返回空 |
protocol | " https: (or http:)/) " | 页面使用的协议 |
search | " ?name=aha&age=20 " | 返回 url 的查询字符串, 以问号开头 |
-
location 的应用场景:
1、解析 url 查询字符串参数,并将其返回一个对象,可通过循环、正则来实现,方法有很多,实现的大体思路是:
通过location
的search
属性来获取当前 url 传递的参数,如果 url 中有查询字符串的话就将其问号截取掉,然后再遍历里面的字符串并以等号为断点,使用decodeURIComponent()
方法来解析其参数的具体数值,并将其放在对象容器中,并将其返回
2、载入新的文档,也可以说是刷新页面,主要有三个方法:assign()
: location.assign(“http://www.xxx.com”)就可立即打开新 url 并在浏览器是我历史中生成一条新的记录, 在一个生成了 5 条浏览记录的页面中,然后使用 assign()跳转 url 后,history 记录只剩两条,一条是通过 assign 跳转的页面,另一条则是上一个页面(使用 assign()跳转方法的页面),其余的所有页面都被清除掉了replace()
: location.replace(“http://www.bbb.com”)只接受 url 一个参数,通过跳转到的 url 界面不会在浏览器中生成历史记录,就是 history 的 length 不会+1,但是会替代掉当前的页面reload()
: 其作用是重新加载当前显示的页面,当不传递参数的时候,如果页面自上次请求以来并没有改变过,页面就会从浏览器中重新加载,如果传递true
,则会强制从服务器重新加载
Navigation 对象
navigation 接口表示用户代理的状态和标识,允许脚本查询它和注册自己进行一些活动
- navigation 对象的属性方法
- navigation 应用场景
- 检测插件
- 注册处理程序
Screen 对象
其提供有关窗口显示的大小和可用的颜色输入信息。
- screen 对象的属性和方法
History 对象
history 对象保存着用户上网的历史记录,从窗口被打开的那一刻算起,history 对象是用窗口的浏览历史用文档和文档状态列表的形式表示。history 对象的 length 属性表示浏览历史列表中的元素数量,但出于安全考虑,脚本不能访问已保存的 url
- History 对象的属性及方法
属性 | 说明 |
---|---|
go() | 1、以在用户的历史记录中任意跳转,go(n) 表示前进 n 页, go(-n) 表示后退 n 页(n>0) 2、go() 可以传递字符串参数,浏览器历史中如果有这条 url 则实现跳转至包含该字符串的第一个位置,否则什么也不做 |
back() | 后退一页 go(-1) |
forword() | 前进一页 go(1) |
length | 保存历史记录的数量,可用于检测当前页面是否是用户历史记录的第一页(history.length === 0) |
详解浏览器事件捕获,冒泡
- 浏览器事件模型中的过程主要分为三个阶段:捕获阶段、目标阶段、冒泡阶段。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="parent" class="flex-center">
parent
<p id="child" class="flex-center">
child
<span id="son" class="flex-center">
son
<a href="https://www.baidu.com" id="a-baidu">点我啊</a>
</span>
</p>
</div>
</body>
<script src="index.js" type="text/javascript"></script>
<style>
#parent{
background-color: bisque;
width: 700px;
height: 700px;
}
#child {
background-color: chocolate;
width: 500px;
height: 500px;
}
#son {
background-color: crimson;
width: 300px;
height: 300px;
}
.flex-center {
display: flex;
justify-content: center;
align-items: center;
font-size: 20px;
}
</style>
</html>
第三个参数
- 这里要注意
addEventListener
的第三个参数, 如果为true
,就是代表在捕获阶段执行。如果为false
,就是在冒泡阶段进行
阻止事件传播
e.stopPropagation()
【阻止事件的传播】大家经常听到的可能是阻止冒泡,实际上这个方法不只能阻止冒泡,还能阻止捕获阶段的传播。stopImmediatePropagation()
如果有多个相同类型事件的事件监听函数绑定到同一个元素,当该类型的事件触发时,它们会按照被添加的顺序执行。如果其中某个监听函数执行了event.stopImmediatePropagation()
方法,则当前元素剩下的监听函数将不会被执行。
阻止默认行为
e.preventDefault()
可以阻止事件的默认行为发生,默认行为是指:点击a
标签就转跳到其他页面、拖拽一个图片到浏览器会自动打开、点击表单的提交按钮会提交表单等等,因为有的时候我们并不希望发生这些事情,所以需要阻止默认行为
const baidu = document.getElementById('a-baidu')
baidu.addEventListener('click', function (e) {
e.preventDefault()
})
场景设计题
现在有一个页面,这个页面上有许多元素,
div
p
button
每个元素上都有自己的click
事件,都不相同
现在来了一个新的需求,一个用户进入这个页面的时候,会有一个状态banned
window.banned
:
true
: 当前用户被封禁了,用户点击当前页面上的任何元素,都不执行原有的click
逻辑,而是alert
弹窗,提示你被封禁了!
false
: 不作任何操作
方法1:
window.addEventListener('click', function (e) { }, true)
banned
为true
时在window
的 捕获阶段 阻止事件的传播
方法2:写一个全屏的 最高层级的元素,遮罩住整个页面
const parent = document.getElementById('parent')
const child = document.getElementById('child')
const son = document.getElementById('son')
const banned = true
window.addEventListener(
'click',
function (e) {
if (banned) {
e.stopPropagation()
alert('你被封禁了!')
return
}
// e.target 当前点击的的元素
// e.currentTarget 绑定时间监听的元素
console.log('window 捕获', e.target.nodeName, e.currentTarget.nodeName)
},
true
)
parent.addEventListener(
'click',
function (e) {
e.stopPropagation()
console.log('parent 捕获', e.target.nodeName, e.currentTarget.nodeName)
},
true
)
child.addEventListener(
'click',
function (e) {
console.log('child 捕获', e.target.nodeName, e.currentTarget.nodeName)
},
true
)
son.addEventListener(
'click',
function (e) {
console.log('son 捕获', e.target.nodeName, e.currentTarget.nodeName)
},
true
)
son.addEventListener('click', function (e) {
console.log('son 冒泡', e.target.nodeName, e.currentTarget.nodeName)
})
child.addEventListener('click', function (e) {
console.log('child 冒泡', e.target.nodeName, e.currentTarget.nodeName)
})
parent.addEventListener('click', function (e) {
console.log('parent 冒泡', e.target.nodeName, e.currentTarget.nodeName)
})
window.addEventListener('click', function (e) {
console.log('window 冒泡', e.target.nodeName, e.currentTarget.nodeName)
})
/**
window 捕获 SPAN undefined
parent 捕获 SPAN DIV
child 捕获 SPAN P
son 捕获 SPAN SPAN
son 冒泡 SPAN SPAN
child 冒泡 SPAN P
parent 冒泡 SPAN DIV
window 冒泡 SPAN undefined
*/
兼容性
attachEvent
—— 兼容:IE7、IE8; 不支持第三个参数来控制在哪个阶段发生,默认是绑定在冒泡阶段addEventListener
—— 兼容:firefox、chrome、IE、safari、opera;
封装一个多浏览器兼容的绑定事件函数
class BomEvent {
constructor(element) {
this.element = element
}
addEvent(type, handler) {
if (this.element.addEventListener) {
this.element.addEventListener(type, handler, false)
} else if (this.element.attachEvent) {
this.element.attachEvent(`on${type}`, handler)
} else {
this.element[`on${type}`] = handler
}
}
removeEvent(type, handler) {
if (this.element.removeEventListener) {
this.element.removeEventListener(type, handler, false)
} else if (this.element.detachEvent) {
this.element.detachEvent(`on${type}`, handler)
} else {
this.element[`on${type}`] = null
}
}
}
function stopPropagation(ev) {
if (ev.stopPropagation) {
ev.stopPropagation() // 标准 w3c 浏览器
} else {
ev.cancelBubble = true // IE
}
}
function preventDefault(ev) {
if (ev.preventDefault) {
ev.preventDefault() // 标准 w3c 浏览器
} else {
ev.returnValue = false // IE
}
}
绑定事件的运用
大家常见的一个面试题可能是ul + li,点击每个li alert对应的索引,这里就给大家来写一下看看
-
先来给每个li绑定事件
-
再来写一个事件委托的方式
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> </head> <body> <ul id="ul"> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> <li>6</li> <li>7</li> <li>8</li> </ul> </body> <script type="text/javascript"> const ul = document.querySelector("ul"); ul.addEventListener("click", function (e) { const target = e.target; if (target.tagName.toLowerCase() === "li") { const liList = this.querySelectorAll("li"); index = Array.prototype.indexOf.call(liList, target); alert(`内容为${target.innerHTML}, 索引为${index}`); } }); // const liList = document.getElementsByTagName("li"); // for (let i = 0; i < liList.length; i++) { // liList[i].addEventListener("click", function (e) { // alert(`内容为${e.target.innerHTML}, 索引为${i}`); // }); // } </script> <style> #ul { background-color: gray; width: 700px; position: relative; display: flex; flex-direction: column; align-items: center; } li { margin-bottom: 20px; width: 80%; height: 100px; display: flex; justify-content: center; align-items: center; background-color: lightgoldenrodyellow; font-size: 20px; font-weight: bold; } </style> </html>
ajax 及 fetch API 详解
1. XMLHTTPRequest
const xhr = new XMLHttpRequest()
xhr.open('GET', 'http://domain/service')
// 要在发送请求前注册 不然可能会出现请求响应过快而注册未完成的情况
xhr.onreadystatechange = function () {
if (xhr.readyState !== 4) {
return
}
if (xhr.status === 200) {
console.log(xhr.responseText)
} else {
console.error(
`HTT{ error, status=${xhr.status}, errorText=${xhr.statusText}`
)
}
}
// 超时处理
xhr.timeout = 3000
xhr.ontimeout = () => {
console.log('当前请求超时啦!!')
}
// 文件上传
xhr.upload.onprogress = (p) => {
const percent = Math.round((p.loaded / p.total) * 100) + '%'
}
// 发送请求
xhr.send()
2. fetch
-
默认不带
cookie
-
错误不会
reject
-
不支持超时设置
-
需要借用
AbortController
中止fetch
fetch('http://domain/service', { method: 'GET', // 默认不带cookie credentials: 'same-origin' // 同域的请求需携带cookie }) .then((response) => { // 错误不会reject // HTTP错误(例如404 Page Not Found 或 500 Internal Server Error)不会导致Fetch返回的Promise标记为reject;.catch()也不会被执行。 // 想要精确的判断 fetch是否成功,需要包含 promise resolved 的情况,此时再判断 response.ok是不是为 true if (response.ok) { // 请求成功 return response.json } throw new Error('http error') }) .then((json) => { console.log(json) }) .catch((error) => { console.log(error) }) // 不支持直接设置超时, 得自己实现, 可以用promise function fetchTimeout(url, init, timeout = 3000) { return new Promise((resolve, reject) => { // 下面2行代码是同步代码 几乎同时执行 fetch(url, init).then(resolve).catch(reject) // 1. 不超时 上面是fulfilled 毫无意义 // 2. 超时 setTimeout 的回调先被执行 setTimeout(reject, timeout) }) } // 中止fetch const controller = new AbortController() fetch('http://domain/service', { method: 'GET', credentials: 'same-origin', signal: controller.signal }) .then((response) => { if (response.ok) { return response.json } throw new Error('http error') }) .then((json) => { console.log(json) }) .catch((error) => { console.log(error) }) // 中断 controller.abort()
3. request header 请求头
:method: GET
:path:
:scheme: https
accept: application/json, text/plain, ****/****
accept-encoding: gzip, deflate, br
cache-control: no-cache
cookie: deviceId=c12;
origin:
referer:
user-agent: Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1
- referer:当前请求页面的来源页面的地址,即表示当前页面是通过此来源页面里的链接进入
- user-agent:识别发起请求的用户代理软件的应用类型、操作系统、软件开发商以及版本号
面试题1:为什么常见的 cdn 域名 和 业务域名不一样?
- 例:
www.baidu.com
业务域名cdn.baidu-aa.com
cdn 域名
- 答案:
- 安全问题
- cookie 中携带用户身份信息,公司不希望将该信息暴露给cdn厂商
- 增加资源消耗/增加带宽
- 请求cdn资源的时候,如果同域名,会无意间携带cookie,每一个静态资源无谓携带cookie
- 并发请求数
- 安全问题
4. response header 响应头
access-control-allow-credentials: true
access-control-allow-origin:
content-encoding: gzip
content-type: application/json;charset=UTF-8
date: Thu, 06 Aug 2020 08:15:05 GMT
set-cookie: sess=QvrAQ0Cq+EcDQQPTer2X;
status: 200
- access-control-allow-origin:* 或者 http://www.baidu.com
- 指定具体域名 或者 * 不做限度
- content-encoding:gzip
- 用于对特定媒体类型的数据进行压缩
- set-cookie:userId=xxx
- 由服务器端向客户端发送 cookie
5. status
- 200 get 成功
- 201 post 成功
- 301 永久重定向
- 302 临时重定向
- 304 协商缓存 服务器文件未修改
- 强缓存
- max-age 毫秒数,接管cookie多少毫秒后失效
- expired 代表什么时候过期,机器时间改变之后cookie有效性会产生偏差
- max-age:600 // 单位是秒,返回是相对时间 (相比expires更加精确)
- expires:Wed, 21 Oct 2021 07:28:00 GMT // 响应头包含日期/时间,即在此时间之后,相应过期(返回具体时间)
- 协商缓存 浏览器和服务器去协商
- last-modified:Wed, 21 Oct 2021 07:28:00 GMT // 通过判断上次修改的时间是什么时候
- 判断当前文件是否被修改
- 问题:打开关闭文件而不做任何修改,last-modified还是会改变
- 缺点:
- 1、最小单位是秒。也就是说如果短时间内资源发生了改变,Last-Modified并不会发生变化
- 2、周期性变化。如果这个资源在一个周期内修改回原来的样子了,我们认为文件是没有变化的是可以使用缓存的,但是
Last-Modified
记录的是上次修改时间,即使文件没有变化,但修改时间变了,所以它认为缓存失效
- Etag:33a64df551425fcc55e4d42a148795d9f25f89d4
- 为了解决Last-Modified不准确的问题,后面引入了Etag,Etag一般是由文件内容hash生成的,也就是说它保证资源的唯一性,资源发生改变就会导致Etag发生变化,在浏览器第一次请求资源时,服务器会返回一个Etag标识。当再次请求该资源时,会通过 If-no-match 字段将 Etag 发送回服务器,然后服务器进行比较,如果相等,则返回304表示未修改;
- etag的缺点是:所有内容进行hash,比较消耗性能
- last-modified:Wed, 21 Oct 2021 07:28:00 GMT // 通过判断上次修改的时间是什么时候
- 强缓存
- 400 客户端请求有语法错误,不能被服务器识别
- 403 服务器受到请求,但是拒绝提供服务,可能是跨域
- 404 请求的资源不存在
- 405 请求的method不允许
- 500 服务器发生不可预期的错误
面试题2:vue/react这种单页面应用,都会存在一个 index.html 文件,这个也是所谓的单页,针对 index.html 文件,如果非要做缓存的话,适合做什么缓存?
答案:协商缓存,因为打包生成后,会加入各种script、link标签,也就是引入js、css文件,而这些js/css都是被hash的,目的为了防止被缓存,而index.html是没有hash的,由于版本迭代的速度快,js/css经常被修改。如果用强缓存,不能被实时修改。
ps: 一般不做缓存 no-cache,文件体积非常小(700B)
6. 发送请求的示例,以及封装一个多浏览器兼容的请求函数
interface IOptions {
url: string
type?: 'GET' | 'POST'
data: any
timeout: number
}
function formatUrl(object) {
// a=xxx&b=xxxx; queryString
let dataArr = []
for (let key in object) {
dataArr.push(`${key}=${encodeURIComponent(object[key])}`)
}
return dataArr.join('&')
}
export function ajax(
options: IOptions = {
type: 'GET',
data: {},
timeout: 3000,
url: ''
}
) {
return new Promise((resolve, reject) => {
if (!options.url) {
return
}
const queryString = formatUrl(options.data)
const onStateChange = () => {
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
clearTimeout(timer)
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
resolve(xhr.responseText)
} else {
reject(xhr.status)
}
}
}
}
let timer
let xhr
if ((window as any).XMLHttpRequest) {
xhr = new XMLHttpRequest()
} else {
xhr = new ActiveXObject('Microsoft.XMLHTTP')
}
if (options.type.toUpperCase() === 'GET') {
xhr.open('GET', `${options.url}?${queryString}`)
onStateChange()
xhr.send()
} else if (options.type.toUpperCase() === 'POST') {
xhr.open('POST', options.url)
xhr.setRequestHeader('ContentType', 'application/x-www-form-urlencoded')
onStateChange()
xhr.send(options.data)
}
if (options.timeout) {
timer = setTimeout(() => {
xhr.abort()
reject('timeout')
}, options.timeout)
}
})
}
http的一些问题
HTTP 1.0/1.1/2.0在并发请求上主要区别是什么?
-
HTTP/1.0
- 每次TCP连接只能发送一个请求,当服务器响应后就会关闭这次连接,下一个请求需要再次建立TCP连接。
-
HTTP/1.1
- 默认采用持久连接(TCP连接默认不关闭,可以被多个请求复用,不用声明Connection:keep-alive)。
- 如果想关闭持久连接,变成HTTP/1.0的话,设置Connection:close;
- 增加了管道机制,在同一个TCP连接里,允许多个请求同时发送,增加了并发性,进一步改善了HTTP协议的效率。(一问一答的形式)
- 所有的数据通信是有顺序的,A B C,A先到达服务器开始响应,响应结束后才能进行下一个请求。所以,同一个TCP连接里,所有的数据通信是按次序进行的。如果有响应很慢的,会有许多请求排队,造成“队头阻塞”;
-
HTTP/2.0
- 加了双工模式,不仅客户端能够同时发送多个请求,服务器也能同时处理多个请求,解决了队头阻塞的问题。
- 使用了多路复用的技术,没有次序的概念了。做到同一个连接并发处理多个请求,而且并发请求的数量比 HTTP/1.1 多得多。
- 增加服务器推送的功能,不经请求服务端主动向客户端发送数据。
问:HTTP/1.1的长连接和2.0的多路复用有什么区别?
-
1.1:同一时间一个TCP连接只能处理一个请求,采用一问一答的形式,上一个请求响应后才能处理下一个请求;由于浏览器最大TCP连接数的限制,所以有了最大并发请求数的限制;
- 追问:Chrome浏览器支持最大6个同域请求的并发是什么意思?
- 答:因为Chrome支持最大6个TCP连接;
-
2.0:同域名下所有通信都在单个连接上完成,消除了因多个TCP连接而带来的延迟和内存消耗。单个连接上可以并行交错的请求和响应,之间互不干扰;
问:为什么HTTP/1.1不能实现多路复用呢?
-
HTTP/2.0是基于二进制“帧”的协议,HTTP/1.1是基于“文本分割”解析的协议。
-
HTTP/1.1的报文结构中,服务器需要不断的读入字节,直到遇到换行符,或者说一个空白行,处理顺序是串行的,一个请求和一个响应需要通过一问一答的形式才能对应起来;
GET / HTTP/1.1 Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 Accept-Encoding:gzip, deflate, br Accept-Language:zh-CN,zh;q=0.9,en;q=0.8 Cache-Control:max-age=0 Connection:keep-alive Host:www.imooc.com Referer:https://www.baidu.com/
-
HTTP2.0中,有两个非常重要的概念,分别是帧(frame)和流(stream);
- 帧代表着最小的数据单位,每个帧会标识出该帧属于哪个流,流也就是多个帧组成的数据流。
-
多路复用,就是在一个TCP连接中可以存在多条流。换句话说,也就是可以发送多个请求,对端可以通过帧中的标识知道属于哪个请求。通过这个技术,可以避免HTTP旧版本中的队头阻塞问题,极大的提高传输性能。