ChromeExtension入門淺談



0、寫在前面的話

朋友上班時每天好幾個時段都有個客流信息需要匯報到微信里,都是照着網頁上的數據手動填寫,着實麻煩。所以給寫了個簡單的函數每次到控制台里去運行,但是體驗也並不好,今天就花了一整天的時間鼓搗chrome的擴展程序,並順利完成,可以說很棒了。

尤其感謝以下參考鏈接:

另外官方文檔如下,英文的還是有點吃力(手動微笑):

然后,以下部分僅作重點記錄,以備不時之需。又會一個小技能了,以后可以實現自己的小需求,太棒了!

1、chrome擴展的基本構成

這是我自己今天折騰寫完的擴展,如下圖:
 
可以看出,比較典型的擴展,實際上就是一個小的頁面而已,實際上也正是如此:
  • 這個彈出式的頁面我們稱之為popup,實際上就是個html,你可以在里面引用JS和CSS,和普通網頁開發一樣
  • popup就是個單獨頁面,這意味着不能共享標簽頁面的DOM,這意味着你不能使用popup中的JS來操作標簽頁面中的內容
  • 為了能實現JS跨頁面執行,chrome提供了好幾種方式,如contentScriptbackgroundinjectedScript
  • 為了能夠傳輸一些信息(如JS的執行結果),chrome提供了相應的api來進行跨頁面通信
  • 最后當然還有個核心文件叫做manifest.json,用來配置擴展的信息
  • 還有很多...

大概的目錄結構也就如下了:
 
當然,你如果沒有JS、CSS等文件需要使用,那么這幾個文件夾完全不要也是可以的。同時也可以知道,既然就是HTML+CSS+JS,所以也就不需要什么專門的IDE之類的了,我就直接用IDEA建了個項目就寫上了。

然而我們知道,谷歌的擴展類型是 “.crx”,所以這些結構最終會用工具處理一下,這個工具其實就是谷歌瀏覽器自帶的(此處借用一下別人的圖片):
打包

保存
打包好后會在跟目錄同級的地方生成一個跟目錄同名的以 .crx 為后綴的擴展文件;同時還有個 .pem 的密鑰文件,用於擴展程序更新打包時使用,也就是同個擴展在第二次及以后打包時使用,當然你也完全可以把密鑰刪掉,重新打包。

此時crx直接安裝就可以使用嗎?並不行,如果不在谷歌商店上架的話,你安裝了也無法使用,只能在開發者模式下,以加載文件夾的形式進行安裝使用,顧名思義,開發者模式,如果實在不便上架,以后也只能這么使用了:
勾选开发者模式

2、核心文件說明

2.1 manifest.json

