2017/8/9

[入門] Chrome Extension 入門 #1

在過去的將近十年間, 我陸續在不同的公司裡寫了好幾個 Chrome Extension (以下簡稱 ChrExt)。很不幸地, 由於這幾個 ChrExt 都是任務型的專案, 寫好之後就放著了, 而且很少需要修改。正因為如此, 每次我又需要寫 ChExt 時, 幾乎都要重頭開始。

每次都重頭開始, 似乎並不是個省事的方式。所以, 不如我來寫個入門文章, 當做 ChrExt 的懶人包。當我下次又要重頭開始時, 學習曲線可以快速縮短。這個入門文章不只供我自己看, 也可以分享給大家

ChrExt 既然稱為 Chrome Extension, 當然是因為它只能在 Chrome 瀏覽器中使用, 不能在任何其它瀏覽器使用; 所以這個限制就是它最大的缺點。這也是為何我只會把寫 ChrExt 當做任務型專案的原因。如果你希望把這種專案當作商業軟體來開發, 並不是不可以, 只是市場會有很大的侷限。

然而, ChrExt 有它的迷人之處。當你把 ChrExt 部署到公司內部客戶的瀏覽器上面之後, 他只需要在工具列上按個圖型按鈕, 就能觸發某個動作:

當然, 你也可以把它寫成不需按任何按鈕, 就在背景裡做好其它動作。你也可以限制只在哪個網頁做哪個動作。這就是 ChrExt 最方便的地方。

一旦你把 ChrExt 學到純熟之後, 你將會發現它簡直是一把網頁上最便利的瑞士刀。基本上, 你可以使用它來操控幾乎所有網頁上的 DOM 元素, 差不多可以把人家的整個前端改寫了。由於它的功能太過強大, 所以後來 Google 給它加上了很大的限制。不過, 即便如此, 只要你把它使用在正常的、正確的、光明的方面, 它還是極為強大而且方便的。

Chrome Extension 是什麼?

基本上, ChrExt 的組成就像是一個 SPA 專案, 由幾個 HTML、CSS、JavaScript 、JSON和圖形等等檔案所組成:

除了 JavaScript 原本就提供的功能外, Chrome 也提供了一組 API 供你使用。不過, 由於這組 API 過於龐大, 我在這篇入門文章裡不會做詳細介紹。

在繼續進行之前, 有幾個基本觀念先說明一下:

  1. 在 ChrExt 中, 如果我們要在 Chrome 的工具列中出現按鈕以幾進行什麼動作, 我們會把執行的單位稱為某個 "Action"。最常使用的 Action 是所謂的 "Browser Action"。我們可以把 "Browser Action" 當做一個元件, 定義在 menifest.json 檔案中, 決定在網址列(Omnibox)右邊的工具列要使用哪一個專案按鈕(見最上方圖片), 以及按下那個按鈕之後要執行哪個檔案:
    此外, 還有一種 "Page Action", 其用法和 "Browser Action" 很類似, 但是略為複雜, 使用時機也不同。但是相對的, 如果我們不需要在 Chrome 的工具列中出現按鈕, 就不需要定義 browser_action 和 page_action。
  2. 當你的 ChrExt 在 Chrome 瀏覽器中被載入時, 在這個 ChrExt 裡面定義的 "Background" 就會自動執行。同樣的, 其動作也是在 menifest.json 檔案裡定義:
    另一種可以自動執行的稱為 "content_scripts"。顧名思義, 這種 scripts 會在目標網頁上執行。有趣的是, "background" 和 "content_scripts" 並不是互斥的; 它們可以同時存在, 也可以只存在一個; 視需要而定。但是不管怎樣, 在這類 scripts 中定義的動作, 不需要按下專案按鈕就會自動執行。至於詳細做法, 將在下面說明。

建立專案目錄

要建立一個 ChrExt, 我們首先必須建立一個專案目錄。所謂的「專案目錄」其實只是一個普通的資料夾, 隨便你要放在什麼地方(例如桌面)。

