浏览器的通信能力
- 浏览器可以代替用户完成
http请求,代替用户解析响应结果,所以称之为用户代理user agent
在网络层面,浏览器拥有的两大核心能力
- 自动发出请求的能力
- 自动解析响应的能力

- 如上图所示,也可以不通过浏览器,直接发送请求,但是响应的结果不是开发者想看到的,那么浏览器的可以帮助开发者发送请求,并且解析响应的内容
自动发出请求的能力
当以下事情发生时,浏览器会代替用户自动发出
http请求
- 用户在地址栏输入了一个
url地址并按下回车 ,浏览器会自动解析URL,并发出一个GET请求,同时抛弃当前页面 - 用户点击页面中的
a元素 ,浏览器会拿到a元素的href地址,并发出GET请求,同时抛弃当前页面 - 用户点击提交按钮
<button type="submit">...</button>,浏览器会获取按钮所在的<form>元素的action地址,同时拿到method属性值,最后把表单数据组织到请求体,发出指定方法的请求,同时抛弃当前页面 - 解析
HTML时遇到<link>、<img>、<script>、<video>、<audio>等元素 ,浏览器会获取到对应的地址,并发出GET请求 - 用户点击了刷新,浏览器会获取到当前页面的地址,以及当前页面的请求方法,重新发送请求,同时抛弃当前页面
服务器和浏览器的约定:发送
GET请求,不附带请求体,该默认行为逐步造成GET和POST的差异
- 浏览器在发送
GET请求时,不会附带请求体 GET请求的传递信息量有限(URL长度有限),适合传递少量数据;POST请求的传递信息量没有限制,适合传输大量数据GET请求只能传递ASCII数据,若遇到非ASCII数据则需进行编码;POST请求没有限制- 大部分
GET请求传递的数据都附带在path参数中,能够通过分享地址完整重现页面,但同时暴露了数据,若有敏感数据传递,则不该使用GET请求,至少不该放到path中 POST不会被保存到浏览器的历史记录中,历史记录的本质是复现请求,所以点开浏览器的历史记录都是通过GET方式进行请求- 刷新页面时,若当前的页面是通过
POST请求得到的,则浏览器会提示用户是否重新提交;若是GET请求得到的页面则没有提示

自动解析响应的能力
- 浏览器不仅能发送请求,还能针对服务器的各种响应结果做出不同的自动处理
常见的响应处理
-
识别响应码 :浏览器能够自动识别响应码,当出现特殊响应码时浏览器会自动完成处理,如
301、302 -
自动分析响应头的
Content-Type,并进行不同处理text/plain: 普通的纯文本,浏览器通常会将响应体原封不动的显示到页面上text/html:html文档,浏览器通常会将响应体作为页面进行渲染text/javascript或application/javascript:JavaScript代码,浏览器通常会使用JavaScript执行引擎进行解析执行text/css:css代码,浏览器会将它视为样式image/jpeg: 浏览器会将它视为jpg图片application/octet-stream: 二进制数据,会触发浏览器下载功能attachment: 附件,会触发下载功能,该值应放到Content-Disposition中
Ajax的由来
- 浏览器本身就具备网络通信的能力,但在早期浏览器并没有把这个能力开放给
JavaScript - 最早是微软在
IE浏览器中把这一能力向JavaScript开放,让JavaScript可以在代码中实现发送请求,且不会刷新页面,这项技术在 2005 年被正式命名为AJAX(A synchronous J avaScript A nd XML>
AJAX是指在web应用程序中异步向服务器发送请求,实现方式有两种
| 功能点 | XHR | Fetch |
|---|---|---|
| 基本的请求能力 | ✅ | ✅ |
| 基本的获取响应能力 | ✅ | ✅ |
| 监控请求进度 | ✅ | ❌ |
| 监控响应进度 | ✅ | ✅ |
| Service Worker 中是否可用 | ❌ | ✅ |
| 控制 cookie 的携带 | ❌ | ✅ |
| 控制重定向 | ❌ | ✅ |
| 请求取消 | ✅ | ✅ |
| 自定义 referrer | ❌ | ✅ |
| 流 | ❌ | ✅ |
API 风格 |
Event |
Promise |
| 活跃度 | 停止更新 | 不断更新 |
XHR进度监控
- 首先准备一个上传文件的控件,这里以上传图片为例
css
/* 控制预览图和进度的显示与隐藏 */
.upload.select .upload-select {
display: block;
}
.upload.select .preview {
display: none;
}
.upload.progress .upload-progress {
display: block;
}
.upload.result .upload-result {
display: block;
}
html
<div class="upload select">
<!-- 文件上传控件 -->
<div class="upload-select">
<input type="file" />
</div>
<!-- 展示进度 -->
<div class="upload-progress" style="--percent: 0">
<div class="progress-bar"></div>
</div>
<!-- 展示预览的图片 -->
<img alt="" class="preview" />
</div>

- 点击后获取图片文件,并展示预览图和上传进度
javascript
function showArea(areaName) {
$('.upload').className = `upload ${areaName}`;
}
// 监听change事件,选中文件发生变化
$('.upload-select input').onchange = (e) => {
// 获取图片文件对象
const file = e.target.files[0];
// 使用图片文件读取器展示文件预览图
const reader = new FileReader();
// 将图片文件数据读取成base64
reader.readAsDataURL(file);
// 监听图片文件读取完成事件
reader.onload = (e) => {
// 读取完成
$('.preview').src = e.target.result;
展示需要上传的预览图
showArea('progress');
// 上传文件
uploadFile(file);
};
};

- 上传到服务器,并实时更改上传进度
javascript
function uploadFile(file) {
// 设置当前上传进度
setProgress(0);
// 创建xhr实例
const xhr = new XMLHttpRequest();
// 打开一个请求
xhr.open('POST', 'http://localhost:9527/upload/single');
// 生成 multipart/form-data 格式的请求体
const form = new FormData();
// 构建一条请求体(其中file是文件对象)
form.append('avatar', file);
// 监听请求进度
xhr.upload.onprogress = (e) => {
console.log(e.loaded, e.total);
// 计算请求进度,loaded是当前已经上传部分,total是总大小
const percent = Math.floor((e.loaded / e.total) * 100);
// 设置进度
setProgress(percent);
};
// 监听上传完成
xhr.onload = () => {
showArea('result');
};
// 发送请求
xhr.send(form);
}
// 设置进度的函数
function setProgress(value) {
$('.upload-progress').style.setProperty('--percent', value);
}