{ 
    // (必需)清單文件的版本,這個必須寫,且必須是2
    "manifest_version": 2,
    // (必需)插件的名稱
    "name": "demo",
    // (必需)插件的版本
    "version": "1.0.0",
    // 插件描述
    "description": "簡單的Chrome擴展demo",
    // 圖標,一般偷懶全部用一個尺寸的也沒問題
    "icons":
    {
        "16": "img/icon.png",
        "48": "img/icon.png",
        "128": "img/icon.png"
    },
    // 會一直常駐的后台JS或后台頁面
    "background":
    {
        // 2種指定方式,如果指定JS,那么會自動生成一個背景頁
        "page": "background.html"
        //"scripts": ["js/background.js"]
    },
    // 瀏覽器右上角圖標設置,browser_action、page_action、app必須三選一
    "browser_action": 
    {
        "default_icon": "img/icon.png",
        // 圖標懸停時的標題,可選
        "default_title": "這是一個示例Chrome插件",
        "default_popup": "popup.html"
    },
    // 當某些特定頁面打開才顯示的圖標
    /*"page_action":
    {
        "default_icon": "img/icon.png",
        "default_title": "我是pageAction",
        "default_popup": "popup.html"
    },*/
    // 需要直接注入頁面的JS
    "content_scripts": 
    [
        {
            //"matches": ["http://*/*", "https://*/*"],
            // "<all_urls>" 表示匹配所有地址
            "matches": ["<all_urls>"],
            // 多個JS按順序注入
            "js": ["js/jquery-1.8.3.js", "js/content-script.js"],
            // JS的注入可以隨便一點,但是CSS的注意就要千萬小心了,因為一不小心就可能影響全局樣式
            "css": ["css/custom.css"],
            // 代碼注入的時間,可選值: "document_start", "document_end", or "document_idle",最后一個表示頁面空閑時,默認document_idle
            "run_at": "document_start"
        },
        // 這里僅僅是為了演示content-script可以配置多個規則
        {
            "matches": ["*://*/*.png", "*://*/*.jpg", "*://*/*.gif", "*://*/*.bmp"],
            "js": ["js/show-image-content-size.js"]
        }
    ],
    // 權限申請
    "permissions":
    [
        "contextMenus", // 右鍵菜單
        "tabs", // 標簽
        "notifications", // 通知
        "webRequest", // web請求
        "webRequestBlocking",
        "storage", // 插件本地存儲
        "http://*/*", // 可以通過executeScript或者insertCSS訪問的網站
        "https://*/*" // 可以通過executeScript或者insertCSS訪問的網站
    ],
    // 普通頁面能夠直接訪問的插件資源列表,如果不設置是無法直接訪問的
    "web_accessible_resources": ["js/inject.js"],
    // 插件主頁,這個很重要,不要浪費了這個免費廣告位
    "homepage_url": "https://www.baidu.com",
    // 覆蓋瀏覽器默認頁面
    "chrome_url_overrides":
    {
        // 覆蓋瀏覽器默認的新標簽頁
        "newtab": "newtab.html"
    },
    // Chrome40以前的插件配置頁寫法
    "options_page": "options.html",
    // Chrome40以后的插件配置頁寫法,如果2個都寫,新版Chrome只認后面這一個
    "options_ui":
    {
        "page": "options.html",
        // 添加一些默認的樣式,推薦使用
        "chrome_style": true
    },
    // 向地址欄注冊一個關鍵字以提供搜索建議,只能設置一個關鍵字
    "omnibox": { "keyword" : "go" },
    // 默認語言
    "default_locale": "zh_CN",
    // devtools頁面入口,注意只能指向一個HTML文件,不能是JS文件
    "devtools_page": "devtools.html"
}

2.2 contentScript

其實就是Chrome插件中向頁面注入腳本的一種形式,我們可以通過manifest.json配置的方式向頁面注入指定JS和CSS,示例配置如下:
{
	// 需要直接注入頁面的JS
	"content_scripts": 
	[
		{
			//"matches": ["http://*/*", "https://*/*"],
			// "<all_urls>" 表示匹配所有地址
			"matches": ["<all_urls>"],
			// 多個JS按順序注入
			"js": ["js/jquery-1.8.3.js", "js/content-script.js"],
			// JS的注入可以隨便一點,但是CSS的注意就要千萬小心了,因為一不小心就可能影響全局樣式
			"css": ["css/custom.css"],
			// 代碼注入的時間,可選值: "document_start", "document_end", or "document_idle",最后一個表示頁面空閑時,默認document_idle
			"run_at": "document_start"
		}
	],
}

contentScript和原始頁面共享DOM,但是不共享JS,這意味着: contentScript中的JS可以操作標簽頁面的DOM,但是不能調用標簽頁面的JS

2.3 background

后台(姑且這么翻譯吧),是一個常駐的頁面,它的生命周期是插件中所有類型頁面中最長的,它隨着瀏覽器的打開而打開,隨着瀏覽器的關閉而關閉,所以通常把需要一直運行的、啟動就運行的、全局的代碼放在background里面。示例配置如下:
{
	// 會一直常駐的后台JS或后台頁面
	"background":
	{
		// 2種指定方式,如果指定JS,那么會自動生成一個背景頁
		"page": "background.html"
		//"scripts": ["js/background.js"]
	},
}

2.4 injectedScript

官方並沒有這樣的說法,是引用文中網友的說法,姑且就這樣吧。該類型的腳本不是通過配置實現了,而是通過DOM操作的方式往頁面注入的JS,就是強行寫進去再說了,如下示例:
// 向頁面注入JS
function injectCustomJs(jsPath)
{
	jsPath = jsPath || 'js/inject.js';
	var temp = document.createElement('script');
	temp.setAttribute('type', 'text/javascript');
	// 獲得的地址類似:chrome-extension://ihcokhadfjfchaeagdoclpnjdiokfakg/js/inject.js
	temp.src = chrome.extension.getURL(jsPath);
	temp.onload = function()
	{
		// 放在頁面不好看,執行完后移除掉
		this.parentNode.removeChild(this);
	};
	document.head.appendChild(temp);
}

