关于"连接"部分的学习分为两篇文章介绍,这是上篇,介绍Ajax、Comet、postMessage、Worker和WebSockets。
# Ajax
# 什么是Ajax
Async JavaScript and XML,是一种在不刷新整个页面下,通过JavaScript与服务器进行异步通信的技术,用户体验更好。
# Ajax的实现方式
img标签:响应是图片,无法轻易获取数据
iframe标签:响应是HTML,但存在跨域问题
JSONP:script标签发起请求
说明:
只支持GET请求,并追加参数
&jsonp=funcName
指定回调函数名响应内容必须是一段正确的JavaScript代码,一般形如
funcName(params)
的结构使用该方式注意服务器是可信的,否则存在安全隐患
function getJSONP(url, callback = () => {}) {
// url追加回调函数名
const cbNum = `cb${getJSONP.counter++}`;
const cbName = `getJSONP.${cbNum}`;
if(url.indexOf('?') === -1) {
url += `?jsonp=${cbName}`
} else {
url += `&jsonp=${cbName}`;
}
// 定义回调函数,注意作用域是全局的
getJSONP[cbNum] = function(response) {
try {
callback(response);
} finally {
delete getJSONP[cbNum];
document.body.removeChild(script);
}
}
// 通过script发起请求
const script = document.createElement('script');
script.src = url;
document.body.appendChild(script);
}
getJSONP.counter = 0;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
- XMLHttpRequest
// 通过初封装http请求来认识它
function request({
method = 'GET',
url = 'http://example.com/api',
data = null,
headers = {
'Content-Type': 'application/json'
},
callback = () => {},
overrideMimeType,
async = true,
name,
password
}) {
// 创建XMLHttpRequest对象
const request = new XMLHttpRequest();
// 设置是否允许携带凭证,如cookie、token、用户、密码
// 判断该属性是否存在可以作为当前浏览器是否支持CORS
request.withCredentials = true;
// 设置请求类型(不区分大小写)、URL(支持相对URL和绝对URL)、是否异步(默认为true,若需要同步,可考虑与worker搭配使用)、用户名、密码(仅在跨域时需要)
request.open(method, url, async, name, password);
// 设置请求头
// 一些请求头设置是无效的,浏览器会自动添加,如Accept-Charset、Accept-Encoding、Cookie、Date、Referer等等
// 必须在调用open和send方法之间设置
// 当没有设置Content-Type请求头时,浏览器会自动设置合适的值,比如传送的是XML、File、FormData
for(const key in headers) {
request.setRequestHeader(key, headers[key]);
}
// 设置响应的MIME类型,将忽略Content-Type响应头(是否不支持或废弃)
if(overrideMimeType) request.overrideMimeType(overrideMimeType);
// 监听状态变化
request.onreadystatechange = function() {
switch (request.readyState){
case 0:
console.log('open尚未调用');
break;
case 1:
console.log('open已调用');
break;
case 2:
console.log('接受到响应头');
break;
case 3:
console.log('接收到响应体');
break;
case 4:
console.log('响应结束');
if (request.status === 200) {
// 注意无法通过getResponseHeader或getAllResponseHeaders获取到cookie信息
const type = request.getResponseHeader('Content-Type');
let data;
if (type.indexOf('json') > -1) {
data = JSON.parse(request.responseText);
} else if (type.indexOf('xml') > -1) {
data = request.responseXML;
} else {
data = request.responseText;
}
callback(data);
}
}
// 各类监听事件(部分是否不支持或废弃)
request.onloadstart = function() {
console.log('请求开始');
}
// 监听下载进度
request.onprogress = function(e) {
console.log('下载中');
if(e.lengthComputable) {
console.log(`已下载${e.loaded / e.total * 100}%`);
}
}
request.onload = function() {
console.log('下载完成');
}
// 监听上传进度
request.upload.onprogress = function(e) {
console.log('上传中');
if(e.lengthComputable) {
console.log(`已上传${e.loaded / e.total * 100}%`);
}
}
request.upload.onload = function() {
console.log('上传完成');
}
// load\timeout\abort\error只有一个会被触发
request.ontimeout = function() {
console.log('请求超时');
}
request.onabort = function() {
// 调用abort方法触发
console.log('请求被取消');
}
request.onerror = function() {
console.log('请求失败');
}
request.onloadend = function() {
console.log('请求结束');
}
}
// 发送请求,如果是GET请求,不传参数或传null
request.send(data);
return request;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
- fetch
// 新一代请求方式
// 用法
const p = fetch(url [,options])
p.then(function(response) {
// 返回Response对象,如调用json方法,返回json数据格式的响应内容
return response.json();
}, function(error) {
// handle error
}).then(function(data) {
// 返回响应内容
}, function(error) {
// handle error
})
// 参数说明
// 第一参数表示请求地址字符串url或请求对象request
// 第二参数表示可选的请求配置对象,具有以下属性,
// method,表示请求方式,默认值为"GET"
// body,表示请求体?
// headers,表示请求头,具体结构见Headers
// credentials,表示凭证模式,默认值为omit,请求不携带凭证如Cookie,也可取值same-origin,表示同域请求包含凭证,或include,表示所有域请求包含凭证
// referrer,设置请求引用源,可取值同源url、about:client或空
// referrerPolicy,设置请求的引用源策略。可取值空、no-referrer、no-referrer-when-downgrade、same-origin、origin、strict-origin、origin-when-cross-origin、strict-origin-when-cross-origin、unsafe-url
// mode,设置请求是允许CORS还是仅限于同源访问。可取值navigate、same-origin、no-cors、cors
// cache,设置请求如何与浏览器缓存交互。可取值default、no-store、reload、no-cache、force-cache、only-if-cached
// redirect,可取值follow、error、manual"
// integrity
// keepalive,可替代sendBeacon,用于保持连接不被关闭
// signal,用于取消请求
// window,只能被设置为null
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 相关API
// Headers
// 构造函数
// Headers(meta),参数meta表示请求头或响应头字段的集合对象
// 方法
// has(name),头部是否有指定字段名
// get(name),获取指定的头部字段名
// set(name, value),设置指定的头部字段名
// append(name, value),添加指定的头部字段名和值
// delete(name),删除指定的头部字段名
// foreach(function(value,name){}[,currentContext]),遍历头部字段
// Body
// 属性
// body,表示响应体
// bodyUsed,表示响应体是否被使用
// 方法
// text(),返回字符串类型的响应内容
// json(),返回经JSON.parse解析的json对象
// blob(),返回二进制大对象类型的响应内容
// arrayBuffer(),返回缓冲数组类型的响应内容
// formData(),返回可以被另一个请求转发的表单数据响应
// Request,继承Body
// 构造函数
// Request(url,options),第一参数表示请求地址字符串url或请求对象,第二参数表示请求配置对象
// 属性
// url
// method
// headers
// destination,表示请求目标,可取值"", "audio", "audioworklet", "document", "embed", "font", "image", "manifest", "object", "paintworklet", "report", "script", "sharedworker", "style", "track", "video", "worker", "xslt"
// referrer
// referrerPolicy
// mode
// credentials
// cache
// redirect
// integrity
// keepalive
// isReloadNavigation
// isHistoryNavigation
// signal
// 方法
// clone(),克隆请求对象
// Response,继承Body
// 属性
// status,表示状态码
// statusText,表示状态码含义
// ok,表示状态码是否为2XX
// headers,表示响应头
// url,表示当前地址
// type,表示响应类型,值有basic\cors\default\error\opaque\opaqueredirect
// redirected
// trailer
// 方法
// clone(),克隆响应对象
// 静态方法
// error(),网络错误,返回一个错误对象
// redirect(url,statusCode),重定向
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
点我,提供了xhr和fetch两种方式封装的示例 (opens new window)
# Comet
# 什么是Comet
Web服务器发起通信并异步发送消息到客户端,某种意义上,Ajax是客户端从服务端“拉”数据,Comet是服务端向客户端“推”数据。理解是推流技术或长连接技术。
# Comet的实现方式
短轮询
定时发起ajax请求
长轮询
ajax请求结束后发起另一个ajax请求
长连接
客户端通过XHR发起请求,若有上次事件ID的话则加到请求头,当readyState为3时处理数据,先检查响应类型是否是
text/event-stream
,是则处理数据,否则停止请求,当readyState为4时,若停止请求则不再重连,否则重复上述过程。上述思路可以说是SSE的简易实现,下面就来介绍SSE。
SSE
概念:Server Sent Event。网页自动获取来自服务器的更新,不需开发者手动在客户端作操作向服务端发送请求
特点:轻量,使用相对简单;单向传送(只能服务端向客户端发送);基于HTTP协议;默认支持断线重连;允许自定义发送数据类型
链接:
基于Ajax和Comet可以构建更高级的通信协议,比如RPC(远程过程调用)、发布订阅事件系统。
# 跨域消息传递——postMessage
# 适用范围
允许脚本显式打开的一个新窗口(window.open)或者嵌套其中的窗体(iframe)与当前窗口进行通信
# postMessage参数说明
- 第一个参数表示要传递的消息
- 第二个参数表示目标窗口的源,可以传递一个URL,仅协议、主机和端口号有效,其余部分会被忽略。若同源,传
/
,若无限制,传*
# 示例
# Web Worker
# 背景
设计成单线程的理论是,JS必须不能运行太长时间,否则会导致循环事件,浏览器无法对用户输入作出响应,而Web Worker弥补浏览器无法多线程的缺陷
# 概念
创建新的运行时,有自己的栈、堆、队列,不影响页面的渲染
# 特点
- 处理耗时操作;
- 无法访问window和document,不能操作DOM;
- 同源限制;
- 线程同步问题,在ES8提出了SharedArrayBuffer和Atomics,解决线程同步和线程通信问题。
# 类型
SharedWorker
ServiceWorker
用途有哪些?离线资源缓存与更新、后台消息传递、网络代理、消息推送
# API介绍
Worker
事件属性:onmessage、onerror、onmessageerror
方法:postMessage、terminate
WorkerGlobalScope
除了window和document对象外,其他API基本可以使用
close:自行关闭worker
importScripts:同步加载多个脚本,当中有一个脚本加载出错,则剩余脚本不再载入和运行
Worker执行模型:worker从上到下同步运行代码,然后进入一个异步阶段。当有监听消息,worker永远不会自动退出;而若没有监听消息,则直到所有任务相关的回调函数都被调用,且再也没有挂起的任务时,worker会自动退出
# 示例
# WebSocket
# 概念
一种浏览器与服务器间进行全双工实时通讯的网络技术,实现客户端和服务器端的长连接。
短连接与长连接的区别:短连接是每传输完一段数据便关闭,而长连接是时刻保持着连接,不会因数据传输完毕就断开
# 特点
事件驱动
异步
使用ws或者wss协议(ssl加密可以使用wss),实现真正意义上的推送
# 请求头
Upgrade:取值为websocket,指定客户端期望升级当前协议为WebSocket
Connection:取值为Upgrade,告知服务器客户端希望将当前的http(s)连接升级到另一个协议,后续会根据Upgrade字段判断是否支持客户端请求的协议升级,支持则升级
Sec-WebSocket-Version:指定客户端所使用的WebSocket协议版本
Sec-WebSocket-Key:客户端生成的一个Base64编码的随机字符串,用于进行WebSocket握手安全验证。服务器接收后,会将其与一个固定的GUID(全球唯一标识符)进行拼接,然后对拼接后的字符串进行SHA-1哈希运算,最后将结果进行Base64编码,生成一个Sec-WebSocket-Accept响应头返回给客户端。客户端接收到响应后,会进行相同的计算并验证Sec-WebSocket-Accept的值是否正确,以此确保握手过程的安全性
Sec-WebSocket-Extensions:用于协商WebSocket连接支持的扩展功能
示例如下,
GET /ws HTTP/1.1
Host: 192.168.33.1:8099
Pragma: no-cache
Cache-Control: no-cache
Origin: http://dev.1thx.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36 QQBrowser/4.3.4986.400
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.8
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: mIsurCgKrroYO7m/0QNqRg==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
2
3
4
5
6
7
8
9
10
11
12
13
# 示例
使用比较简单,直接附上链接 (opens new window)查看