Chrome插件開發(fā)快速入門

前言

周末學(xué)習(xí)了下Chrome插件的開發(fā)执庐,總體來說入門還是比較容易的,動手配合一些demo就能了解基本的開發(fā)過程导梆。這篇是一個學(xué)習(xí)筆記和總結(jié)轨淌,希望對大家也有所幫助。

什么是Chrome插件

Chrome插件其實和一個普通web應(yīng)用一樣都是由html+css+js打包組成的看尼,插件可以使用Chrome提供的瀏覽器API递鹉,增強(qiáng)瀏擴(kuò)展覽器的功能。
Chrome插件通常是.crx后綴的文件藏斩,通過谷歌網(wǎng)上應(yīng)用商店下載或者在開發(fā)者模式中可以直接拖入到瀏覽器進(jìn)行安裝

用戶界面網(wǎng)頁(popup)

點擊插件圖標(biāo)出來的彈窗其實就是一個html頁面躏结,彈窗要顯示的內(nèi)容,和工具欄小圖標(biāo)在manifest.json文件中配置狰域。

彈窗在每次點擊插件開始運(yùn)行媳拴,彈窗關(guān)閉后結(jié)束,可以與background腳本交互兆览。

基礎(chǔ)用法

1.創(chuàng)建manifest.json

任何插件都必須要有這個文件屈溉,用來描述插件的元數(shù)據(jù),插件的配置信息抬探。

一個常用配置項如下:

{
// 必須
"manifest_version": 2,
"name": "插件名稱a",
"version": "1.1.2",

// 推薦
"default_locale": "en",
"description": "插件的描述",
"icons": {
 "16": "img/icon.png",   // 擴(kuò)展程序頁面上的圖標(biāo)
 "32": "img/icon.png",   // Windows計算機(jī)通常需要此大小子巾。提供此選項可防止尺寸失真縮小48x48選項。
 "48": "img/icon.png",   // 顯示在擴(kuò)展程序管理頁面上
 "128": "img/icon.png"   // 在安裝和Chrome Webstore中顯示
},

// 可選
"background": {
 "page": "background/background.html",
 "scripts": ["background.js"],
 // 推薦
 "persistent": false
},
"browser_action": {
 "default_icon": "img/icon.png", 
 // 特定于工具欄的圖標(biāo)小压,至少建議使用16x16和32x32尺寸线梗,應(yīng)為方形,
 // 不然會變形
 "default_title": "懸浮在工具欄插件圖標(biāo)上時的tooltip內(nèi)容",
 "default_popup": "hello.html"   // 不允許內(nèi)聯(lián)JavaScript怠益。
},
"content_scripts": [ {
 "js": [ "inject.js" ],
 "matches": [ "http://*/*", "https://*/*" ],
 "run_at": "document_start"
 } ],
"permissions": [
 "contextMenus",
 "tabs",
 "http://*/*",
 "https://*/*"
],
"web_accessible_resources": [ "dist/*", "dist/**/*" ]
}

上面的配置項看起來內(nèi)容較多仪搔,主要有以下幾項:

  • 定義當(dāng)前插件的名字,描述版本號等信息溉痢。
  • "manifest_version":現(xiàn)在應(yīng)該總是2僻造。
  • background
  • content_scripts
  • permissions:在background或是popup的js里使用一些chrome api,需要授權(quán)才能使用孩饼,例如要使用chrome.tabs.xxx的api髓削,就要在permissions引入“tabs”

background、content_scripts的使用后面會詳細(xì)介紹

我們先使用如下基本信息

manifest.json

 {
    "name": "My QrCode",
    "description" : "My QrCode Extension",
    "version": "1.0",
    "manifest_version": 2,
   
    "browser_action": {
        "default_popup": "./popup.html", 
        "default_icon": "./logo.png"
    },
  }

2.新建popup.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
            <h2>hello world!</h2>
</body>
</html>

并找個png的圖作為logo

3.打包擴(kuò)展程序

現(xiàn)在我們就可以看下效果了镀娶。

打包擴(kuò)展程序:

打開chrome://extensions/立膛,打開開發(fā)者模式,點擊打包擴(kuò)展程序,選擇我們的開發(fā)目錄

image-20200607172122923.png