現在假設我們在桌面上建立一個叫做「SampleChrExt」的子目錄好了。在這個子目錄裡面, 至少必須有以下幾個檔案存在:

  • manifest.json
  • background.js 或 content_scripts.js (或二者)

其中, manifest.json 這個檔名不能隨便改, 其餘檔案(例如 background.js)則可以取作其它名字。不過為了後續維護方便, 除非真的有必要, 否則不建議你把這些檔案改名。

此外, 我們其實可以在這個子目錄中放置任何其它檔案, 視需要而定。

manifest.json 是一個標準的 JSON 檔案, 其內容長得像如下的樣子:

{
  "name": "Page Redder",
  "description": "A Test Extension",
  "version": "2.0",
  "permissions": [
    "activeTab"
  ],
  "background": {
    "scripts": ["background.js"],
    "persistent": false
  },
  "browser_action": {
    "default_title": "A Test Extension"
  },
  "content_scripts": [
    {
      "matches": [
        "https://www.google.com/*"
      ]
      ,"all_frames": true
      ,"js": [
        "jquery-3.2.1.min.js",
        "content_script.js"
      ]
      ,"run_at": "document_idle"
    }
  ],
  "manifest_version": 2
}

我們通常不會無中生有地自己去建立一個新的 manifest.json (但不是不可以); 我們一般都拿既有的 manifest.json 來改。在 Chrome 的 Sample Extensions 網頁裡有一些範例, 其中每一個 Sample 裡一定有一個 manifest.json, 可以拿來參考。

在這個 Sample Extensions 網頁裡, 你可以看到很多不同的 ChrExt 應用方式。基本上, 光是參考這些範例的寫法, 你就能夠學會 ChrExt 的精髓了。不過, 如果你比較懶的話, 請繼續往下讀, 可以再省下你一點時間。

開始動手

我們可以從 Sample Extensions 網頁找一個最簡單的 ChrExt 來參考: Page Redder。它只有兩個檔案: manifest.json 和 background.js。

它的 manifest.json 這個檔案我們上面已經介紹過了。它是 ChrExt 的核心, 是整個擴充功能的主控檔:

{
  "name": "Page Redder",
  "description": "Make the current page red",
  "version": "2.0",
  "permissions": [
    "activeTab"
  ],
  "background": {
    "scripts": ["background.js"],
    "persistent": false
  },
  "browser_action": {
    "default_title": "Make this page red"
  },
  "manifest_version": 2
}

其中, "permissions": [ "activeTab" ] 這段描述指的是這個 ChrExt 只能有存取目前分頁(Active Tab)的能力。在上面範例中, 項目只有一個; 但實際上項目可以不只一個; 例如:

"permissions": [
  "tabs",
  "bookmarks",
  "http://www.blogger.com/",
  "http://*.google.com/",
  "unlimitedStorage"
]

如果你對這個主題有興趣, 可以參考 API 的相關說明頁。可惜這些 API 的網頁都沒有中文版本, 只有英文版。限於篇幅, 在這裡就不一一介紹了。

接著, 我們來看 "background": { "scripts": ["background.js"], "persistent": false } 這一段。

這裡的 "background" 指的是「在背景執行」的意思。標注為 "scripts": ["background.js"], 指的是一旦這個 ChrExt 啟用後, 這個 "background.js" 就會在背景中自動執行。

至於 "persistent": false 是什麼意思呢? Persistent 是常駐的意思。設為 false, 代表這裡的 "background.js" 及其相關資源不會常駐在記憶體裡; 它只會在需要時才載入, 不需要時將被卸載。

"background.js" 的內容很簡單:

chrome.browserAction.onClicked.addListener(function(tab) {
  chrome.tabs.executeScript({
    code: 'document.body.style.backgroundColor="red"'
  });
});

當這個 ChrExt 一啟用的時候, 這個檔案就會執行一次。而上述程式內容是把專案圖示按鈕加上一個 onClicked 的 listener; 一旦這個按鈕被按下, 它會把當前分頁的背景色設定為紅色。

