用户代理 user agent
浏览器可以代替用户完成http请求,代替用户解析响应结果,所以我们称之为:用户代理
在网络层面,浏览器拥有两大核心能力,分别是:
- 自动发出请求的能力
- 自动解析响应的能力
自动发出请求的能力
当一些事情发生的时候,浏览器会代替用户自动发出http请求,常见的包括:
-
用户在地址栏输入了一个url地址,并按下了回车
浏览器会自动解析URL,并发出一个
GET
请求,同时抛弃当前页面。 -
当用户点击了页面中的a元素
浏览器会拿到a元素的href地址,并发出一个
GET
请求,同时抛弃当前页面。
这里的$0可以在控制台输出,相当于选中DOM元素
$0.href会拿到完整的url地址
浏览器通过书写的地址(绝对路径,相对路径)--转换-> URL
通过转换变成了URL
绝对路径就是和当前的页面的路径部分没用任何关系
例如:
当前页面在 https://www.baidu.com/a/b/1.html
而在网页栏中输入 https://www.google.com/
省略htts://
,省略协议也是绝对路径
相对路径相对的是当前页面path部分
./表示最后一个斜杠
./2.html --> http://www.baidu.com/a/b/2.html
../是倒数第二个斜杠
../2.html --> http://www.baidu.com/a/2.html
啥也不写相当于./
-
当用户点击了提交按钮
<button type="submit">...</button>
浏览器会获取按钮所在的
<form>
元素,拿到它的action
属性地址,同时拿到它method
属性值,然 后把表单中的数据组织到请求体中,发出指定方法
的请求,同时抛弃当前页面。这种方式的提交现在越来越少见了
form表单是用来发请求的(浏览器自动发请求的能力:不需要额外的 JavaScript 代码)
- action:放url地址,method
- 请求体靠input
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Submit Form Example</title>
</head>
<body><h2>用户信息表单</h2>
<form action="https://study.duyiedu.com/api/user/login" method="post"><div><label for="username">用户名:</label><input type="text" id="username" name="username" required></div><div><label for="password">密码:</label><input type="password" id="password" name="password" required></div><div><button type="submit">提交</button></div>
</form></body>
</html>
请求体-payload(负荷)
name对应的就是属性名,value对应属性值
submit就是触发浏览器发送请求
这个叫持久化日志,点击后刷新网页,日志依然会存储,方便调试和分析 web
在 body 中加入下面代码
<script>const form = document.querySelector('form');form.onsubmit = (e) => {e.preventDefault();console.log('submit');}</script>
阻止了表单的默认行为,点击提交后不再提交网络请求
如果没有阻值会直接跳页面,借用form表单发请求的能力
-
当解析HTML时遇到了
<link> <img> <script> <video> <audio>
等元素浏览器会拿到对应的地址,发出
GET
请求 -
当用户点击了刷新
浏览器会拿到当前页面的地址,以及当前页面的请求方法,重新发一次请求,同时抛弃当前页面。
浏览器在发出请求时,会自动附带一些请求头
常见面试题,get和post的区别
- 协议层面没有什么差别
- 所有主流浏览器客户端都认为get请求不能携带请求体
从古至今,服务器和浏览器都有一个约定:
当发送GET请求时,不会附带请求体
这个约定深刻的影响着后续的前后端各种应用,现在,几乎所有人都在潜意识中认同了这一点,无论是前端开发人员还是后端开发人员。
由于前后端程序的默认行为,逐步造成了GET和POST的各种差异:
- 浏览器在发送GET请求时,不会附带请求体
- GET请求的传递信息量有限,适合传递少量数据;POST请求的传递信息量是没有限制的,适合传输大量数据。
- GET请求只能传递ASCII数据,遇到非ASCII数据需要进行编码;POST请求没有限制
- 大部分GET请求传递的数据都附带在path参数中,能够通过分享地址完整的重现页面,但同时也暴露了数据,若有敏感数据传递,不应该使用GET请求,至少不应该放到path中
- POST不会被保存到浏览器的历史记录中
- 刷新页面时,若当前的页面是通过POST请求得到的,则浏览器会提示用户是否重新提交。若是GET请求得到的页面则没有提示。
encodeURIComponent()
encodeURIComponent()
是 JavaScript 中用于对 URI(Uniform Resource Identifier)中的组成部分进行编码的一个函数
它会将字符转换成可安全传输的格式,以避免在 URL 中出现特殊字符导致错误或不稳定的情况
自动解析响应的能力
常见的处理有:
-
识别响应码
浏览器能够自动识别响应码,当出现一些特殊的响应码时浏览器会自动完成处理,比如301.302
-
根据响应结果做不同的处理浏览器能够自动分析响应头中的 Content-Type,根据不同的值进行
不同处理,比如:- text/plain:普通的纯文本,浏览器通常会将响应体原封不动的显示到页面上
- text/html:html文档,浏览器通常会将响应体作为页面进行渲染
- text/javascript或application/javascript:js代码,浏览器通常会使用JS执行引擎将它解析执行
- text/css:css代码,浏览器会将它视为样式
- image/jpeg:浏览器
- application/octet-stream:二进制数据,会触发浏览器下载功能
- attachment:附件,会触发下载功能,也可以放在Content-Disposition中
基本流程(url输入浏览器后回车)
点击a后又重新来一遍
AJAX
Asynchronous Javascript And XML(异步 Javascript 和 XML)
它的实现方式有两种,一种是XMLHttpRequest (简称XHR)和 Fetch
功能点 | XHR | Fetch |
---|---|---|
基本的请求能力 | ✅ | ✅ |
基本的获取响应能力 | ✅ | ✅ |
监控请求进度 | ✅ | ❌ |
监控响应进度 | ✅ | ✅ |
Service Worker中是否可用 | ❌ | ✅ |
控制cookie的携带 | ❌ | ✅ |
控制重定向 | ❌ | ✅ |
请求取消 | ✅ | ✅ |
自定义referrer | ❌ | ✅ |
流 | ❌ | ✅ |
API风格 | Event |
Promise |
活跃度 | 停止更新 | 不断更新 |
库:axios --> xhr
umi-request --> fetch
原生能力:fetch xhr
代码
这个promise拿到了响应头就完成了
而上图的 resp.json()
则是等待转换为json格式,解析的结果就是json对象
如果改为 text()
是等待响应体完成并转化为纯文本之后
blob 就是二进制
ArrayBuffer 只读的、空间连续的、定长字节数组
先拿到头,后面体要等的
其实 await 就实现了这个
文件上传:
交互逻辑
请求逻辑 AJAX
所有的I/O操作都是异步的
URL的本质是什么:获取资源
通过字符串 --> (定位)互联网资源
dataUrl就是直接把资源描述出来了
background: url()
<script src="data url">
可以生成dataurl放在这
图片预览就搞定了
要监控上传进度,就要用传统的xhr
浏览器还出了api
xhr.about()是终止请求
登录和鉴权
由于HTTP协议的特点,每次「请求-响应」都是独立的,这就会导致身份信息丢失的问题
这个问题可以使用token令牌解决
在使用AJAX时可以按照这样一种通用模式处理:
- 在处理响应结果时,只要服务器给我的响应头中包含了
token
,就将其保存在localStorage
中 - 在请求时,只要
localStorage
中有token
,就将其代入到请求头发送到服务器
流式读取
在大语言模型中,通过流式输出可以边生成边输出,即每生成一部分内容就立即输出一个片段(chunk)
而在前端应该采用蹦一个词过来加一个词的流式读取方式,之前我的做法都是一次性获取完之后再前端进行模拟一个字一个字蹦,但这样的响应时间会比较长,这块先埋个坑,后面来填
END
本文主要介绍了浏览器的通信能力,包括用户代理、AJAX,在用户代理中浏览器具有自动发送请求的能力和自动解析响应的能力;在AJAX中主要有两种实现方式,分别是XMLHttpRequest和Fetch;另外介绍了XML可以监控请求进度可用于文件上传进度的监控,Fetch更擅长处理异步代码具有流的能力