寫chrome外掛前必須要知道的

Eric_zhang發表於2019-03-09

介紹

瀏覽器外掛本質上就是利用前端的html\css\javascript等技術,借用瀏覽器對外提供的API,實現各種不同的功能

外掛組成

首先先看一下我們要開發的外掛的各個檔案組成

manifest.json :我們在專案的根檔案必須要有一個命名為manifest.json的檔案,它是整個外掛的功能入口,用來告訴瀏覽器外掛的一些基本資訊,及需要載入和執行的資原始檔等,裡面包含一些必填項:

  • name 外掛的名字
  • version 外掛版本
  • manifest_version manifest的版本
  • description 外掛的描述(選填)
{
    "name": "hello extension",
    "description": "Base Level Extension",
    "version": "1.0",
    "manifest_version": 2,
}
複製程式碼
  • icons 在擴充套件程式管理介面,呈現的圖示,可以設定不同的尺寸的選項,用來適配不同的場景
{
    "icons": {
        "16": "images/extension_icon16.png",
        "32": "images/extension_icon32.png",
        "48": "images/extension_icon48.png",
        "128": "images/extension_icon128.png"
      },
}
複製程式碼
  • background 在擴充套件程式被開啟時,會首先觸發執行background 選項中指定的js資源,並且一直存在於擴充套件程式的整個生命週期,直到擴充套件程式被關閉或者刪除,background一直被用來做任務和狀態的控制管理工作
// scripts 子選項指定了需要執行的js檔案的路徑及檔名
{
    "background": {
        "scripts":["background.js"],
        "persistent": false
    }
}
複製程式碼

background.js 通常最外層使用監聽事件chrome.runtime.onInstalled.addListener(()=>{})初始化外掛,監聽外掛安裝成功後,會觸發對應的邏輯開始工作

  • permissions 屬性值為一個陣列,申請chrome API的許可權,只有在這個選項中申請了才能使用(比如通過 XMLHttpRequest 跨域請求資料、訪問瀏覽器選項卡(tabs)、獲取當前活動選項卡(activeTab)、瀏覽器通知(notifications)、設定外掛啟用規則(declarativeContent)、類似localStorage的儲存(storage)等)
{
    "permissions": [
        "http://xxx.com/api"
        "tabs",
        "activeTab",
        "notifications",
        "declarativeContent",
        "storage"
    ],
}
複製程式碼

與使用者實現UI互動的功能主要有兩個:browser_action、page_action 兩者選擇其中一個

  • browser_action 提供的功能面向所有網站,外掛圖示一直是啟用的狀態
    {
        "browser_action": {
          "default_popup": "popup.html"        // 指定click外掛圖示時,展示的頁面
          "default_icon": "images/icon.png", // 指定瀏覽器工具欄展示的外掛的圖示 
          "default_title": "display tooltip" // 當滑鼠懸浮在外掛圖片上方時,展示的文字,叫tooltip;如果沒有這個配置選項,則懸浮時顯示menifest.json中的name選項值
      }
    }
    複製程式碼
  • page_action 只針對對應目標網站提供的功能,外掛會在background頁設定啟用外掛的規則,只有滿足條件的網頁的外掛才是啟用的,其他網站外掛是不可用的,外掛的icon是灰的
    寫chrome外掛前必須要知道的
    background script裡,chrome.runtime.onInstalled.addListener(()=>{})初始化中使用chrome.declarativeContent定義外掛被啟用的規則
    {
        "page_action":{
            "default_popup": "popup.html", // 指定click外掛圖示時,展示的頁面
            "default_icon": "hello_extension.png" // 指定瀏覽器工具欄展示的外掛的圖示 
            "default_title": "display tooltip"
    },
    }
    複製程式碼
        chrome.runtime.onInstalled.addListener(function() {
        // Replace all rules ...
        chrome.declarativeContent.onPageChanged.removeRules(undefined, function() {
        // With a new rule ...
        chrome.declarativeContent.onPageChanged.addRules([
            {
            // That fires when a page's URL contains a 'g' ...
            conditions: [
                new chrome.declarativeContent.PageStateMatcher({
                pageUrl: { urlContains: 'g' }, //url的內容中包含字母g的,外掛才會被啟用
                })
            ],
            // And shows the extension's page action.
            actions: [ new chrome.declarativeContent.ShowPageAction() ]
            }
        ]);
        });
    });
    複製程式碼

popup.html

popup.html 是使用browser_action或者page_action 指定的default_popup選項,表示點選外掛圖示會觸發的html資源,其與普通的html頁面的區別就是不能使用內聯script,其他都一樣 it can contain links to stylesheets and script tags, but does not allow inline JavaScript.

