《前端入门》系列主要为个人对前端一些经验和认识总结。本节主要涉及 HTTP 协议和 Ajax 请求,日常开发的联调等内容。
# Ajax 请求
Ajax 不是 JavaScript 的规范,它只是 Jesse James Garrett 提出的新术语:Asynchronous JavaScript and XML
,意思是用 JavaScript 执行异步网络请求。
# 网络请求的发展
网络请求,是用来从服务端获取需要的信息,然后解析协议和内容,来进行页面渲染或者是信息获取的过程。前面《6. 认识浏览器》一节已经大致说过关于浏览器渲染,以及完整的 HTTP 请求流程。
在很久以前,我们的网络请求除了静态资源(html/css/js
等)文件的获取,主要用于表单的提交。我们在完成表单内容的填写之后,点击Submit
按钮,表单开始提交,浏览器就会刷新页面,然后在新页面里告诉你操作是成功了还是失败了。
然后随着时间发展,大家觉得这样每次都刷新页面的体验太糟了,然后开始使用XMLHttpRequest
来获取请求内容,再更新到页面中。页面开始支持局部更新、动态加载,后面还有懒加载、首屏加载等等,其实都可以算是基于这个基础吧。
同步请求会阻塞进程,页面呈现假死状态,导致体验效果也较差。接下来,Ajax 的应用越来越广,慢慢大家都开始使用异步请求 + 回调的方式,来进行请求处理。那是一个浏览器兼容困难时期,jQuery 封装的$.ajax()
,由于兼容性处理较好,也开始被大家广泛使用
。
现在,我们用上了路由管理,编写单页应用,Ajax 已经是一个不可或缺的功能了。
我们先来认识下 Ajax 的核心:XMLHttpRequest
API 。
# XMLHttpRequest
XMLHttpRequest
让发送一个 HTTP 请求变得非常容易。你只需要简单的创建一个请求对象实例,打开一个 URL,然后发送这个请求。当传输完毕后,结果的 HTTP 状态以及返回的响应内容也可以从请求对象中获取。
来看个简单的例子(我们常用的 Ajax 处理):
var request = new XMLHttpRequest(); // 新建XMLHttpRequest对象
request.onreadystatechange = function() {
// 状态发生变化时,函数被回调
if (request.readyState == 4) {
// 成功完成
// 判断响应结果:
if (request.status == 200) {
// 成功,通过responseText拿到响应的文本
console.log(request.responseText);
} else {
// 失败,根据响应码判断失败原因:
console.log(request.status);
}
}
};
// 发送请求
// open的参数:
// 一:请求方法,包括get/post等
// 二:请求地址
// 三:表示是否异步请求,若为false则是同步请求,会阻塞进程
request.open("GET", "/api/categories", true);
request.send();
大概就是上面这样,来处理一个 HTTP 请求。我们通常会将它封装成一个通用的方法,方便调用。上面例子中使用200
来判断是否成功,但有些时候200-400
(不包括400
)的范围,都可以算是成功的。
如果说我们将其封装起来,同时使用 ES6 的 Promise 的方式来操作的话,大概会是这样:
function ajax({ method, url, params, contentType }) {
const xhr = new XMLHttpRequest();
const formData = new FormData();
Object.keys(params).forEach(key => {
formData.append(key, params[key]);
});
return new Promise((resolve, reject) => {
try {
xhr.open(method, url, false);
xhr.setRequestHeader("Content-Type", contentType);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 400) {
// 这里我们使用200-400来判断
resolve(xhr.responseText);
} else {
// 返回请求信息
reject(xhr);
}
}
};
xhr.send(formData);
} catch (err) {
reject(err);
}
});
}
这里使用了FormData
来处理。通过FormData
对象可以组装一组用XMLHttpRequest
发送请求的键/值对。
它可以更灵活方便的发送表单数据,因为可以独立于表单使用。如果你把表单的编码类型设置为multipart/form-data
,则通过FormData
传输的数据格式和表单通过submit()
方法传输的数据格式相同。也支持文件的上传和添加。
上面的代码也只是一个简单的例子,如果要封装成完善的库,我们通常还需要处理一些问题:
- 浏览器兼容性
- babel polyfill 处理 ES6
- get 方法通过将 params 转换拼接 URL 处理
如果想知道不使用FormData
对象的情况下,通过 AJAX 序列化和提交表单,以及更多的XMLHttpRequest
内容,可以参考Using XMLHttpRequest | MDN (opens new window)。
# HTTP 协议
关于 HTTP 协议的内容,实在是太多太多,这里只大致讲一下接触比较多的。
更多与 HTTP 协议相关的详细说明,可参考《10. 理解 HTTP 协议》。
还有 TCP/IP 协议的就直接略过,可参考下《9. 网络协议基础》。
# 理解 HTTP 协议
# HTTP 结构
# HTTP 消息的结构
- Request
------------------
Request line
(包括:请求方法、请求的资源、HTTP协议的版本号)
------------------
Request header
(包括:Cache头域、Client头域、Cookie/Login头域、Entity头域、Miscellaneous头域、Transport头域等)
------------------
空行
------------------
Request body
------------------
- Response
------------------
Response line
(包括:HTTP协议的版本号、状态码、消息)
------------------
Response header
(包括:Cache头域、Cookie/Login头域、Entity头域、Miscellaneous头域、Transport头域、Location头域等)
------------------
空行
------------------
Response body
------------------
# 状态码
状态码由三位数字组成,第一个数字定义了响应的类别(括号中为常见的状态码):
- 1XX--提示信息:表示请求已被成功接收,继续处理
- 2XX--成功:表示请求已被成功接收,理解,接受(200 OK)
- 3XX--重定向:要完成请求必须进行更进一步的处理(302 Found 重定向/304 Not Modified 缓存)
- 4XX--客户端错误:请求有语法错误或请求无法实现(400 Bad Request 客户端请求与语法错误/403 Forbidden 服务器拒绝提供服务/404 Not Found 请求资源不存在)
- 5XX--服务器端错误:服务器未能实现合法的请求(500 Internal Server Error 服务器发生了不可预期的错误)
# 无连接的 HTTP
# 无连接
无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。
# Keep-Alive
Keep-Alive 功能使客户端到服务器端的连接持续有效,当出现对服务器的后继请求时,Keep-Alive 功能避免了建立或者重新建立连接。
# 无状态的 HTTP
无状态是指协议对于事务处理没有记忆能力,服务器不知道客户端是什么状态。
通常我们会根据场景,使用Cookie
、Token
、Session
等方法来记录用户状态,完善上下请求的承接性。
# HTTP 与浏览器缓存
浏览器会在第一次请求完服务器后得到响应,我们可以在服务器中设置这些响应,从而达到在以后的请求中尽量减少甚至不从服务器获取资源的目的。
静态资源的缓存能减轻很多流量,如今我们的文件很多都加上了 md5,则缓存的使用越来越广泛。
浏览器是依靠请求和响应中的的头信息来控制缓存的,主要涉及Expires
与Cache-Control
、Last-Modified/If-Modified-Since
、ETag/If-None-Match
这几个。
第一次请求:
再次请求:
# HTTP 与跨域
# 浏览器同源政策
同源政策的目的,是为了保证用户信息的安全,防止恶意的网站窃取数据。所谓"同源"指的是"三个相同": 协议相同、域名相同、端口相同。
随着互联网的发展,"同源政策"越来越严格。目前,如果非同源,共有三种行为受到限制。
Cookie
、LocalStorage
和IndexDB
无法读取。DOM
无法获得。AJAX
请求不能发送。
# 前端解决跨域
跨域方法大概以下几种:
document.domain + iframe
(只有在主域相同的时候才能使用该方法)- 动态创建
script
(JSONP) location.hash + iframe
window.name + iframe
postMessage
- CORS
websockets
现在的话,应该是 CORS 的使用会更广泛吧。实现 CORS 通信的关键是服务器。只要服务器实现了 CORS 接口,就可以跨源通信。
# 请求联调
一般来说,我们的日常联调通常有两种:浏览器查看请求,或是工具抓包查看(Fiddler)。
# 浏览器查看请求
我们又来看浏览器的控制台了:
# Network 面板
Network 面板可以记录页面上的网络请求的详情信息,从发起网页页面请求 Request 后分析 HTTP 请求后得到的各个请求资源信息(包括状态、资源类型、大小、所用时间、Request 和 Response 等),可以根据这个进行网络性能优化。
该面板主要包括 5 大块窗格,如图:
- Controls:控制 Network 的外观和功能。
- Filters:控制 Requests Table 具体显示哪些内容。
- Overview:显示获取到资源的时间轴信息。
- Requests Table:按资源获取的前后顺序显示所有获取到的资源信息,点击资源名可以查看该资源的详细信息。
- Summary:显示总的请求数、数据传输量、加载时间信息。
# 查看具体资源的详情
通过点击某个资源的 Name 可以查看该资源的详细信息,根据选择的资源类型显示的信息也不太一样,可能包括如下 Tab 信息:
- Headers:该资源的 HTTP 头信息。
- Preview:根据你所选择的资源类型(JSON、图片、文本)显示相应的预览。
- Response:显示 HTTP 的 Response 信息。
- Cookies:显示资源 HTTP 的 Request 和 Response 过程中的 Cookies 信息。
- Timing:显示资源在整个请求生命周期过程中各部分花费的时间。
一般来说,联调主要关注请求是否正确发送、回包是否是约定的格式,所以我们更多使用资源详情的查看,包括:
- 查看 HTTP 头信息是否正确
- 查看请求数据是否带上
- 查看请求是否成功,分析 HTTP 状态码
- 查看回包格式和内容是否正确
而其他功能,或许对于性能优化的时候会使用更多,先不多介绍。
这里面有一篇文章写得挺详细的,可以参考《Chrome 开发者工具详解(2):Network 面板》 (opens new window)
# Fiddler
Fiddler 是一个 HTTP 的调试代理,以代理服务器的方式,监听系统的 Http 网络数据流动。Fiddler 可以也可以让你检查所有的 HTTP 通讯,设置断点,以及 Fiddle 所有的“进出”的数据(可用于抓包、修改请求等)。
通常来说,我们会使用它来解决一些问题:
- 查看请求详情(类似上方的浏览器 Network 面板)。
- 请求失败时,抓包给后台查看问题。
- 模拟请求。
- 拦截请求,并更改请求内容。
- 移动端的请求查看和抓包。
具体的使用方式,可以参考Fiddler 教程 (opens new window)。
# 其他深入学习
就目前来说,大致的入门内容大概到这,还有很多内容或许分散在本骚年的各个系列博客中。这里放一些总结和整理相关的吧。
# 前端阶段性总结
《前端阶段性总结之「理解 HTTP 协议」》 (opens new window) > 《前端阶段性总结之「网络协议基础」》 (opens new window)
《前端阶段性总结之「自动化和构建工具」》 (opens new window)
《前端阶段性总结之「框架相关」》 (opens new window)
《前端阶段性总结之「javascript 新特性」》 (opens new window)
《前端阶段性总结之「深入 javascript」》 (opens new window)
《前端阶段性总结之「掌握 javascript」》 (opens new window)
《前端阶段性总结之「总览整理」》 (opens new window)
# 前端杂谈
《前端模板引擎》
《对话抽象》
《前端思维转变--从事件驱动到数据驱动》
还有《纯前端的进军》系列,更多可以查看 github 上博客:godbasin/godbasin.github.io (opens new window)。
# 结束语
这一节主要讲了 HTTP 请求相关,包括 Ajax(XMLHttpRequest)、HTTP 协议/跨域/缓存等,以及常用的前后台交互(联调)方式的介绍。这里面都是书面的介绍,我们需要更多的其实是实践,动手去写吧。