注意:第一次打包的時候宝泵,會生成一個pem個人密鑰文件好啰,以后再次打包的時候就需要密鑰文件了。

image-20200607163732018.png

4.加載已解壓的擴(kuò)展程序

image-20200607165136815.png

選擇剛剛打包出來的文件即可

現(xiàn)在我們就可以看到效果了:

image-20200607165437236.png

實現(xiàn)一個二維碼插件

在上面的基礎(chǔ)上儿奶,我們實現(xiàn)一個簡單的當(dāng)前頁面生成二維碼插件:

popup.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script type="text/javascript" src="jquery.min.js"></script>
    <script type="text/javascript" src="qrcode.min.js"></script>
</head>
<body>
    <div id="qrcode"></div>
    <script type="text/javascript" src="popup.js"></script>
</body>
</html>

這里我們二維碼生成用了這個開源項目:
https://github.com/davidshimjs/qrcodejs

把項目中的jquery.min.js框往、qrcode.min.js拷過來

注意:

由于popup不允許內(nèi)聯(lián)JavaScript,因此需要script標(biāo)簽引入外部腳本

popup.js

let url;

chrome.tabs.query({active: true, currentWindow: true}, function (tabs) {
    url = tabs[0].url
    var qrcode = new QRCode(document.getElementById("qrcode"), {
        text: url,
        width: 256,
        height: 256,
        colorDark : "#000000",
        colorLight : "#ffffff",
        correctLevel : QRCode.CorrectLevel.M
    });
});

chrome.tabs.query()這部分用來獲取當(dāng)前頁面url

在manifest.json中加入允許:

{
    "name": "My QrCode",
        ...
    "permissions": ["activeTab"]
}

這樣闯捎,我們一個簡單的生成二維碼插件就完成了

background

問題:
我們想在彈窗中寫一個計數(shù)器椰弊,每點擊一次加1。但發(fā)現(xiàn)Popup 彈窗無法記錄數(shù)據(jù)

實現(xiàn)代碼如下:

popup.html

    <input type="text" id="input" value="0">
    <button id="btn">+1</button>

popup.js

var count = 0;
$(function(){
    $('#input').val(count);
    $('#btn').click(function(){
        count = count+1;
        $('#input').val(count);
    });
})

當(dāng)我們點擊 “+1” 瓤鼻,輸入框中的數(shù)字就會增加秉版,然后關(guān)閉彈窗,再點開茬祷,發(fā)現(xiàn)數(shù)字又變成了0清焕,這說明當(dāng)我們關(guān)閉彈窗時,popup.html就被銷毀了祭犯,我們在popup.js 中用count 存儲的全局變量秸妥,也被銷毀了。

解法:
由于popup在彈窗關(guān)閉后就銷毀了盹憎。如果想要在popup中記錄上次關(guān)閉后的結(jié)果的話筛峭,需要引入background

概念

background可以理解為插件運(yùn)行在瀏覽器中的一個后臺網(wǎng)站/腳本,注意它是與當(dāng)前瀏覽頁面無關(guān)的陪每。
實際上這部分內(nèi)容的配置情況也會寫在manifest里影晓,對應(yīng)的是background配置項。單獨拿出來講檩禾,是彰顯它的分量很重挂签,也是一個插件常用的配置。從其中幾個配置項項去了解一下什么是background script

manifest.json

   "background": {
    "page": "background/background.html",
    "scripts": ["background.js"],
    // 推薦
    "persistent": false
   },

page

可以理解為這個后臺網(wǎng)站的主頁盼产,在這個主頁中饵婆,有引用的腳本,其中一般都會有一個專門來管理插件各種交互以及監(jiān)聽瀏覽器行為的腳本戏售,一般都起名為background.js侨核。這個主頁,不一定要求有灌灾。

scripts

這里的腳本其實跟寫在page里html引入的腳本目的一樣搓译,個人的理解是,page的html在沒有的情況下锋喜,那么腳本就需要通過這個屬性引入了些己;
如果在存在page的情況下豌鸡,一般在這里引入的腳本是專門為插件服務(wù)的腳本,而那些第三方腳本如jquery還是在page里引用比較好段标,或許這是一個眾人的“潛規(guī)則”吧

persistent