Content Script 對當前web頁面注入擴充套件程式程式碼,也就是在當前web頁面的上下文環境中,可以訪問頁面的DOM資訊並且修改DOM,具有操作Chrome API的許可權,並且可以把資訊傳遞給父級外掛

  1. content script工作在一個獨立的空間,它使得不會訪問和呼叫web page或者其他content script中定義的變數和函式,兩者唯一共享的是DOM
  2. Content Script與Page action 或Browser action通訊的方式是通過傳遞Message的方式,正確的機制應該是:
    • Page action 或Browser action 中的js使用chrome.tabs.sendMessage()釋出一個請求資訊,傳遞出去當前的tab頁id及其他資訊
    • 注意:從Page action 或Browser action釋出訊息到Content Script必須用chrome.tabs.sendMessage(),從Content Script釋出簡單的資訊到Page action 或Browser action 可以使用chrome.runtime.sendMessage()
     chrome.tabs.sendMessage(
         tabs[0].id, //當前啟用的tab頁id
         {greeting: "hello"}, //需要傳遞的資訊
         function(response) { //用來接收反饋的回撥函式
         console.log(response.farewell);
     });
    複製程式碼
    • Content Script完成初始化,設定chrome.runtime.onMessage().addListener()監聽事件,等待另一側請求資訊的釋出
    chrome.runtime.onMessage.addListener(
     function(request, sender, sendResponse) {
         console.log(sender.tab ?
                     "from a content script:" + sender.tab.url :
                     "from the extension");
         if (request.greeting == "hello")
         sendResponse({farewell: "goodbye"});
     });
    複製程式碼
    • Content Script將資訊通過Message釋出訂閱的方式,釋出出去
    • Page action 或Browser action 中的jschrome.tabs.sendMessage()最後一個引數是一個回撥函式,用來接收另一側反饋回來的資料,最後同步到UI上
  chrome.tabs.sendMessage()
複製程式碼
  1. Content script有兩種注入方式:程式設計式動態注入、宣告式注入

  2. 程式設計式動態注入:獲取activeTab許可權後,使用chrome.tabs.executeScript()來注入js程式碼片段,使用chrome.tabs.insertCSS()注入css程式碼片段

     //manifest.json
     {
       "name": "My extension",
       ...
       "permissions": [
         "activeTab"
       ],
     }
    複製程式碼

在Page action 或Browser action 中的js中實行程式設計式動態注入content script,其訪問和更改的就是web page上的DOM屬性

