目錄:
如果你對GmailAssist感興趣,可以在chrome商店中搜索“Gmail助手”,或點擊這里直接訪問商店來安裝試用;
如果你對GmailAssist的源碼感興趣,可以在我的GitHub上查看它的源碼。
一、數據本地存儲常用的兩種方式
1. HTML5 的 localStorage
在第二篇中說過,chrome擴展中的腳本都可以調用H5的一切API,自然localStorage也不例外。而localStorage的兩種形式:sessionStorage 和 localStorage 分別可以看成臨時和永久存儲:后者永久留在硬盤上,除非你在程序里remove掉,或者手動去刪掉;而前者,對在chrome中而言,本地存的數據的生存時間就等於相應標簽的生存時間。(這基本上是二者的唯一區別)
localStorage 有 setItem, getItem, removeItem, key, clear 這5個方法。localStorage中都是以 key/value的形式來存儲數據的,存儲的數據類型都是字符串。並且是明文,沒有加密(所以用戶密碼啥的可不能存啊)。
GmailAssist開發初期,OAuth2的認證一開始搞得我一頭霧水,好不容易大概懂了原理,又覺得自己很難很好地拿js實現一個認證的模塊。Chrome給擴展程序的API中雖然有OAuth2相關的接口,但感覺好像不是很好用(現在回過頭來看,只是自己當時理解有問題,另外當時偷懶了沒去好好找好好學google給的例子程序)。於是從網上找來了一個現成的輪子(就是這個),源碼和使用說明都很詳細,就直接拿來用了。現在有了對js語法的進一步理解,回頭看看已經能清晰理解這份源碼了。其中保存授權token等信息都是直接使用的 localStorage,查了查發現還是很簡單好用的。可以簡單理解為大號的cookie。(cookie允許4K,localStorage允許5M)。
可以通過 localStorage 的API們訪問數據,也可以直接像訪問js變量一樣訪問。下面是從網上摘抄的例子:
//可以像對象字面量那樣使用Web Storage: localStorage.fresh = “vfresh.org”; //設置一個鍵值 var a = localStorage.fresh; //獲取鍵值 delete localStorage['fresh']; //刪除鍵值 //或者使用它的API: localStorage.setItem(“fresh”,“vfresh.org”); //設置一個鍵值 localStorage.getItem(“fresh”); //獲取一個鍵值 localStorage.key(0); //獲取指定下標的鍵的名稱(如同Array) localStorage.removeItem(“fresh”); //刪除一個鍵值 localStorage.clear(); //清空storage
注意一點!雖然所以腳本都可以訪問 localStorage 接口,但訪問時仍然是基於當前的域的。也就是說 content script 們和 background script 是無法通過 localStorage 訪問到同一塊本地數據的!(前者域是當前瀏覽頁面站點,后者是擴展程序自己的域“chrome://extension-id/”,如下圖是localStorage 所存放的本地文件夾,從命名就可以看出這些文件對應的域不同)
最后說一下,localStorage 既然是永久保存在本地的,那它在硬盤的什么位置呢?如果我想直接看里面存的值,該怎么看呢?
硬盤位置:C:\Users\your_Username\AppData\Local\Google\Chrome\User Data\Default\Local Storage 。里面一個個的文件可以用SQLite打開查看。更方便的一個途徑是直接在Chrome中查看和修改:
打開chrome –> 訪問chrome://extensions/ (或從瀏覽器的設置中,找到“擴展程序”)-> 點擊你要看的擴展程序相關信息里的“背景頁”-> 在 resource 中的localStorage 處即可查看,雙擊即可修改。(如下圖)
2. Chrome.storage
GmailAssist中未涉及,就不舉例細說了,詳細內容可以看官方文檔。只談它和 localStorage 有什么異同:
同:
- 可以認為 chrome.storage 本身就是對 localStorage 的一個封裝。所以顯然,后者的功能它都有。
- 它存的也是明文。
異:(我直接摘抄了漢化的文檔)
這一 API 為擴展程序的存儲需要而特別優化,它提供了與 localStorage API 相同的能力,但是具有如下幾個重要的區別:
- 用戶數據可以通過 Chrome 瀏覽器的同步功能自動同步(使用
storage.sync
)。 - 您的擴展程序的內容腳本可以直接訪問用戶數據,而不需要后台頁面。
- 即使使用分離式隱身行為,用戶的擴展程序設置也會保留。
- 它是異步的,並且能夠進行大量的讀寫操作,因此比阻塞和串行化的
localStorage
API 更快。 - 用戶數據可以存儲為對象(
localStorage
API 以字符串方式存儲數據)。 - 可以讀取管理員為擴展程序配置的企業策略(使用
storage.managed
和架構)。
3. 何時用哪個
分別說下這兩種方式的優勢劣勢,選擇就很顯然了。
localStorage:
優勢:可以像普通的js變量一樣使用,簡單方便。
劣勢:
- content script 無法和 background 共同訪問一個域的數據,只能通過第二篇中提過的 sendMessage 來交流數據;
- 不像chrome.storage 是用異步方式訪問數據,localStorage相對在訪問數據略多時會比 chrome.storage 慢;
- 隱身模式下無法訪問數據;
- 如果擴展程序的一些設置參數是用 localStorage 保存在本地的,用戶在其他計算機上無法獲得這份設置的同步備份,而chrome.storage通過 sync的設置就可以實現這種同步;
- 存儲數據都是字符串,若需要對象,還需要手動轉一下(雖然也不麻煩吧…)。
chrome.storage:
優勢:(就和上面的劣勢相對嘛)
- content script可以使用.storage 接口來訪問擴展程序的域的數據;
- 異步訪問,快;
- 隱身模式下仍可以訪問數據;
- 聯網並登陸chrome時,將自動同步 sync 域的數據;
- 可以直接存對象類型的數據。
劣勢:異步訪問導致需要回調函數來處理獲取的數據,不能像localStorage那樣直接像js變量一樣訪問。
4. 還有一種方式 Web SQL
這種我沒用過,不多提了。查過一些與它相關的東西,我的感覺是它至少對於開發 GmailAssist 這種本地存東西不多,而且沒有什么需要在本地執行復雜查詢的擴展程序而言,不合適。
二、下載
chrome 擴展程序要發起文件下載,必須通過chrome.downloads API 來完成。沒什么不好理解的地方,要用的話就好好看看文檔就都明白了。我嘗試寫寫也感覺寫不出啥,無非是拷貝。所以直接給出這個我認為已經寫得很清楚的電子書章節的鏈接吧。
結合GmailAssist 說一下,我的下載功能的實現。
需求是:希望用戶可以在附件列表中勾選想要下載的項目,進行批量下載。
實現是:通過一個表,每行給個復選框,然后把每個復選框對應的附件的下載地址(這個地址是通過gmail API獲取郵件信息中的partid來生成的,更具體的我會在說gmail API時提到)作為參數,來調用 chrome.downloads.download 方法。但 partid 是在content script中獲取的,下載地址自然地就在content script中拼出來了,然而 content script能調用的chrome.* API中很不幸地沒有包括 .downloads 。咋整?誰能調這個API就把下載的邏輯寫在誰那,然后通過 sendMessage 把 url 傳給它唄。
於是我的 background.js 中就有如下代碼:
chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) { if (message.url) { chrome.downloads.download({ url: message.url, conflictAction: 'uniquify', saveAs: false }); } else if (...) { ...//其他的必須在后台處理的邏輯 } });
當然前提是必須在 manifest.json 中聲明使用 download 接口的權限:
{ ... "permissions": [ "downloads" ], ... }
然后在 content.js 中:
btnDownload.onclick = function () { var id2 = 0;//id2用於遍歷附件列表,找出誰被用戶選中了,需要下載 for (id2 = 0; id2 < visibleRows.length; id2++) { var chebox = document.getElementById("checkbox_" + id2); if (chebox.checked == true) { var url = 'https://mail.google.com/mail/u/0/?ui=2&ik=undefined&view=att&th=' + visibleRows[id2].split('|-|')[6] + '&attid=0.' + vis ibleRows[id2].split('|-|')[7] + '&disp=safe&zw';//這句就是在拼下載地址 chrome.runtime.sendMessage({url: url}, function (response) { ...//這里可以針對返回的 response 做些操作,不過我這里不需要 }); } } };
這樣就完成了下載。