當然,還需要在配置中聲明一下網頁允許使用的資源地址,否則會報錯:
{
	// 普通頁面能夠直接訪問的插件資源列表,如果不設置是無法直接訪問的
	"web_accessible_resources": ["js/inject.js"],
}

contentScript可以控制DOM但是不能共享頁面的JS,但injectedScript則可以,畢竟是強行寫入的JS。

2.5 popup

這個頁面就是個臨時交互使用的HTML頁面,這之中的JS自然也就只屬於popup頁面了

2.6 動態注入或執行

雖然在background和popup中無法直接訪問頁面DOM,但是可以通過chrome.tabs.executeScript來執行腳本,從而實現訪問web頁面的DOM(注意,這種方式也不能直接訪問頁面JS)。

當然,這種方式還是需要先在配置文件中說明:
{
	"name": "動態JS注入演示",
	...
	"permissions": [
		"tabs", "http://*/*", "https://*/*"
	],
	...
}

而執行時也有兩種,直接執行JS Code,或者執行指定的JS文件:
// 動態執行JS代碼
chrome.tabs.executeScript(tabId, {code: 'document.body.style.backgroundColor="red"'});
// 動態執行JS文件
chrome.tabs.executeScript(tabId, {file: 'some-script.js'});

2.7 其他補充

 

3、通信交互

通信部分直接就引用 官方文檔的消息傳遞部分的了吧,首先由兩種連接方式:
  • 簡單的一次性請求
  • 長時間的連接

3.1 簡單的一次性請求

如果您只需要向您的擴展程序的另一部分發送一個簡單消息(以及可選地獲得回應),您應該使用比較簡單的 runtime.sendMessage 方法。這些方法允許您從內容腳本向擴展程序發送可通過 JSON 序列化的消息,可選的 callback 參數允許您在需要的時候從另一邊處理回應。

如下列代碼所示從內容腳本中發送請求:
chrome.runtime.sendMessage({greeting: "您好"}, function(response) {
  console.log(response.farewell);
});

從擴展程序向內容腳本發送請求與上面類似,唯一的區別是您需要指定發送至哪一個標簽頁。這一例子演示如何向選定標簽頁中的內容腳本發送消息。
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
  chrome.tabs.sendMessage(tabs[0].id, {greeting: "您好"}, function(response) {
    console.log(response.farewell);
  });
});

在接收端,您需要設置一個 runtime.onMessage 事件監聽器來處理消息。
chrome.runtime.onMessage.addListener(
  function(request, sender, sendResponse) {
    console.log(sender.tab ?
                "來自內容腳本:" + sender.tab.url :
                "來自擴展程序");
    if (request.greeting == "您好")
      sendResponse({farewell: "再見"});
  });

注意: 如果多個頁面都監聽 onMessage 事件,對於某一次事件只有第一次調用 sendResponse() 能成功發出回應,所有其他回應將被忽略

3.2 其他

長時間連接以及跨域等連接訪問此處不再展開了,因為我自己沒有用到,需要用到的時再查吧。

3.3 互相通信概覽

 
如下橫向看,如第三行,表示 popup-js 只能和 content-script / background-js 通信,通信方法如表格中所示:

4、跳坑

4.1 不支持inline javascript代碼

從Chrome Extenstion V2開始,不允許執行任何inline javascript代碼(也就是html內的任何js代碼都不允許執行),比如下面的代碼:
<input type="submit" name="btn_submit" value="收藏" id="btn_submit" class="btn_submit" onclick="addwz()"/>

onclick中的addwz()函數不允許執行,點擊時會報錯。只能在內部引用的JS文件中綁定事件:
$('#btn_submit').click(function () {
    addwz();
});

4.2 通信在調試過程中失敗的原因

很有可能你需要的是關掉瀏覽器,重新打開(手動微笑,為此我浪費了不少時間

4.3 JS的ready時機

window.onload = function () {
    //JS
}

4.4 其他

明明下午時候記得還有一些,晚上回憶就記不得了... 看來以后遇到坑在解決之后要立馬記錄,囧,如果又遇到以前有過的坑,又得重新爬


附件列表

     


    免責聲明!

    本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



     
    粵ICP備18138465號   © 2018-2025 CODEPRJ.COM