```js
chrome.runtime.onMessage.addListener(
  function(message, callback) {
    if (message == “changeColor”){
      chrome.tabs.executeScript({
        code: 'document.body.style.backgroundColor="orange"'
      });
    }
 });
 // 你也可以注入一個檔案
 chrome.runtime.onMessage.addListener(
  function(message, callback) {
    if (message == “runContentScript”){
      chrome.tabs.executeScript({
        file: 'contentScript.js'
      });
    }
 });
複製程式碼
  1. 宣告式注入,在manifest.json中使用content_scripts來定義
  {
      "name": "My extension",
      ...
      "content_scripts": [
      {
          "matches": ["http://*.nytimes.com/*"],
          "css": ["myStyles.css"],
          "js": ["contentScript.js"]
      }
      ],
      ...
  }
複製程式碼
  • matches:必填 陣列,用來匹配注入Content Script的頁面地址。可以使用萬用字元來指代:'*'指代任意長度字元;'?'指代單個字元
  • css:選填 陣列,在頁面的任何DOM建立和展示之前,按照陣列中定義的順序,依次注入
  • js:選填 陣列,按照陣列中定義的順序,依次注入
  • run_at:Content Script注入的時機
    • 如果是document_start, 檔案將在所有CSS載入完畢,但是沒有建立DOM並且沒有執行任何指令碼的時候注入
    • 如果是document_end,則檔案將在建立完DOM之後,但還沒有載入類似於圖片或frame等的子資源前立刻注入
    • 如果是document_idle,瀏覽器會在document_end和發出window.onload事件之間的某個時機注入。具體的時機取決與文件載入的複雜度,為加快頁面載入而優化
{
    "content_scripts" : [{
      "matches" : ["*://*/*"],
      "js" : ["content.js", "jquery.js"],
      "css" : ["style.css"],
      "run_at" : "document_end"
  }],
}
複製程式碼
  • options_page 設定外掛的選項頁面,配置了此選項後,在外掛上滑鼠右鍵時,會有一個‘選項’按鈕,點選後會進入options_page對應的頁面

    {
        "options_page":"options.html"
    }
    複製程式碼

    寫chrome外掛前必須要知道的

  • chrome_url_overrides 設定可替換的chrome預設頁面

    • newtab:新建標籤時開啟的頁面
    • bookmarks:書籤頁面
    • history:歷史記錄
    {
        "chrome_url_overrides":{
          "newtab": "newTab.html"
      }
    }
    複製程式碼

    寫chrome外掛前必須要知道的

常用到的Chrome API

chrome.tabs

  1. chrome.tabs.create(object createProperties, function callback) 建立新的標籤。注: 無需請求manifest的標籤許可權,此方法也可以被使用。 Parameters createProperties ( object )

    • windowId ( optional integer ) 建立新標籤的目標視窗。預設是當前視窗 。
    • index ( optional integer ) 標籤在視窗中的位置。 值在零至標籤數量之間。
    • url ( optional string ) 標籤導航的初始頁面。完整的URL 必須包含一個字首 (如 'www.google.com', 不能寫為 'www.google.com')。 相對 URL則與擴充套件所在的頁面相對, 預設值為新標籤頁面。
    • selected ( optional boolean ) 標籤是否成為選中標籤。預設為true。
    • pinned ( optional boolean ) 標籤是否被固定。預設值為false。
    • callback ( optional function )

    Callback function 回撥 引數 應該如下定義:

    function(Tab tab) {...}; tab ( Tab ) 所建立的標籤的細節,包含新標籤的ID。

  2. chrome.tabs.executeScript(integer tabId, object details, function callback) 向頁面注入JavaScript 指令碼執行 Parameters

    1. tabId ( optional integer ) 執行指令碼的標籤ID;預設為當前視窗所選中的標籤。
    2. details ( object ) 要執行的指令碼內容,可選code或者file,但不能同時選兩者。
      • code ( optional string ) 要執行的指令碼程式碼。
      • file ( optional string ) 要執行的指令碼檔案。
      • allFrames ( optional boolean ) true的時候,給所有frame執行指令碼。預設為false,只給頂級frame執行指令碼。
    3. callback ( optional function ) 所有指令碼執行後會被呼叫的回撥。
  3. chrome.tabs.query(object queryInfo, function callback) 獲取經過特定過濾條件篩選的標籤頁資訊,如果沒有特定過濾條件則是獲取所有標籤頁資訊

    1. queryInfo 為特定的屬性,用來對標籤頁進行過濾。 常用的屬性有:(所有屬性均為boolean值)

      • active 標籤在他們的視窗中是否是啟用狀態
      • currentWindow 標籤是否在當前視窗
          chrome.tabs.query({
              active:true,
              currentWindow:true
          },function(tabs){
              //...
          })
      複製程式碼
    2. callback 是獲取到特定標籤頁資訊後的回撥函式,引數為標籤的資訊

chrome.contextMenus 當前頁面,選中內容後右鍵展示的內容

寫chrome外掛前必須要知道的

  1. 在manifest.json中設定許可權{"permissions": ["contextMenus", "storage"]}
  2. 放在background.js中,初始化之後的回撥函式中,使用chrome.contextMenus.create({})建立內容目錄
    chrome.runtime.onInstalled.addListener(function() {
     //...
         chrome.contextMenus.create({
             "id": "sampleContextMenu",
             "title": "Sample Context Menu",
             "contexts": ["selection"]
         });
     });
    複製程式碼

外掛安裝

  1. 進入擴充套件程式管理介面 chrome://extensions
  2. 開啟'開發者模式'
    寫chrome外掛前必須要知道的
  3. 可以選擇'載入已解壓的擴充套件程式',將本地開發目錄上傳上去即可
  4. 可以使用'打包擴充套件程式',生成.ctx字尾的擴充套件程式,就可以釋出到google應用市場了

外掛除錯

注意:

  • 每次重新整理頁面,都會執行browser_action或page_action 指定的popup.html資源
  • background指定的js資源只在外掛安裝時執行,之後就一直存在程式中,不會重複執行

除錯步驟

  1. 在瀏覽器工具欄右鍵外掛圖示
    寫chrome外掛前必須要知道的
  2. 點選'審查彈出內容',進入控制檯
  3. 切換到Source欄,即可看到browser_action或page_action 指定的資源,設定斷點
  4. 切換到Console欄,輸入location.reload(),發現原有的頁面重新進行了載入,同時browser_action或page_action 指定的資源也重新執行了一遍,並在設定的斷點處停下來,等待debug

參考連結

chrome擴充套件程式
如何從零寫一個Chrome擴充套件

相關文章