HTML5學習-day02【悟空教程】
JavaScript API
基礎API提升
New Selectors
提供類似於jQuery中選擇器的API
- 通過類名查找元素
- 通過CSS語法查找元素
Element.classList
提供類似於jQuery中CSS操作的API
訪問歷史 API
在此之前我們可以通過history對象實現前進、後退和刷新之類的操作
H5中開放了更多的API:歷史狀態操作
HTML5 history API包括:
- history.pushState()方法:新增狀態
- history.replaceState()方法:替換狀態
- window.onpopstate事件:得到狀態
HTML5 history API有什麼用呢?
從Ajax翻頁的問題說起
請想象你正在看一個視頻下面的評論,在翻到十幾頁的時候,你發現一個寫得稍長,但非常有趣的評論。正當你想要停下滾輪細看的時候,手殘按到了F5。然後,頁面刷新了,評論又回到了第一頁,所以你又要重新翻一次。
再或者,你想把這個評論發給別人分享,一面給了別人頁面地址(爲什麼不直接複製呢?因爲要連帶視頻等場景啊),一面又要加一句囑咐:請翻到下面評論的第XX頁的XX樓。
這就是問題。試想一下,如果瀏覽器能記住你當前的狀態(比如看到了第十幾頁),而不是一刷新就還原,是不是就顯得智能多了?
爲什麼用Ajax?
用Ajax實現翻頁等內容切換是有原因的。在傳統的無Ajax的站點裏,頁面A和頁面B可能只有10%的地方是不同的,其他90%的內容(尤其是導航、頁腳等公用元素)都是一樣的,但卻仍然需要瀏覽器下載並顯示新的一整個頁面。而如果使用Ajax,不僅節省了瀏覽器需要下載的資源,而且無刷新切換明顯比頁面跳轉更平滑、流暢。
就視頻下面的評論來說,Ajax可以說是必須的。視頻這樣的重量級元素,動不動給你重新加載一次,不能忍。
傳統的跳轉翻頁的可取之處
傳統的不使用Ajax的站點,每一個翻頁是一個跳轉,然後你可以在瀏覽器地址欄裏看到諸如?page=2
這樣的參數。每一頁就這樣通過地址欄的URL做了標記,每一次請求,瀏覽器都會根據參數返回正確的頁碼。
所以,傳統的跳轉翻頁,刷新也不會丟失狀態。
結合兩者
現在我們就可以想到,如果在Ajax更新頁面局部內容的同時,也在地址欄的URL裏更新狀態參數,就可以做出更完美的Ajax翻頁了。
不過,JavaScript修改location
的除hash
外的任意屬性,頁面都會以新URL重新加載。而唯一不引發刷新的hash
參數並不會發送到服務器,因此服務器無法獲得狀態。
然後,HTML5 history API將解決這個問題。
介紹HTML5 history API
HTML5 history API只包括2個方法:history.pushState()
和history.replaceState()
,以及1個事件:window.onpopstate
。
history.pushState()
它的完全體是 history.pushState(stateObject, title, url)
,包括三個參數。
第1個參數是狀態對象,它可以理解爲一個拿來存儲自定義數據的元素。它和同時作爲參數的url
會關聯在一起。
第2個參數是標題,是一個字符串,目前各類瀏覽器都會忽略它(以後纔有可能啓用,用作頁面標題),所以設置成什麼都沒關係。目前建議設置爲空字符串。
第3個參數是URL地址,一般會是簡單的?page=2
這樣的參數風格的相對路徑,它會自動以當前URL爲基準。需要注意的是,本參數URL需要和當前頁面URL同源,否則會拋出錯誤。
調用pushState()
方法將新生成一條歷史記錄,方便用瀏覽器的“後退”和“前進”來導航(“後退”可是相當常用的按鈕)。另外,從URL的同源策略可以看出,HTML5 history API的出發點是很明確的,就是讓無跳轉的單站點也可以將它的各個狀態保存爲瀏覽器的多條歷史記錄。當通過歷史記錄重新加載站點時,站點可以直接加載到對應的狀態。
history.replaceState()
它和history.pushState()
方法基本相同,區別只有一點,history.replaceState()
不會新生成歷史記錄,而是將當前歷史記錄替換掉。
window.onpopstate
push的對立就是pop,可以猜到這個事件是在瀏覽器取出歷史記錄並加載時觸發的。但實際上,它的條件是比較苛刻的,幾乎只有點擊瀏覽器的“前進”、“後退”這些導航按鈕,或者是由JavaScript調用的history.back()
等導航方法,且切換前後的兩條歷史記錄都屬於同一個網頁文檔,纔會觸發本事件。
上面的“同一個網頁文檔”請理解爲JavaScript環境的document
是同一個,而不是指基礎URL(去掉各類參數的)相同。也就是說,只要有重新加載發生(無論是跳轉到一個新站點還是繼續在本站點),JavaScript全局環境發生了變化,popstate
事件都不會觸發。
popstate
事件是設計出來和前面的2個方法搭配使用的。一般只有在通過前面2個方法設置了同一站點的多條歷史記錄,並在其之間導航(前進或後退)時,纔會觸發這個事件。同時,前面2個方法所設置的狀態對象(第1個參數),也會在這個時候通過事件的event.state
返還回來。
此外請注意,history.pushState()
及history.replaceState()
本身調用時是不觸發popstate
事件的。pop和push畢竟不一樣!
如何應用
HTML5 history API的內容不多,具體如何應用它來改進Ajax翻頁呢?
首先,在服務器端添加對URL狀態參數的支持,例如?page=3
將會輸出對應頁碼的內容(後端模板)。也可以是服務器端把對應頁碼的數據給JavaScript,由JavaScript向頁面寫入內容(前端模板)。
接下來,使用history.pushState()
,在任一次翻頁的同時,也設置正確的帶參數的URL。代碼可能是這樣:
newURL = "?page=" + pageNow; history.pushState(null, "", newURL);
到此,就解決了F5刷新狀態還原的事了。
不過,還沒有結束,在瀏覽器中點擊後退,例如從?page=3
退到?page=2
,會發現沒有變化。按道理說,這時候也應該對應變化。這就要用到popstate
事件了。
爲window
添加popstate
事件,加入這種導航變化時的處理。代碼可能是這樣(jQuery):
這樣,就完成了。這樣看起來是否會覺得還挺容易的呢?在支持HTML5 history API的瀏覽器中,以上部分就已經做到了帶頁碼記錄的Ajax翻頁。
有待斟酌的兼容性問題
根據[caniuse][]上的數據,IE10+及其他主流瀏覽器都支持HTML5 history API。爲保證不支持的瀏覽器不報錯,可以加入是否支持HTML5 history API的判斷:
這樣,一個Ajax翻頁,在支持HTML5 history API的瀏覽器上,將會智能地保存當前頁碼信息,而不支持的瀏覽器仍然可以正常使用,只是不保存頁碼信息(就像改進前那樣)。我認爲,按照“漸進增強”的思路,這樣就是最好的了,也就是:只使用較少的代碼優化高級瀏覽器的使用體驗。
如果真的想要在各類瀏覽器裏都表現一致,擁有這樣的記錄功能呢?
這時候推薦使用Benjamin Lupton的[History.js][],它提供和HTML5 history API近似的api,會在不支持的瀏覽器裏回退到hash形式去處理歷史記錄。儘管爲了兼容這種hash的回退形式你可能要額外做點事(hash不會發送到服務器端),但它確實可以讓你做到更廣範圍的兼容。
HTML5 history API並不完美
即使只考慮支持HTML5 history API的瀏覽器,它們對HTML5 history API的一些細節處理也會有差異和問題。History.js提供的只針對HTML5瀏覽器的版本,仍然包含了不少處理兼容問題的代碼。
但是,不完美也沒有關係。以我的測試結果,本文所介紹的簡單的寫法,就可以在絕大部分支持HTML5 history API的瀏覽器上正常運行。如果你擔心有哪些瀏覽器會有潛在問題,去測試那個瀏覽器就可以了。你最後用於兼容處理的自寫代碼很可能遠比一個JavaScript庫少得多,畢竟,你也不一定會喜歡額外引入一個JavaScript庫來完成一個功能吧。
一些相關內容
地址欄裏的hash曾是過去被廣泛用來記錄頁面狀態的標記,你可以閱讀[W3C Blog的這篇文章][]瞭解它的經歷。
現在可以在不刷新的狀況下操作瀏覽器地址欄和歷史記錄了,那同一站點的普通鏈接跳轉是否都可以轉變爲Ajax來提升使用體驗?是的,而且已經有了pjax[]這些專門完成這個功能的作品。
不只是翻頁,HTML5 history API將尤其適合用在大量使用Ajax、包含多個視圖的單頁應用。
爲一個頁面的每一個狀態都生成一條歷史記錄不一定合適(會讓用戶的歷史記錄變多變亂),酌情使用replaceState()
而不是pushState()
來控制歷史記錄的數量。
結語
HTML5 history API簡單易學,不多的幾行代碼就可以做到“狀態記錄”這個小小的改進,如果可以由你選擇“漸進增強”,它還真的可以上線!
演示
IE10+和其他主流瀏覽器
全屏 API
Please press F
基礎API提升 學習目標
- 熟練使用新選擇器和ClassList
- 對訪問歷史操作有基本的瞭解,爲以後開發SPA做準備
- 瞭解全屏API
網頁存儲
通過以下API,可以輕鬆構建離線H5應用
Application Cache
就是讓網頁可以離線訪問的技術
演示
什麼是Application Cache API?
HTML5提供了一系列的特性來支持離線應用:
- application cache
- localStrorage
- web SQL & indexed database
- online/offline events
本文要講的是application cache。傳統的web程序中瀏覽器也會對資源文件進行cache,但是並不是很可靠,有時起不到預期的效果。而HTML5中的application cache支持離線資源的訪問,爲離線web應用的開發提供了可能。
哪些瀏覽器支持Application Cache API?
目前支持application cache的瀏覽器如下:
使用application cache能帶來什麼好處?
使用application cache能夠帶來以下幾點收益:
- 用戶可以在離線時繼續使用
- 緩存到本地,節省帶寬,加速用戶體驗的反饋
- 減輕服務器的負載
如何使用application cache
要使用application cache,主要用到緩存清單文件:manifest,該文件告訴瀏覽器需要緩存哪些資源
manifest文件結構
CACHE MANIFEST# 以上折行必需要寫 CACHE: # 這部分寫需要緩存的資源文件列表 # 可以是相對路徑也可以是絕對路徑index.html index.css images/logo.png js/main.js http://img.baidu.com/js/tangram-base-1.5.2.1.jsNETWORK: # 可選 # 這一部分是要繞過緩存直接讀取的文件login.php FALLBACK: # 可選 # 這部分寫當訪問緩存失敗後,備用訪問的資源 # 每行兩個文件,第一個是訪問源,第二個是替換文件*.html /offline.html
manifest文件使用
寫完一個manifest文件之後,像下面這樣在你的web頁面中引用他
<html manifest="demo.cache"> ...</html>
其中文件後綴可以自定義,但是需要在服務器中進行相應配置,指定其爲text/cache-manifest MIME 類型文件
在apache中定義如下:
AddType text/cache-manifest .cache
做完以上工作,你的應用程序就可以使用application cache了。
更新緩存的方式
開發人員想要通知客戶的瀏覽器更新application cache的方法有以下兩類:
更新manifest文件
瀏覽器發現manifest文件本身發生變化,便會根據新的manifest文件去獲取新的資源進行緩存。
當manifest文件列表並沒有變化的時候,我們通常通過修改manifest註釋的方式來改變文件,從而實現更新。
通過javascript操作
瀏覽器提供了applicationCache供js訪問,通過對於applicationCache對象的操作也能達到更新緩存的目的。
var appCache = window.applicationCache; appCache.update(); //嘗試更新緩存...if (appCache.status == window.applicationCache.UPDATEREADY) { appCache.swapCache(); //更新成功後,切換到新的緩存}
另外,用戶想要更新緩存,可以通過刪除緩存文件的方式來清除緩存。
applicationCache對象
該對象是window對象的直接子對象,window.applicationCache
基類:DOMApplicationCache
事件列表:
事件 | 接口 | 觸發條件 | 後續事件 |
---|---|---|---|
checking | Event | 用戶代理檢查更新或者在第一次嘗試下載manifest文件的時候,本事件往往是事件隊列中第一個被觸發的 | noupdate, downloading, obsolete, error |
noupdate | Event | 檢測出manifest文件沒有更新 | 無 |
downloading | Event | 用戶代理髮現更新並且正在取資源,或者第一次下載manifest文件列表中列舉的資源 | progress, error, cached, updateready |
progress | ProgressEvent | 用戶代理正在下載資源manifest文件中的需要緩存的資源 | progress, error, cached, updateready |
cached | Event | manifest中列舉的資源已經下載完成,並且已經緩存 | 無 |
updateready | Event | manifest中列舉的文件已經重新下載並更新成功,接下來js可以使用swapCache()方法更新到應用程序中 | 無 |
obsolete | Event | manifest的請求出現404或者410錯誤,應用程序緩存被取消 | 無 |
error | Event | manifest的請求出現404或者410錯誤,更新緩存的請求失敗 | 無 |
manifest文件沒有改變,但是頁面引用的manifest 文件沒有被正確地下載 | |||
在取manifest列舉的資源的過程中發生致命的錯誤 | |||
在更新過程中manifest文件發生變化 | 用戶代理會嘗試立即再次獲取文件 |
屬性:status 返回緩存的狀態
可選值 | 匹配常量 | 描述 |
---|---|---|
0 | appCache.UNCACHED | 未緩存 |
1 | appCache.IDLE | 閒置 |
2 | appCache.CHECKING | 檢查中 |
3 | appCache.DOWNLOADING | 下載中 |
4 | appCache.UPDATEREADY | 已更新 |
5 | appCache.OBSOLETE | 失效 |
方法
方法名 | 描述 |
---|---|
update() | 發起應用程序緩存下載進程 |
abort() | 取消正在進行的緩存下載 |
swapcache() | 切換成本地最新的緩存環境 |
manifest解析機制
注意事項
- 站點離線存儲的容量限制是5M
- 如果manifest文件,或者內部列舉的某一個文件不能正常下載,整個更新過程將視爲失敗,瀏覽器繼續全部使用老的緩存
- 引用manifest的html必須與manifest文件同源,在同一個域下
- 在manifest中使用的相對路徑,相對參照物爲manifest文件
- CACHE MANIFEST字符串應在第一行,且必不可少
- 系統會自動緩存引用清單文件的 HTML 文件
- manifest文件中CACHE則與NETWORK,FALLBACK的位置順序沒有關係,如果是隱式聲明需要在最前面
- FALLBACK中的資源必須和manifest文件同源
- 當一個資源被緩存後,該瀏覽器直接請求這個絕對路徑也會訪問緩存中的資源。
- 站點中的其他頁面即使沒有設置manifest屬性,請求的資源如果在緩存中也從緩存中訪問
- 當manifest文件發生改變時,資源請求本身也會觸發更新
整體介紹了一下appcache,接下來會對appcache做一些黑盒測試,以便我們瞭解更多。
這個demo中主要涉及到3類資源,兩個頁面,我們觀察3類資源在不同的場景下瀏覽器的appcache策略。
demo代碼:
test1.html如下:
<html manifest="manifest.appcache"><head> <meta http-equiv="Content-Type" content="text/html;charset=utf-8"> <title>demo</title></head><script type="text/javascript" src="js/index.js"></script><script type="text/javascript"> applicationCache.onchecking = function(){ console.log("checking") } applicationCache.ondownloading = function(){ console.log("dowload") } applicationCache.onnoupdate = function(){ console.log("noupdate") } applicationCache.onprogress = function(){ console.log("progress") } applicationCache.oncached = function(){ console.log("cached") } applicationCache.onupdateready = function(){ console.log("updateready") } applicationCache.onobsolete = function(){ console.log("obsolete") } applicationCache.onerror = function(){ console.log("error") }</script><link rel="stylesheet" type="text/css" href="css/index.css" media="all" /><body> <div id="test">此處觀察樣式效果<div> <img src="img/index.jpg" /> <img src="img/no-cache.jpg"></body></html>
test2.html如下:
demo 此處觀察樣式效果
- 我們在兩個頁面中對於applicationCache對象的事件進行了監聽,並將當前觸發的事件名輸出到console中,以便我們瞭解發生了什麼
- 兩者區別在於,一個引用了manifest,一個沒有,用於進行對比。
- js和css初始化爲空,用於觀察效果
結論及場景
1.application cache並不因服務器上資源改變而改變,只有manifest改變纔會觸發重新download,並且是所有cache文件均重新獲取
正常載入test1.html時,console輸出如下:
checking /html5/appcache/:13 noupdate /html5/appcache/:37
當js,css和圖片發生變化時,載入test1.html ,console不變:
checking /html5/appcache/:13 noupdate /html5/appcache/:37
當manifest文件進行修改後,console如下:
checking /html5/appcache/:13 dowload /html5/appcache/:27 5 progress /html5/appcache/:49 updateready /html5/appcache/:67
2.對於另一個沒有引用manifest文件的html,當它加載時,不會觸發applicationCache的任何事件,但是會使用緩存。
頁面加載的時候。console輸入爲空
修改服務器js,css等資源,頁面中沒有變化,修改manifest文件後,刷新頁面,資源修改的效果出現。
3.直接請求資源的絕對路徑,只要該url被緩存過,那麼所有的訪問均是該資源的緩存,而與引用所在的宿主頁面是否有manifest沒有關係
給js中寫上alert("更新"),訪問該資源的url,結果沒有變化
更新manifest之後,該js的訪問得到更新
4.js和css,圖片文件的本身的訪問,均會進行checking
直接在地址欄輸入一個緩存的js文件,console顯示如下:
Document was loaded from Application Cache with manifest http://localhost/html5/appcache/manifest.appcache Application Cache Checking event Application Cache NoUpdate event
修改manifest文件後,再次訪問這個資源。顯示如下:
Document was loaded from Application Cache with manifest http://localhost/html5/appcache/manifest.appcache Application Cache Checking event Application Cache Downloading event Application Cache Progress event (0 of 4) http://localhost/html5/appcache/css/index.css Application Cache Progress event (1 of 4) http://localhost/html5/appcache/js/index.js Application Cache Progress event (2 of 4) http://localhost/html5/appcache/ Application Cache Progress event (3 of 4) http://localhost/html5/appcache/img/index.jpg Application Cache Progress event (4 of 4) Application Cache UpdateReady event
所有的資源,均被重新下載
經過反覆試驗後,我們可以總結出以下瀏覽器在應用緩存方面處理url的邏輯策略:
Web Storage
localStorage & sessionStorage
演示
Web StorageAPI
web storage內容
web storage提供在瀏覽器端通過key/value的方式存儲數據。包括以下兩部分:
- session storage(會話級別的存儲,會話結束後失效)
- local storage(持久性存儲,用戶主動刪除或js操作清空)
web storage優勢
web storage的提出的初衷主要是爲了解決cookie在數據存儲時的一些不足。和cookie相比,web storage主要有以下幾點優勢:
- 存儲空間大,默認5m
- 節省帶寬,不用在每次請求中發送到服務器端
- 操作簡便,封裝了很多便捷的操作方法
- 數據獨立性強
支持web Storage的瀏覽器
- chrome4+
- FF3.5+
- IE8+
- safari4+
- opera10.5+
JS操作對象
使用 local storage和session storage主要通過在js中操作這兩個對象來實現,分別爲window.localStorage和window.sessionStorage.
這兩個對象均是Storage類的兩個實例,自然也具有Storage類的屬性和方法。
Storage類的相關成員如下:
成員名 | 方法參數 | 描述 |
---|---|---|
length | 屬性 | 獲取存儲數據的條數 |
key(n) | n:索引值 | 根據索引值,返回鍵名 |
getItem(key) | key:鍵名 | 根據鍵名,獲取數據值 |
setItem(key,value) | key:鍵名 value:鍵值 | 根據鍵名和鍵值設置數據項,如果鍵名已經存在,則覆蓋值 |
removeItem(key) | key:鍵名 | 根據鍵名刪除一個數據項 |
clear() | 無 | 清空當前的Storage對象 |
所有支持web Storage的瀏覽器均實現了以上標準方法,另外IE8還自己實現了remainingSpace用於查看剩餘的存儲空間
事件:
onstorage,當發生存儲相關操作的時候觸發
標準中事件對象的屬性:
事件屬性 | 描述 |
---|---|
key | 返回發生改變的數據項的鍵名 默認空字符串 |
oldValue | 返回發生改變的數據項的舊值 默認null |
newValue | 返回發生改變的數據項的新值 默認null |
url | 返回發生改變的數據的頁面所對應的url 默認空字符串 |
storageArea | 返回代表所屬的storage對象 默認null |
其中,webkit內核的瀏覽器(Chrome、Safari)以及Opera是完全遵循標準的,IE8則只實現了url,Firefox下則均未實現。
各個瀏覽器對於事件註冊的對象也不一致。其中IE和FF是document對象,chrome和opera是window對象,safari是body。
local storage的漸進設計方案
對於不支持的瀏覽器,可以做漸進設計,考慮的順序如下:
- localStorage
- globalStorage
- userdata
- Cookie
session storage 的會話
session storage主要存儲會話級別的數據,會話結束後,數據自動銷燬。
關於瀏覽器會話在頁面跳轉時的理解,各個瀏覽器實現有些差異,具體表現如下:
瀏覽器 | 原窗口 | target="_blank" | window.open | ctrl+click | 跨域訪問 |
---|---|---|---|---|---|
IE8 | 是 | 是 | 是 | 是 | 否 |
FF3.6 | 是 | 是 | 是 | 否 | 否 |
chrome5 | 是 | 是 | 是 | 否 | 否 |
safari4 | 是 | 否 | 是 | 否 | 否 |
opera | 是 | 否 | 否 | 否 | 否 |
web storage的安全注意事項:
- 明文存儲,不要存敏感信息
- 不能抵禦xss漏洞攻擊
- 對於存儲的數據要嚴格過濾,防止自身產生存儲型xss攻擊
- 容易遭受跨目錄攻擊
- 容易遭受DNS欺騙攻擊
IndexedDB
HTML5中的NoSQL數據庫
indexedDB爲何物
在使用一個技術之前,先搞清楚它是什麼,這對你的理解很重要,從DB就可以看出,它肯定是一個數據庫,而說到數據庫,有兩種不同類型的數據庫,就是關係型數據庫和非關係型數據庫,關係型數據庫如Mysql、Oracle等將數據存儲在表中,而非關係型數據庫如Redis、MongoDB等將數據集作爲個體對象存儲。indexedDB就是一個非關係型數據庫,它不需要你去寫一些特定的sql語句來對數據庫進行操作,因爲它是nosql的,數據形式使用的是json,
indexedDB出現的意義
也許熟悉前端存儲的會說,不是有了LocalStorage和Cookies嗎?爲什麼還要推出indexedDB呢?其實對於在瀏覽器裏存儲數據,你可以使用cookies或local storage,但它們都是比較簡單的技術,而IndexedDB提供了類似數據庫風格的數據存儲和使用方式。
首先說說Cookies,英文直接翻譯過來就是小甜點,聽起來很好吃,實際上並不是,每次HTTP接受和發送都會傳遞Cookies數據,它會佔用額外的流量。例如,如果你有一個10KB的Cookies數據,發送10次請求,那麼,總計就會有100KB的數據在網絡上傳輸。Cookies只能是字符串。瀏覽器裏存儲Cookies的空間有限,很多用戶禁止瀏覽器使用Cookies。所以,Cookies只能用來存儲小量的非關鍵的數據。
其次說說LocalStorage,LocalStorage是用key-value鍵值模式存儲數據,但跟IndexedDB不一樣的是,它的數據並不是按對象形式存儲。它存儲的數據都是字符串形式。如果你想讓LocalStorage存儲對象,你需要藉助JSON.stringify()能將對象變成字符串形式,再用JSON.parse()將字符串還原成對象。但如果要存儲大量的複雜的數據,這並不是一種很好的方案。畢竟,localstorage就是專門爲小數量數據設計的,所以它的api設計爲同步的。而IndexedDB很適合存儲大量數據,它的API是異步調用的。IndexedDB使用索引存儲數據,各種數據庫操作放在事務中執行。IndexedDB甚至還支持簡單的數據類型。IndexedDB比localstorage強大得多,但它的API也相對複雜。對於簡單的數據,你應該繼續使用localstorage,但當你希望存儲大量數據時,IndexedDB會明顯的更適合,IndexedDB能提供你更爲複雜的查詢數據的方式。
indexedDB的特性
1.對象倉庫
有了數據庫後我們自然希望創建一個表用來存儲數據,但indexedDB中沒有表的概念,而是objectStore,一個數據庫中可以包含多個objectStore,objectStore是一個靈活的數據結構,可以存放多種類型數據。也就是說一個objectStore相當於一張表,裏面存儲的每條數據和一個鍵相關聯。我們可以使用每條記錄中的某個指定字段作爲鍵值(keyPath),也可以使用自動生成的遞增數字作爲鍵值(keyGenerator),也可以不指定。選擇鍵的類型不同,objectStore可以存儲的數據結構也有差異。
鍵類型 | 存儲數據 |
---|---|
不使用 | 任意值,但是沒添加一條數據的時候需要指定鍵參數 |
keyPath | 任意值,但是沒添加一條數據的時候需要指定鍵參數 |
keyGenerator | 任意值 |
都使用 | Javascript對象,如果對象中有keyPath指定的屬性則不生成新的鍵值,如果沒有自動生成遞增鍵值,填充keyPath指定屬性 |
如上圖,有一個用於保存person的object Store,這個倉庫的鍵就是person的ID值。
2. 事務性
在indexedDB中,每一個對數據庫操作是在一個事務的上下文中執行的。事務範圍一次影響一個或多個object stores,你通過傳入一個object store名字的數組到創建事務範圍的函數來定義。例如:db.transaction(storeName, \'readwrite\'),創建事務的第二個參數是事務模式。當請求一個事務時,必須決定是按照只讀還是讀寫模式請求訪問。
3. 基於請求
對indexedDB數據庫的每次操作,描述爲通過一個請求打開數據庫,訪問一個object store,再繼續。IndexedDB API天生是基於請求的,這也是API異步本性指示。對於你在數據庫執行的每次操作,你必須首先爲這個操作創建一個請求。當請求完成,你可以響應由請求結果產生的事件和錯誤。
4. 異步
在IndexedDB大部分操作並不是我們常用的調用方法,返回結果的模式,而是請求—響應的模式,所謂異步API是指並不是這條指令執行完畢,我們就可以使用request.result來獲取indexedDB對象了,就像使用ajax一樣,語句執行完並不代表已經獲取到了對象,所以我們一般在其回調函數中處理。
indexedDB怎麼玩
IndexedDB 鼓勵使用的基本模式如下所示:
- 打開數據庫並且開始一個事務。
- 創建一個 object store。
- 構建一個請求來執行一些數據庫操作,像增加或提取數據等。
- 通過監聽正確類型的 DOM 事件以等待操作完成。
- 在操作結果上進行一些操作(可以在 request 對象中找到)
接下來如果想要理解indexedDB具體怎麼玩,最好的方法就是創建一個簡單的web應用:把人的姓名、電話、地址存儲在IndexedDB裏。IndexedDB裏提供了簡單的增、刪、改、查接口,界面如下:
1.打開數據庫
a) 首先,你需要知道你的瀏覽器是否支持IndexedDB。
var indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.msIndexedDB;if(!indexedDB) { console.log("你的瀏覽器不支持IndexedDB"); }
b) 創建請求打開indexedDB:一旦你的瀏覽器支持IndexedDB,我們就可以打開它。你不能直接打開IndexedDB數據庫。IndexedDB需要你創建一個請求來打開它。
var request = indexedDB.open(name, version);
第一個參數是數據庫的名稱,第二個參數是數據庫的版本號。版本號可以在升級數據庫時用來調整數據庫結構和數據。但你增加數據庫版本號時,會觸發onupgradeneeded事件,這時可能會出現成功、失敗和阻止事件三種情況:
request.onerror = function(e) { console.log(e.currentTarget.error.message); }; request.onsuccess = function(e) { myDB.db = e.target.result; console.log(\'成功打開DB\'); }; request.onupgradeneeded = function(e) { var db = e.target.result; if (!db.objectStoreNames.contains(\'person\')) { console.log("我需要創建一個新的存儲對象"); //如果表格不存在,創建一個新的表格(keyPath,主鍵 ; autoIncrement,是否自增),會返回一個對象(objectStore) var objectStore = db.createObjectStore(\'person\', { keyPath: "id", autoIncrement: true }); //指定可以被索引的字段,unique字段是否唯一 objectStore.createIndex("name", "name", { unique: false }); objectStore.createIndex("phone", "phone", { unique: false }); } console.log(\'數據庫版本更改爲:\' + version); };
onupgradeneeded事件在第一次打開頁面初始化數據庫時會被調用,或在當有版本號變化時。所以,你應該在onupgradeneeded函數裏創建你的存儲數據。如果沒有版本號變化,而且頁面之前被打開過,你會獲得一個onsuccess事件。
2. 添加數據
a) 首先需要創建一個事務,並要求具有讀寫權限
var transaction = db.transaction(storeName, \'readwrite\');
b) 獲取objectStore,再調用add方法添加數據
var store = transaction.objectStore(storeName); var request = store.get(key); request.onsuccess = function(e) { data = e.target.result; console.log(student.name); };
3.刪除數據
刪除跟新增一樣,需要創建事務,然後調用刪除接口,通過key刪除對象。
var transaction = db.transaction(storeName, \'readwrite\'); var store = transaction.objectStore(storeName); store.delete(key);
4.查找數據
a) 按key查找
開啓事務,獲取objectStore,調用往get()方法,往方法裏傳入對象的key值,取出相應的對象
var transaction = db.transaction(storeName, \'readwrite\'); var store = transaction.objectStore(storeName); var request = store.get(key); request.onsuccess = function(e) { data = e.target.result; console.log(student.name); };
我們可以在創建object store的時候指明索引,使用object store的createIndex創建索引,方法有三個參數:索引名稱、索引屬性字段名、索引屬性值是否唯一。
b) 使用索引查找
objectStore.createIndex("name", "name", { unique: false});
如上代碼中,我們建好了name索引,就可以用該索引來進行查詢了:
var transaction = db.transaction(storeName); var store = transaction.objectStore(storeName); var index = store.index(search_index); index.get(value).onsuccess = function(e) { data = e.target.result; console.log(student.id); }
c) 遊標遍歷數據
對數據庫熟悉的同學很好理解遊標的作用,有了數據庫object store的遊標,我們就可以利用遊標遍歷object store了。
var transaction = db.transaction(storeName); var store = transaction.objectStore(storeName); var request = store.openCursor(); //打開遊標 var dataList = new Array(); var i = 0; request.onsuccess = function(e) { var cursor = e.target.result; if (cursor) { console.log(cursor.key); dataList[i] = cursor.value; console.log(dataList[i].name); i++; cursor.continue(); } data = dataList; };
4.更新對象
更新對象,首先要把它取出來,修改,然後再放回去。
var transaction = db.transaction(storeName, \'readwrite\'); var store = transaction.objectStore(storeName); var request = store.get(key); request.onsuccess = function(e) { var data = e.target.result; for (a in newData) { //除了keypath之外 data.a = newData.a; } store.put(data); };
5.關閉與刪除數據庫
關閉數據庫可以直接調用數據庫對象的close方法
function closeDB(db) { db.close(); }
刪除數據庫使用數據庫對象的deleteDatabase方法
function deleteDB(name) { indexedDB.deleteDatabase(name); }
總結
以上就是indexedDB的一些基本概念以及使用,由於篇幅原因,還有一些更深入的細節沒有介紹,比如indexedDB的遊標結合索引,發揮其真正的優勢,有興趣的小夥伴可以繼續深入研究,還有就是要注意瀏覽器的支持問題,IE9以及更早的版本並不支持,火狐和谷歌瀏覽器沒有問題,推薦使用,文章如果紕漏或者不足,歡迎指正~
WebSQL
HTML5中的關係型數據庫
簡介
Web SQL數據庫API實際上未包含在HTML 5規範之中,它是一個獨立的規範,它引入了一套使用SQL操作客戶端數據庫的API。這些 SQL 語句可以直接在 js中編寫運行,並且帶有基本的數據庫事務性的支持。
兼容瀏覽器
- chrome 17+
- Safari5+
- opera11.6+
- iOS Safari3.2+
- Opera Mobile11.0+
- Android Browser2.1+
接口
var db = openDatabase(\'mydb\', \'1.0\', \'Test DB\', 2 * 1024 * 1024); db.transaction(function (tx) { tx.executeSql(\'CREATE TABLE IF NOT EXISTS LOGS (id unique, log)\'); tx.executeSql(\'INSERT INTO LOGS (id, log) VALUES (1, "foobar")\'); tx.executeSql(\'INSERT INTO LOGS (id, log) VALUES (2, "logmsg")\'); });
openDatabase 創建/打開數據庫,返回數據庫的引用
db.transaction 執行數據庫事務
tx.executeSql 在事務中執行sql語句
示例
創建數據庫
function initDB(){ var myDB = null; try { if (!window.openDatabase) { // 當前瀏覽器沒有數據庫支持 alert(\'db not supported\'); } else { var shortName = \'testdb\'; var version = \'1.0\'; var displayName = \'test offline database\'; var maxSize = 65536; // 字節 myDB = openDatabase(shortName, version, displayName, maxSize); } } catch(e) { // 這裏開始異常處理 . if (e == INVALID_STATE_ERR) { // 數據庫版本異常 . alert("Invalid database version."); } else { alert("Unknown error "+e+"."); } } // 返回創建好的數據庫實例 return myDB; }
創建表
function createTables(db){ db.transaction( function (transaction) { transaction.executeSql(\'CREATE TABLE IF NOT EXISTS User(name TEXT, age INTEGER);\', [], function(result){}, function(tx,error){}); } ); }
執行插入語句
db.transaction( function (transaction) { transaction.executeSql(\'INSERT INTO User values(?,?)\',[“Mark”, 60], function(result){}, function(tx,error){}); })
執行查詢語句
db.transaction( function (transaction) { transaction.executeSql(\'SELECT * FROM User WHERE name=?\', [name], function(result){}, function(tx,error){}); })
執行刪除語句
db.transaction( function (transaction) { transaction.executeSql(\'DELETE FROM User where name=?\',[name], function(result){}, function(tx,error){}); });
網頁存儲 學習目標
- 熟練使用Application Cache
- 熟練使用本地存儲
文件系統
提供客戶端本地操作文件的可能
File API
通過file表單或拖放操作選擇文件
還可以通過JavaScript讀取文件的名稱、大小、類型、和修改時間
演示
FileReader
單只是讀取文件信息沒意思,讀內容
FileReader就是用來讀取本地文件的對象
演示
文件系統 學習目標
- 熟練使用File API
- 利用FileReader在頁面本地預覽
拖放操作
詳解:https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/Drag_operations
網頁內拖放
文件拖入
拖放操作 學習目標
- 學會使用ondragenter
- 學會使用ondragover
- 學會使用ondragleave
- 學會使用ondrop
設備信息訪問
HTML5提供了讓我們可以訪問設備的硬件信息API
網絡狀態
對於離線應用,經常需要獲取當前的網絡連接狀態
現在瀏覽器支持比較好的是是否在線
重力感應方向控制
獲取移動設備的重力感應信息
DeviceOrientation事件規範
詳解https://www.w3.org/html/ig/zh/wiki/DeviceOrientation%E4%BA%8B%E4%BB%B6%E8%A7%84%E8%8C%83
加速度計
座標系
地理圍欄
獲取設備所在位置的座標
演示
設備信息訪問 學習目標
- 課後嘗試使用地理位置API結合百度地圖做案例
- 可以寫一個搖一搖