所謂的后臺腳本涯冠,在chrome擴(kuò)展中又分為兩類,分別運(yùn)行于后臺頁面(background page)和事件頁面(event page)中逼庞。兩者區(qū)別在于蛇更,

前者(后臺頁面)持續(xù)運(yùn)行,生存周期和瀏覽器相同赛糟,即從打開瀏覽器到關(guān)閉瀏覽器期間械荷,后臺腳本一直在運(yùn)行,一直占據(jù)著內(nèi)存等系統(tǒng)資源虑灰,persistent設(shè)為true;

后者(事件頁面)只在需要活動時活動痹兜,在完全不活動的狀態(tài)持續(xù)幾秒后穆咐,chrome將會終止其運(yùn)行,從而釋放其占據(jù)的系統(tǒng)資源字旭,而在再次有事件需要后臺腳本來處理時对湃,重新載入它,persistent設(shè)為false遗淳。

保持后臺腳本持久活動的唯一場合是擴(kuò)展使用chrome.webRequest API來阻止或修改網(wǎng)絡(luò)請求拍柒。webRequest API與非持久性后臺頁面不兼容。

popup與background的通信

popup與background的交流屈暗,常見于popup要獲取background里的某些“東西”

通信方式:

popup:

var bg = chrome.extension.getBackgroundPage();
bg.someMethod();    //someMethod()是background中的一個方法

實現(xiàn)一個計數(shù)器

對于計數(shù)器的例子來說:

manifest.json

    "background" : {
        "scripts": ["background.js"],
        "persistent": false
    },

background.js

var count = 0;

popup.js

var bg = chrome.extension.getBackgroundPage(); 
$(function(){
    $('#input').val(bg.count);
    $('#btn').click(function(){
        bg.count = bg.count+1;
        $('#input').val(bg.count);
    });
})

這樣拆讯,按鈕的點擊次數(shù)就可以記錄下來了

content script

概念

向符合條件的網(wǎng)頁插入該js腳本,對網(wǎng)頁做一些處理养叛。它具有獨立而富有包容性种呐。

  • 獨立,指它的工作空間弃甥,命名空間爽室,域等是獨立的,不會說跟插入到的頁面的某些函數(shù)和變量發(fā)生沖突淆攻;

  • 包容性阔墩,指插件把自己的一些腳本(content script)插入到符合條件的頁面里,作為頁面的腳本瓶珊,因此與插入的頁面共享dom的啸箫,即用dom操作是針對插入的網(wǎng)頁的,在這些腳本里使用的window對象跟插入頁面的window是一樣的艰毒。主要用在消息傳遞上(使用postMessage和onmessage)

配置也會寫在manifest里筐高,對應(yīng)的是content_scripts配置項。

manifest.json

   "content_scripts": [ {
    "js": [ "inject.js" ],
    "matches": [ "http://*/*", "https://*/*" ],
    "run_at": "document_start"
    } ],

js

要插入到頁面里的腳本。例子很常見柑土,例如在一個別人的網(wǎng)頁上蜀肘,你要打開你做的擴(kuò)展,對別人的網(wǎng)頁做一些處理或者獲取一些數(shù)據(jù)等稽屏,那怎么跟別人的頁面建立起聯(lián)系呢扮宠?就是通過把js里的這些腳本嵌入都別人的網(wǎng)頁里。

matches

必需狐榔。匹配規(guī)則組成的數(shù)組坛增,用來匹配頁面url的,符合條件的頁面將會插入js的腳本薄腻。當(dāng)然收捣,有可以匹配的自然會有不匹配的——exclude_matches。匹配規(guī)則:

developer.chrome.com/extensions/…

run_at

js配置項里的腳本何時插入到頁面里呢庵楷,這個配置項來控制插入時機(jī)罢艾。有三個選擇項:

  • document_start
  • document_end
  • document_idle(默認(rèn))

document_ start

style樣式加載好,dom渲染完成和腳本執(zhí)行前

document_end

dom渲染完成后尽纽,即DOMContentLoaded后馬上執(zhí)行

document_idle

在DOMContentLoaded 和 window load之間咐蚯,具體是什么時刻,要視頁面的復(fù)雜程度和加載時間弄贿,并針對頁面加載速度進(jìn)行了優(yōu)化春锋。

popup與content script通信