現在, 請在你的桌面中建立一個新的資料夾, 命名為 Page Redder:

接著, 把剛才範例頁中兩個檔案拷貝過去:

當然, 如果你直接把範例程式直接拷貝過去也是可以的。

到這裡, 我們的預備動作已經完成了。不過, 在我們正式可以使用這個 ChrExt 之前, 我們必須開啟 Chrome 的開發人員模式。請從 Chrome 選單裡選擇「擴充功能」:

在畫面右上角, 把「開發人員模式」核選方塊上打勾。

接著, 在畫面上方, 按下「載入未封裝擴充功能」, 然後選擇桌面上的 "Page Redder" 資料夾。

載入後, 確定這個擴充功能已啟用:

如果載入成功, 你應該在網址列右邊看到一個通用型的擴充圖示:

離開 "chrome://extensions/", 開啟任何一個你熟悉的網頁, 然後按下剛才那個按鈕, 看看發生了什麼。如果畫面上沒有任何變化, 請試試其它網頁。

現在我們回頭來說明 "background.js" (請在編輯器裡打開這個檔案)。前面提到, 這個程式會在 ChrExt 啟用時執行一次。在這裡, "background.js" 程式裡只會執行一個指令, 也就是 "chrome.browserAction.onClicked.addListener()" 這個動作; 前面已經介紹過, 它的目的就是為剛才講到的擴充圖示加上一個 onClicked 的 Listener。意思是, 每當使用者按下那個專案按鈕時, 就觸發包在裡面的那個匿名的 function(), 它會執行 document.body.style.backgroundColor="red" 這段指令, 把目前分頁的背景色設為紅色。不過有些網頁可能整個把背景遮住, 使你看不到效果; 並不是指令有誤。

注意事項

前面提到, ChrExt 只能在 Chrome 使用。我想, 如果你這篇文章能夠看到這裡, 你應該不會在乎這個問題才對。不過, 它還有另一個問題。

當你把你的 ChrExt 寫好, 若依正常程序, 你可以把它封裝, 然後發行到「Chrome 線上應用程式商店」上面去。但是, 一旦你公開發行之後, 你寫的 ChrExt 基本上就等於是供大家欣賞了。如果你好奇的話, 你的機器上所有已安裝的擴充套件其實通通都放在 C:\Documents and Settings\username\Local Settings\Application Data\Google\Chrome\User Data\Default\Extensions 子目錄裡面。

所以我一開始不是說我寫的 ChrExt 全部都是任務型的工具嗎? 意思是說我所寫的都是只有公司會用到的工具, 裡面有很多公司機密, 而我一點都不想讓公司以外的人知道; 而且我很確定別人也不大會有興趣知道這些商業邏輯(當然, 商業間諜例外)。

在這種前題之下, 我就更不可能把這類任務型工具「發行」出去了。

但是如果不發行的話, 那麼我只能透過一開始提到的「載入未封裝擴充功能」來安裝並載入我寫的 ChrExt。但是, 這樣做會導致一個有點討人厭的後遺症, 就是每天第一次打開 Chrome 瀏覽器時, 它總會問我是否要將開發者模式關閉。

當你要把寫好的 ChrExt 布署到公司內部客戶時, 你必須把開發人員模式打開, 才能載入和執行未封裝擴充功能。不幸的是, 每次打開 Chrome, 上面這個對話框都會出現一次; 請記得按下 Cancel 或「取消」按鈕, 否則你的 ChrExt 可能會無法執行。

此外, 如果你打算修改你的 ChrExt, 原則上你可以直接修改專案子目錄裡面的任何檔案; 改完後存檔。然後, 回到 Chrome 擴充功能頁, 按「重新載入」即可。

至於開發 ChrExt 的工具, 我個人推薦使用 Sublime Text。你可以一次把最常用的 json、js 和 html 檔案通通載入, 而且, 當你隔天再開啟時, 那些檔案都會繼續開啟著(即使你尚未存檔)。

沒有留言:

張貼留言