[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();
```