popup與content script通信 和 background與contentscript通信用法是一致的

發(fā)送消息:

chrome.runtime.sendMessege(
    message,
    function(response) {…}
)

第一個參數(shù)message為發(fā)送的消息(基礎(chǔ)數(shù)據(jù)類型),回調(diào)函數(shù)里的第一個參數(shù)為background接收消息后返回的消息(如有)

接受消息:

chrome.runtime.onMessege.addListener(
    function(request, sender, sendResponse) {…}
)

監(jiān)聽發(fā)來的消息差凹,request表示發(fā)來的消息期奔,sendResponse是一個函數(shù),用于對發(fā)來的消息進(jìn)行回應(yīng)直奋,如 sendResponse('我已收到你的消息:'+JSON.stringify(request));

這里需要注意的是能庆,默認(rèn)情況下sendResponse函數(shù)的執(zhí)行是同步的,如果在這個監(jiān)聽消息的處理函數(shù)的同步執(zhí)行流程里沒有發(fā)現(xiàn)sendResponse脚线,則默認(rèn)返回undefined搁胆,假設(shè)我們是要經(jīng)過一個異步處理之后才調(diào)用sendResponse,已經(jīng)為時已晚了邮绿。因此渠旁,我們可能需要異步執(zhí)行sendResponse,這時我們在這個監(jiān)聽函數(shù)里的添加return true就能實現(xiàn)了船逮。

實現(xiàn)一個搜索請求

實現(xiàn)一個向百度搜索框發(fā)送關(guān)鍵詞顾腊,并提交搜索請求。

這個功能其實沒什么用挖胃,主要是為了了解content_scripts和popup通信的流程

manifest.json

    "content_scripts": [
        {
            "matches": ["https://www.baidu.com/*"],
            "js": ["jquery.min.js","search-in-baidu.js"]
        }
    ],

上述配置表示當(dāng)頁面 url 地址匹配到 “https://www.baidu.com/*” 模式時才向頁面中注入jquery.min.js, search-in-baidu.js 兩個js 文件

一個插件里content-script有多個(一個頁面一個)杂靶,那么怎么向特定的content-script發(fā)送消息梆惯?

首先我們需要知道要向哪個content scripts發(fā)送消息,一般一個頁面一份content scripts吗垮,而一個頁面對應(yīng)一個瀏覽器tab垛吗,每個tab都有自己的tabId,因此首先要獲取要發(fā)送消息的tab對應(yīng)的tabId烁登。

/**
 * 獲取當(dāng)前選項卡id
 * @param callback - 獲取到id后要執(zhí)行的回調(diào)函數(shù)
 */
function getCurrentTabId(callback) {
    chrome.tabs.query({active: true, currentWindow: true}, function (tabs) {
        if (callback) {
            callback(tabs.length ? tabs[0].id: null);
        }
    });
}

當(dāng)知道了tabId后怯屉,就使用該api進(jìn)行發(fā)送消息

chrome.tabs.sendMessage(tabId, message, function(response) {...});

其中message為發(fā)送的消息,回調(diào)函數(shù)的response為content scripts接收到消息后的回傳消息

在我們這個需求中:

popup.js

$(function(){
    var state = $('#state');
    $('#send').click(function () {//給對象綁定事件
        chrome.tabs.query({active:true, currentWindow:true}, function (tab) {//獲取當(dāng)前tab
            //向tab發(fā)送請求
            chrome.tabs.sendMessage(tab[0].id, { 
                action: "send",
                keyword: $('#keyword').val()
            }, function (response) {
                console.log(response);
                state.html(response.state)
            });
        });
    });
    $('#submit').click(function () {
        chrome.tabs.query({active:true, currentWindow:true}, function (tab) {
            chrome.tabs.sendMessage(tab[0].id, {  
               action: "submit"   
            }, function (response) {
                state.html(response.state)
            });
        });
    })
})

search-in-baidu.js

var kw = $('#kw');
var form = $('#form');
chrome.runtime.onMessage.addListener(
    function (request, sender, sendResponse) {
        if (request.action == "send") {
            kw.val(request.keyword)
            sendResponse({state:'關(guān)鍵詞填寫成功饵沧!'});
        }
        if (request.action == "submit") {
            form.submit();
            sendResponse({state:'提交成功锨络!'});
        }
    }
);

