浏览器相关【笔记】

本文详细介绍了浏览器对象模型,包括Window、Location、Navigation、Screen和History对象,以及它们的属性和应用场景。此外,还探讨了浏览器事件捕获、冒泡、阻止事件传播和默认行为。文章还深入讲解了Ajax和fetch API,以及HTTP请求头、状态码和面试相关问题。最后,讨论了HTTP不同版本在并发请求上的区别,以及单页应用的缓存策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

浏览器内置对象详解

知识整理——浏览器对象模型

什么是浏览器对象模型

  • BOMBrowser 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 innerHeightIE9+、Safari、Firefox、Opera: 该容器中页面视图区的大小 Chrome: 返回视口大小 移动设备: 返回可见视口(即屏幕上可见页面区域的大小) 移动IE浏览器: 不支持该属性,当移动IE浏览器将布局视口的信息保存至document.body.clientWidthdocument.body.clientHeight
outerWidth outerHeightIE9+、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 查询字符串参数,并将其返回一个对象,可通过循环、正则来实现,方法有很多,实现的大体思路是:
    通过locationsearch属性来获取当前 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 对象的属性方法

clipboard.png
clipboard.png

  • navigation 应用场景
    • 检测插件
    • 注册处理程序

Screen 对象

其提供有关窗口显示的大小和可用的颜色输入信息。

  • screen 对象的属性和方法

clipboard.png
clipboard.png

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) bannedtrue 时在 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,比较消耗性能
  • 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在并发请求上主要区别是什么?

  1. HTTP/1.0

    • 每次TCP连接只能发送一个请求,当服务器响应后就会关闭这次连接,下一个请求需要再次建立TCP连接。
  2. HTTP/1.1

    • 默认采用持久连接(TCP连接默认不关闭,可以被多个请求复用,不用声明Connection:keep-alive)。
    • 如果想关闭持久连接,变成HTTP/1.0的话,设置Connection:close;
    • 增加了管道机制,在同一个TCP连接里,允许多个请求同时发送,增加了并发性,进一步改善了HTTP协议的效率。(一问一答的形式)
    • 所有的数据通信是有顺序的,A B C,A先到达服务器开始响应,响应结束后才能进行下一个请求。所以,同一个TCP连接里,所有的数据通信是按次序进行的。如果有响应很慢的,会有许多请求排队,造成“队头阻塞”;
  3. 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旧版本中的队头阻塞问题,极大的提高传输性能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值