承上篇, 即便你只學會簡單的 Chrome Extensioin 撰寫技巧, 你也一定能夠感受到它的巨大潛力。在本文裡, 我將會介紹其它技巧, 請耐心看下去。
這次我要示範 ChrExt 提供的幾個其它功能或技巧。
讀取文字檔案
有時候你可能有需要從你的專案子目錄讀取一些靜態的文字檔案, 這時候你必須使用比較特殊的技巧, 否則會出現錯誤。
第一個重點仍然在 manifest.json 檔案裡。你必須指定 web_accessible_resources 屬性並指定你要讀取的檔案:
"web_accessible_resources": [ "InitialData.csv" ]
再次套用上面的範例, 它看起來應該寫成這樣:
{
"name": "Page Redder",
"description": "A Test Extension",
"version": "2.0",
"permissions": [
"activeTab"
],
"web_accessible_resources": [ "InitialData.csv" ],
... (以下省略)
經此宣告後, 你才能在你的 ChrExt 中存取到 InitialData.csv 這個檔案。
不過, 如果你打算使用 "InitialData.csv" 當作路徑的話, 是絕對取不到資料的, 而且會發生 404 錯誤。你應該透過 chrome.runtime.getURL() 以取得正確的路徑:
function readDataSync(url) { // Load text synchronously
let xhr = new XMLHttpRequest();
xhr.open('GET', chrome.runtime.getURL(url), false);
xhr.send();
return xhr.responseText;
}
以上這個做法適合未封裝的 ChrExt。但如果你打算把你的 ChrExt 上傳到 Chrome 線上應用程式商店的話, 你最好是仔細地測試一遍。如果發生問題的話, 你可以採取另一種變通的做法, 就是把文字檔案以手動方式轉換為 JavaScript 檔案, 然後直接載入, 連讀取的動作都省下來。
假設你把上述的 InitialData.csv 轉換成如下的檔案並名為 customers.js:
const customers = [
["RowNo","Address","Phone"],
["1","AAA","02-12345678"],
["2","BBB","02-23456789"],
["3","CCC","02-34567890"]
];
然後在 manifest.json 中把這個檔案包起來:
"content_scripts": [
{
"matches": [
(省略)
]
,"js": [
"customers.js",
"content_script.js"
]
,"run_at": "document_idle"
}
]
這樣一來, 你的 ChrExt 一載入就有這個 customers 物件陣列可以使用, 這樣就不需要特別去載入文字檔案了。
還有一個更簡單的方法。我寫了一個 jsFiddle, 你可以在 Input 欄位中輸入 CSV 文字:
RowNo,Address,Phone
1,AAA,02-12345678
2,BBB,02-23456789
3,CCC,02-34567890
再按下 Convert 按鈕, 程式就會自動轉換成 JavaScript object:
const root = [
{ "RowNo": "1","Address": "AAA","Phone": "02-12345678" },
{ "RowNo": "2","Address": "BBB","Phone": "02-23456789" },
{ "RowNo": "3","Address": "CCC","Phone": "02-34567890" }
];
直接將它拷貝起來再儲存為 .js 檔案就行了。
如果文字裡使用的分隔字元是 TAB 的話, 可以把那個 TAB 字元拷貝起來, 直接貼到 Separator 欄位裡即可。不要使用 "\t"。如果你的資料裡有空格字元的話, 容易發生錯誤; 建議你把 TAB 字元改成其它字元(例如逗點或 PIPE 符號等等)。
Options
這裡的 Options 指的是一般我們所謂的「選項」。當然, 你的 ChrExt 必須稍為複雜到有必要提供選項給用戶, 這個選項頁才有意義。如果你寫的 ChrExt 只提供固定不變的功能, 那麼你其實並不需要 Options 功能。
在你的 Options 功能就緒之前, 你必須在 manifest.json 中進行宣告:
"options_page": "options.html"
如果你不知道 manifest.json 是什麼, 請先閱讀上一篇介紹文章「[入門] Chrome Extension 入門 #1」。
若延用上一篇的範例, 你的 manifest.json 寫法應該像這樣:
{
"name": "Page Redder",
"description": "A Test Extension",
"version": "2.0",
"permissions": [
"activeTab"
],
"options_page": "options.html",
... (以下省略)
這個 options.html 是一個普通的 HTML 檔案。你可以在這個檔案中再行載入需要的 JavaScript 檔案或者 CSS 檔案, 其做法和一般的 HTML 檔案一樣。
你可以在這個 ChrExt 的右上角圖像上按右鍵再選取「選項」, 這個 options.html 就會開啟。你可以在這一頁中讓使用者調整你的 ChrExt 的各種行為, 視你的 ChrExt 的需要而定。
資料交換
有時候我們在跨 session 或 process 時會有交換資料的需要。例如在最上面介紹的 Options 功能, 如果使用者做了任何選擇, 要如何在其它地方使用?
這時候我們就可以用到 Google 專為 ChrExt 提供的 chrome.storage 功能。
如同你在它的介紹頁中看到的, 它是從 Chrome 第 23 版以後才支援的, 所以你應該在 manifest.json 中把 "minimum_chrome_version" 改成 23, 以免費生預期外的問題:
"minimum_chrome_version": "23"
此外, 你也應該在 "permissions" 區段中加上 "storage" 這個關鍵字, 例如:
"permissions": [
"http://*",
"tts",
"tabs",
"storage"
]
基本上, 你可以把 chrome.storage 當作 HTML5 裡的 Window.localStorage 來使用, 差別是 chrome.storage 只能使用於 ChrExt 和 Chrome Apps。如果使用於 ChrExt, 那麼不同的 ChrExt 所使用的 chrome.storage 也是完全不能互通的 (所以可以使用相同的命名)。
若使用 chrome.storage, 資料是以 JavaScript object 型式儲存的; 而 localStorage 是以字串方式儲存。
請注意, chrome.storage 可以使用在 content script 裡, 但不能使用在 background script 裡。如果你需要在兩者之間傳輸資料, 恐怕必須改用 localStorage 才行。
chrome.storage 提供了兩套用法相同但目的略有不同的存取指令, 分別是 chrome.storage.local 和 chrome.storage.sync。如果你採用 chrome.storage.sync 的話, 你寫進去的變數值, 會同步到使用者以相同 Google 帳號登入的 Chrome 瀏覽器。例如有位使用者在他家裡和公司裡的 Chrome 瀏覽器都使用相同的 Google 帳號登入, 也都安裝了同一個 ChrExt; 假設他晚上在家裡的 ChrExt 中改變了什麼變數值, 那麼隔天他在公司裡的 ChrExt 就會看到相同的變化。不過, 當然, 這個功能只支援正式封裝的 ChrExt; 未封裝的 ChrExt 恐怕是無效的。
以下一律以 chrome.storage.local 為例。因為 chrome.storage.sync 的指令也都一樣, 所以就不另外介紹了。
在 chrome.storage.local 之下, 寫入資料用的是 set 指令, 讀取則是 get 指令。請看以下範例:
chrome.storage.local.set({ count: 1, log: 'my log' });
chrome.storage.local.get({ count: 0, log: '' }, function(items) {
let c = items.count;
let l = items.log;
...
}
在這裡 set 指令比較單純, 你只要丟一個 JavaScript 物件 (例如 {count: 1, log: 'my log'} ), 它就會把儲存在 chrome.storage 中的物件更新了。它和 get 指令一樣都可以指定一個 callback 函式, 只是因為它的作用不大, 所以我把它省略了。它的正式語法是這樣的:
StorageArea.set(object items, function callback) <- 第二個參數可省略
至於 get 指令則比較複雜一點。它的正式語法是這樣的:
StorageArea.get(string or array of string or object keys, function callback) <- 第一個參數可省略
請同時對照我在上面範例中的寫法。雖然 get 指令的第一個參數可省略, 但是我建議你, 除非你已經對這個功能很熟悉了, 否則請不要省略它。因為你可以在第一個 get 指令中設定物件中屬性 (例如範例中的 count 與 log) 的初始值。如果你沒有指定各屬性的初始值, 後面可能會遇上各種奇怪的問題。
依照範例中的寫法, 物件的初始結構可以在 get 指令中指定。關於更進一步的示範, 將在之後的系列文章中提到。
至於你在何時下 set、何時下 get 指令, 則是跟你的程式的生命週期有關。如果你使用 chrome.storage 來儲存環境變數的話, 那麼在 content scripts 中, 我們可以一開始就使用 get 指令, 把先前儲存的變數值 restore 回來。使用者可以在 Options 頁中透過 set 指令予以修改。
參考:
- [入門] Chrome Extension 入門 #1
- [入門] Chrome Extension 入門 #2
- [入門] Chrome Extension 入門 #3
- [入門] Chrome Extension 入門 #4
- [入門] Chrome Extension 入門 #5
- [入門] Chrome Extension 入門 #6
- Manifest V3 migration checklist