實現(xiàn)效果:

www.baidu.com中,使用插件狼牺,輸入想要搜索的關(guān)鍵詞羡儿,點擊發(fā)送->點擊提交,就查詢出來了是钥。

image-20200607230601203.png

image-20200607230642078.png
image-20200607230655872.png

開發(fā)調(diào)試

image-20200607231951536.png
  • 開發(fā)過程中失受,如果有報錯,直接點擊錯誤就能看到具體報錯

  • 修改代碼后插件沒有自動更新的話也可以手動點擊刷新按鈕

  • popup調(diào)試:

    右鍵插件圖標(biāo)咏瑟,審查彈出內(nèi)容

image-20200607232711406.png
  • backgroud script調(diào)試:

    點擊背景頁,就可以看到backgroud script進(jìn)行調(diào)試了痪署,而且码泞,還能在控制臺調(diào)用chrome api。請求也可以在這里看到狼犯。

  • content script調(diào)試:
    平常我們打開F12選擇到source選項的時候余寥,一般都會顯示在"page"下,選擇content script悯森,里邊的就是各個擴(kuò)展的內(nèi)容腳本了宋舷。

image-20200607232212373.png

總結(jié)

chrome擴(kuò)展開發(fā)入門還是比較容易的,主要有一下幾個基本概念

  • manifest.json
  • popup
  • background
  • content script

了解后在實際開發(fā)中查閱相關(guān)資料即可

參考

更詳細(xì)的popup瓢姻、background祝蝠、content script的通信方法可以看這篇:

一篇文章教你順利入門和開發(fā)chrome擴(kuò)展程序(插件)

其他:

谷歌(Chrome)瀏覽器插件開發(fā)教程

Chrome 擴(kuò)展開發(fā)教程(2) ——Background的用法

Chrome 擴(kuò)展開發(fā)教程(3)——content_scripts用法

官網(wǎng)教程:https://developer.chrome.com/extensions/getstarted

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市幻碱,隨后出現(xiàn)的幾起案子绎狭,更是在濱河造成了極大的恐慌,老刑警劉巖褥傍,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件儡嘶,死亡現(xiàn)場離奇詭異,居然都是意外死亡恍风,警方通過查閱死者的電腦和手機(jī)蹦狂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進(jìn)店門誓篱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人凯楔,你說我怎么就攤上這事窜骄。” “怎么了啼辣?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵啊研,是天一觀的道長。 經(jīng)常有香客問我鸥拧,道長党远,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任富弦,我火速辦了婚禮沟娱,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘腕柜。我一直安慰自己济似,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布盏缤。 她就那樣靜靜地躺著砰蠢,像睡著了一般。 火紅的嫁衣襯著肌膚如雪唉铜。 梳的紋絲不亂的頭發(fā)上台舱,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天,我揣著相機(jī)與錄音潭流,去河邊找鬼竞惋。 笑死,一個胖子當(dāng)著我的面吹牛灰嫉,可吹牛的內(nèi)容都是我干的拆宛。 我是一名探鬼主播,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼讼撒,長吁一口氣:“原來是場噩夢啊……” “哼浑厚!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起根盒,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤瞻颂,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后郑象,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體贡这,經(jīng)...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年厂榛,在試婚紗的時候發(fā)現(xiàn)自己被綠了盖矫。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片丽惭。...
    茶點故事閱讀 39,731評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖辈双,靈堂內(nèi)的尸體忽然破棺而出责掏,到底是詐尸還是另有隱情,我是刑警寧澤湃望,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布换衬,位于F島的核電站,受9級特大地震影響证芭,放射性物質(zhì)發(fā)生泄漏瞳浦。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一废士、第九天 我趴在偏房一處隱蔽的房頂上張望叫潦。 院中可真熱鬧,春花似錦官硝、人聲如沸矗蕊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽傻咖。三九已至,卻和暖如春岖研,著一層夾襖步出監(jiān)牢的瞬間没龙,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工缎玫, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人解滓。 一個月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓赃磨,卻偏偏與公主長得像,于是被迫代替她去往敵國和親洼裤。 傳聞我的和親對象是個殘疾皇子邻辉,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,629評論 2 354