[TOC]
# Web应用程序
## ApplicationCahe
通过缓存清单文件以及 ApplicationCache API,就能够控制缓存文件。
### 缓存清单文件的创建
在 html 标签的 manifest 属性中指定缓存清单文件的路径。通过 text/cache-manifest 这一 MINE Type 来发布缓存清单文件。在缓存清单文件所在的同一文件夹内创建一个名为 .htaccess 的文件并记录 AddType 目录信息,以设定支持特定扩展名的 MIME Type。
```html
<!DOCTYPE HTML>
<html manifest="sample.appcache">
....
</html>
```
.htaccess 实例
```
AddType text/cache-manifest .appcache
```
sample.appcache 实例
```
CACHE MANIFEST
# revision 1
CACHE:
./cache.js
./cache.css
```
### 缓存更新
通过注释插入版本号或是更新日期,就能实现对缓存清单文件的更新了
```
CACHE MANIFEST
# revision 2
CACHE:
./cache.js
./cache.css
```
### 对缓存更新的确认
可以通过 `applicationCache.update` 方法在启动页面意外的任意时刻执行对缓存更新的确认。
### 在线与离线
通过引用 `navigator.onLine` 来获知网络连接状态。还可以通过 online/offline 事件来监听连接状态的切换时机
# 与桌面应用的协作
## File API
File API 是一种用于获取在本地保存的文件的信息与内容的 API。
### FileReader
通过 FileReader 就可以读取文件的内容。
```javascript
var reader = new FileReader();
```
FileReader 的方法
方法 | 说明
---|---
readAsArrayBuffer(blob) | 以 ArrayBuffer 的形式读取文件
readAsBinaryString(blob) | 以二进制字符串的形式读取文件
readAsText(blob[,encoding]) | 以文本形式读取文件
readAsDataURL(blob) | 以 DataURL 的形式读取文件
abort() | 中止读取
FileReader 的事件处理程序
事件 | 说明
---|---
onloadstart | 读取开始
onprogress | 读取中
onload | 读取成功
onerror | 读取失败
onabort | 读取中止
onloadend | 读取结束(无论成功还是失败)
### 对错误的处理
通过 `onerror` 事件处理程序来捕捉读取的错误,错误原因可以通过 error.code 属性来获取
error.code 属性
属性名| 值 | 说明
---|---|---
NOT_FOUND_ERR | 1 | 没有找到文件
SECURITY_ERR | 2 | 安全性错误
ABORT_ERR | 3 | 文件的读取被中止
NOT_READBLE_ERR | 4 | 没有文件的读取权限
ENCODING_ERR | 5 | 超过了 DataURL 的尺寸限制
### 读取中的处理
通过 `progress` 事件来获知读取的进度
属性名 | 说明
---|---
lengthComputable | 如果文件的长度能够被计算则为 true,否则为 false
loaded | 已经被读取的数据尺寸
total | 读取目标的文件尺寸
### 读取文件的一部分
通过 `slice` 方法来实现文件的部分读取。返回值为 Blob 对象。
```javascript
// 读取的开始位置
var lastPos = 0;
function getDiff(file) {
// 从上一次的读取位置起,切换之后的部分
var blob = file.slice(lastPos, file.size);
// 保存本次读取的位置
lastPos = file.size;
var reader = new FileReader();
reader.onload = function(){...};
reader.readAsText(blob);
}
```
### FileReaderSync
同步读取文件内容的 API
# 存储
## localStorage 与 sessionStorage
localStorage 与 seeionStorage 的区别在于数据的生命周期。对于 localStorage 中保存的数据来说,只要没有被显示的地删除,即使浏览器或计算机执行了重启,这些数据也不会丢失。
## sessionStorage 的生命周期
共享 sessionStorage 的情况
- 通常的页面跳转时
- 在 iframe 内打开了子页面
- 从奔溃中恢复时
- 重新载入时
没有共享sessionStorage 时
- 在新窗口或新标签页中打开了页面
- 窗口被关闭后又被重新打开时
## 多个标签之间的数据同步
对于多个标签之间数据不一致的问题,则必须在合适的时机将本地变量中的数据写入 localStorage 之中以进行同步。这时需要捕获 storage 事件,在其他标签页中执行的 localStorage 更新操作同步至本地变量。
```javascript
// 在更改设定是将其写入 localStorage
function setStorage(key, value){
storage[key] = value;
localStorage[SERVICE_NAME] = JSON.stringify(storage);
}
// 将在其他标签页中进行的 localStorage 更改读入本地变量
window.onstorage = function(event){
if(event.key === SERVICE_NAME && event.newValue){
storage = JSON.parse(event.newValue);
}
}
```
## Indexed Database
在浏览器中通过 JavaScript 进行操作的功能强大数据库。提供创建用于检索的索引及事务的功能。
[参考地址](https://developer.mozilla.org/zh-CN/docs/Web/API/IndexedDB_API)
# WebSocket
WebSocket 是一种用于在服务器与客户端之间实现高效的双向通信的机制。通过 WebSocket,就能够在1个 HTTP 连接上自由地双向收发消息。与通过结合使用 XMLHTTPRequest 与 Server-sent Events 而实现的双向通信相比,这种方式具有通信效率更高、设计与实现容易等优点。
## 长轮询
一种只有在必要时才从服务器返回响应的方式。在客户端发送请求之后,服务器将会保留响应,并维持连接,因此可以在任意的时间点从服务器返回响应。而客户端在收到了响应的同时,将会再次向服务器建立连接。
## 流
通过由客户端发出的第一个请求,建立连接,并在维持该链接的同时从服务器不断向客户端返回响应。由于服务器端始终处于在发送响应的状态,因此将这种方式称为流。
## WebSocket 的执行方式
通过 WebSocket 开始双向通信时,首先需要与服务器建立连接。而用于建立连接的请求,是由客户端通过 HTTP 方式发送的。服务器将会确认连接对象的源以及协议,并发送连接许可的响应。在发送了响应之后,浏览器将会把该连接升格为 WebSocket。这一连串的流程称为握手。
此外,仅在握手时才会在通信中添加 HTTP 头部,因此有必要在握手时,执行所有使用了 UserAgent 或 Cookie 来进行的用户及设备认证。
### 连接的建立
需要在客户端执行对 WebSocket 类的构造函数调用。构造函数调用的第1个参数所指定的是将要进行连接的 WebSocket 服务器的URL,而其第2个参数则需要指定子协议名。
```javascript
var ws = new WebSocket('ws://www.foo.org:8888/bar', 'subprotocol');
```
WebSocket 可以选择 `ws://` 或 `wss://` 这两种协议。如果将协议指定为 `wss`,就能够以 TLS 对通信加密。如果没有指定端口,则将分别默认使用 80 和 443 端口。
在进行构造函数调用之后,内部将会执行握手处理。一旦建立了连接, WebSocket 实例中就会触发 `open` 事件。
### 消息的收发
要将消息发送至服务器,则需要将希望发送的数据传递至 `send` 方法的参数。如果要从服务器接收消息,则可以通过 `message` 事件对其进行捕捉。由服务器发送的数据会保存于 `message` 事件对象的 `data` 的属性之中。
```javascript
// 向服务器发送消息
ws.send('Hello, WebSocket!');
ws.onmessage = function(event) {
// 取出所收到的数据
var receivedMessage = event.data;
}
```
### 连接的切断
可以通过在客户端调用 `close` 方法,来显示地切断连接。
```javascript
ws.close();
ws.onclose = function(event) {
...
}
```
### 连接的状态确认
通过引用 WebSocket 实例的 readState 属性,来确认连接的状态。
属性 | 整数值 | 说明
---|---|---
CONNECTING | 0 | 正在连接
OPEN | 1 | 已处于连接中
CLOSING | 2 | 正在切断连接
CLOSED | 3 | 已经切断或连接失败
# Web Workers
Web Workers 是一种能够在另外的线程中创建新的 JavaScript 运行环境,以使 JavaScript 代码能够在后台处理的一种机制。如果能够视情况恰当地分离出复杂的处理,并将其置于后台运行,就能够在通过客户端进行复杂处理的同时,不妨碍用户的 UI 操作,从而开发出高可用性的 Web 应用程序。
## Web Workers 的执行方式
将客户端运行环境称为主线程,将通过Web Workers 创建的后台运行环境称为工作线程。可以在主线程中创建工作线程,且能够同时创建多个工作线程。
主线程与工作线程的运行环境是相互分离的,无法相互引用对方环境中的变量。
工作线程的环境中无法引用 document 对象。如果要进行数据的收发处理,则必须通过消息收发接口(`postMessage`和`message`)来进行。
## 工作线程的创建
在主线程中调用 Worker 构造函数来创建工作线程。
```javascript
var worker = new Worker('worker.js');
```
## 主线程一侧的消息收发
```javascript
worker.postMessage('foo');
worker.onmessage = function(event) {
var receivedMessage = event.data;
...
}
```
### 工作线程一侧的消息收发
```javascript
postMessage('foo');
onmessage = function(event) {
var receviedMessage = event.data;
...
}
```
### 工作线程的删除
```javascript
// 在主线程中删除
worker.terminate();
// 在工作线程中删除
close();
```
### 外部文件的读取
```javascript
importScripts('http://www.foo.org/external.js', 'dependent.js');
// 此时,外部 JavaScript 文件中的内容以及被分析求值
```
## 共享工作线程
1个工作线程可以被多个页面共享引用。为了区分于通常的工作线程,这种类型的工作线程被称为“共享工作线程”。能够同时在多个不同的窗口之间引用同一个共享工作线程。可以通过共享工作线程来实现窗口间的消息收发,或者以共享工作线程作为中转,将服务器连接相整合。
### 共享工作线程的创建
调用 `SharedWorker` 类的构造函数来创建共享工作线程。第1个参数和通常的工作线程一样,需要制定一个 JavaScript 文件的 URL,第2个参数所指定的则是该共享工作线程的名称。以同样的文件及名称创建的话,则会返回一共引用了相同共享工作线程的 `SharedWorker` 实例。
```javascript
// 创建共享工作线程
var worker = new SharedWorker('worker.js', 'text-worker');
```
### 共享工作线程的消息收发
使用共享工作线程进行消息的收发时,其内部使用了信道通讯,一种通过名为 MessagePort 的成组对象进行消息收发的机制。
在主线程中进行消息收发
```javascript
// 创建共享工作线程
var worker = new SharedWorker('http://www.a.com/worker.js');
// 通过 MessagePort 发送消息
worker.port.postMessage('foo');
// 通过 MessagePort 接收消息
worker.port.onmessage = function(event){
var receivedData = event.data;
...
}
```
在工作线程中进行消息收发
```javascript
// 来自于主线程的连接请求
onconnect = function(connectEvent) {
// 获取连接请求方的 MessagePort
var port = connectEvent.ports[0];
// 通过 MessagePort 发送欢迎消息
port.postMessage('hello');
// 通过 MessagePort 接收消息
port.onmessage = function(messageEvent) {
// 将所接收的数据直接回复
port.postMessage(messageEvent.data);
}
}
```
### 共享工作线程的删除
```javascript
// 在主线程中关闭连接
worker.port.close();
// 在共享线程中关闭
close();
```