欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Chrome插件(擴(kuò)展)開(kāi)發(fā)全攻略(完整demo)

 更新時(shí)間:2021年05月17日 15:08:39   作者:我是小茗同學(xué)  
Chrome插件是一個(gè)用Web技術(shù)開(kāi)發(fā)、用來(lái)增強(qiáng)瀏覽器功能的軟件,它其實(shí)就是一個(gè)由HTML、CSS、JS、圖片等資源組成的一個(gè).crx后綴的壓縮包,本文給大家分享一個(gè)Chrome插件(擴(kuò)展)開(kāi)發(fā)全攻略完整demo,感興趣的朋友跟隨小編一起學(xué)習(xí)下吧

寫(xiě)在前面

我花了將近一個(gè)多月的時(shí)間斷斷續(xù)續(xù)寫(xiě)下這篇博文,并精心寫(xiě)下完整demo,寫(xiě)博客的辛苦大家懂的,所以轉(zhuǎn)載務(wù)必保留出處。本文所有涉及到的大部分代碼均在這個(gè)demo里面:https://github.com/sxei/chrome-plugin-demo ,大家可以直接下載下來(lái)運(yùn)行。

另外,本文圖片較多,且圖片服務(wù)器帶寬有限,右下角的目錄滾動(dòng)監(jiān)聽(tīng)必須等到圖片全部加載完畢之后才會(huì)觸發(fā),所以請(qǐng)耐心等待加載完畢。

本文目錄:

demo部分截圖:

前言

什么是Chrome插件

嚴(yán)格來(lái)講,我們正在說(shuō)的東西應(yīng)該叫Chrome擴(kuò)展(Chrome Extension),真正意義上的Chrome插件是更底層的瀏覽器功能擴(kuò)展,可能需要對(duì)瀏覽器源碼有一定掌握才有能力去開(kāi)發(fā)。鑒于Chrome插件的叫法已經(jīng)習(xí)慣,本文也全部采用這種叫法,但讀者需深知本文所描述的Chrome插件實(shí)際上指的是Chrome擴(kuò)展。

Chrome插件是一個(gè)用Web技術(shù)開(kāi)發(fā)、用來(lái)增強(qiáng)瀏覽器功能的軟件,它其實(shí)就是一個(gè)由HTML、CSS、JS、圖片等資源組成的一個(gè).crx后綴的壓縮包.

個(gè)人猜測(cè)crx可能是Chrome Extension如下3個(gè)字母的簡(jiǎn)寫(xiě):

另外,其實(shí)不只是前端技術(shù),Chrome插件還可以配合C++編寫(xiě)的dll動(dòng)態(tài)鏈接庫(kù)實(shí)現(xiàn)一些更底層的功能(NPAPI),比如全屏幕截圖。

由于安全原因,Chrome瀏覽器42以上版本已經(jīng)陸續(xù)不再支持NPAPI插件,取而代之的是更安全的PPAPI。

學(xué)習(xí)Chrome插件開(kāi)發(fā)有什么意義

增強(qiáng)瀏覽器功能,輕松實(shí)現(xiàn)屬于自己的“定制版”瀏覽器,等等。

Chrome插件提供了很多實(shí)用API供我們使用,包括但不限于:

  • 書(shū)簽控制;
  • 下載控制;
  • 窗口控制;
  • 標(biāo)簽控制;
  • 網(wǎng)絡(luò)請(qǐng)求控制,各類(lèi)事件監(jiān)聽(tīng);
  • 自定義原生菜單;
  • 完善的通信機(jī)制;
  • 等等;

為什么是Chrome插件而不是Firefox插件

  • Chrome占有率更高,更多人用;
  • 開(kāi)發(fā)更簡(jiǎn)單;
  • 應(yīng)用場(chǎng)景更廣泛,F(xiàn)irefox插件只能運(yùn)行在Firefox上,而Chrome除了Chrome瀏覽器之外,還可以運(yùn)行在所有webkit內(nèi)核的國(guó)產(chǎn)瀏覽器,比如360極速瀏覽器、360安全瀏覽器、搜狗瀏覽器、QQ瀏覽器等等;
  • 除此之外,F(xiàn)irefox瀏覽器也對(duì)Chrome插件的運(yùn)行提供了一定的支持;

開(kāi)發(fā)與調(diào)試

Chrome插件沒(méi)有嚴(yán)格的項(xiàng)目結(jié)構(gòu)要求,只要保證本目錄有一個(gè)manifest.json即可,也不需要專(zhuān)門(mén)的IDE,普通的web開(kāi)發(fā)工具即可。

從右上角菜單->更多工具->擴(kuò)展程序可以進(jìn)入 插件管理頁(yè)面,也可以直接在地址欄輸入 chrome://extensions 訪(fǎng)問(wèn)。

勾選開(kāi)發(fā)者模式即可以文件夾的形式直接加載插件,否則只能安裝.crx格式的文件。Chrome要求插件必須從它的Chrome應(yīng)用商店安裝,其它任何網(wǎng)站下載的都無(wú)法直接安裝,所以,其實(shí)我們可以把crx文件解壓,然后通過(guò)開(kāi)發(fā)者模式直接加載。

開(kāi)發(fā)中,代碼有任何改動(dòng)都必須重新加載插件,只需要在插件管理頁(yè)按下Ctrl+R即可,以防萬(wàn)一最好還把頁(yè)面刷新一下。

核心介紹

manifest.json

這是一個(gè)Chrome插件最重要也是必不可少的文件,用來(lái)配置所有和插件相關(guān)的配置,必須放在根目錄。其中,manifest_version、name、version3個(gè)是必不可少的,descriptionicons是推薦的。

下面給出的是一些常見(jiàn)的配置項(xiàng),均有中文注釋?zhuān)暾呐渲梦臋n請(qǐng)戳這里。

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

content-scripts

所謂content-scripts,其實(shí)就是Chrome插件中向頁(yè)面注入腳本的一種形式(雖然名為script,其實(shí)還可以包括css的),借助content-scripts我們可以實(shí)現(xiàn)通過(guò)配置的方式輕松向指定頁(yè)面注入JS和CSS(如果需要?jiǎng)討B(tài)注入,可以參考下文),最常見(jiàn)的比如:廣告屏蔽、頁(yè)面CSS定制,等等。

示例配置:

{
	// 需要直接注入頁(yè)面的JS
	"content_scripts": 
	[
		{
			//"matches": ["http://*/*", "https://*/*"],
			// "<all_urls>" 表示匹配所有地址
			"matches": ["<all_urls>"],
			// 多個(gè)JS按順序注入
			"js": ["js/jquery-1.8.3.js", "js/content-script.js"],
			// JS的注入可以隨便一點(diǎn),但是CSS的注意就要千萬(wàn)小心了,因?yàn)橐徊恍⌒木涂赡苡绊懭謽邮?
			"css": ["css/custom.css"],
			// 代碼注入的時(shí)間,可選值: "document_start", "document_end", or "document_idle",最后一個(gè)表示頁(yè)面空閑時(shí),默認(rèn)document_idle
			"run_at": "document_start"
		}
	],
}

特別注意,如果沒(méi)有主動(dòng)指定run_atdocument_start(默認(rèn)為document_idle),下面這種代碼是不會(huì)生效的:

document.addEventListener('DOMContentLoaded', function()
{
	console.log('我被執(zhí)行了!');
});

content-scripts和原始頁(yè)面共享DOM,但是不共享JS,如要訪(fǎng)問(wèn)頁(yè)面JS(例如某個(gè)JS變量),只能通過(guò)injected js來(lái)實(shí)現(xiàn)。content-scripts不能訪(fǎng)問(wèn)絕大部分chrome.xxx.api,除了下面這4種:

  • chrome.extension(getURL , inIncognitoContext , lastError , onRequest , sendRequest)
  • chrome.i18n
  • chrome.runtime(connect , getManifest , getURL , id , onConnect , onMessage , sendMessage)
  • chrome.storage

其實(shí)看到這里不要悲觀(guān),這些API絕大部分時(shí)候都?jí)蛴昧?,非要調(diào)用其它API的話(huà),你還可以通過(guò)通信來(lái)實(shí)現(xiàn)讓background來(lái)幫你調(diào)用(關(guān)于通信,后文有詳細(xì)介紹)。

好了,Chrome插件給我們提供了這么強(qiáng)大的JS注入功能,剩下的就是發(fā)揮你的想象力去玩弄瀏覽器了。

background

后臺(tái)(姑且這么翻譯吧),是一個(gè)常駐的頁(yè)面,它的生命周期是插件中所有類(lèi)型頁(yè)面中最長(zhǎng)的,它隨著瀏覽器的打開(kāi)而打開(kāi),隨著瀏覽器的關(guān)閉而關(guān)閉,所以通常把需要一直運(yùn)行的、啟動(dòng)就運(yùn)行的、全局的代碼放在background里面。

background的權(quán)限非常高,幾乎可以調(diào)用所有的Chrome擴(kuò)展API(除了devtools),而且它可以無(wú)限制跨域,也就是可以跨域訪(fǎng)問(wèn)任何網(wǎng)站而無(wú)需要求對(duì)方設(shè)置CORS

經(jīng)過(guò)測(cè)試,其實(shí)不止是background,所有的直接通過(guò)chrome-extension://id/xx.html這種方式打開(kāi)的網(wǎng)頁(yè)都可以無(wú)限制跨域。

配置中,background可以通過(guò)page指定一張網(wǎng)頁(yè),也可以通過(guò)scripts直接指定一個(gè)JS,Chrome會(huì)自動(dòng)為這個(gè)JS生成一個(gè)默認(rèn)的網(wǎng)頁(yè):

{
	// 會(huì)一直常駐的后臺(tái)JS或后臺(tái)頁(yè)面
	"background":
	{
		// 2種指定方式,如果指定JS,那么會(huì)自動(dòng)生成一個(gè)背景頁(yè)
		"page": "background.html"
		//"scripts": ["js/background.js"]
	},
}

需要特別說(shuō)明的是,雖然你可以通過(guò)chrome-extension://xxx/background.html直接打開(kāi)后臺(tái)頁(yè),但是你打開(kāi)的后臺(tái)頁(yè)和真正一直在后臺(tái)運(yùn)行的那個(gè)頁(yè)面不是同一個(gè),換句話(huà)說(shuō),你可以打開(kāi)無(wú)數(shù)個(gè)background.html,但是真正在后臺(tái)常駐的只有一個(gè),而且這個(gè)你永遠(yuǎn)看不到它的界面,只能調(diào)試它的代碼。

event-pages

這里順帶介紹一下event-pages,它是一個(gè)什么東西呢?鑒于background生命周期太長(zhǎng),長(zhǎng)時(shí)間掛載后臺(tái)可能會(huì)影響性能,所以Google又弄一個(gè)event-pages,在配置文件上,它與background的唯一區(qū)別就是多了一個(gè)persistent參數(shù):

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

它的生命周期是:在被需要時(shí)加載,在空閑時(shí)被關(guān)閉,什么叫被需要時(shí)呢?比如第一次安裝、插件更新、有content-script向它發(fā)送消息,等等。

除了配置文件的變化,代碼上也有一些細(xì)微變化,個(gè)人這個(gè)簡(jiǎn)單了解一下就行了,一般情況下background也不會(huì)很消耗性能的。

popup

popup是點(diǎn)擊browser_action或者page_action圖標(biāo)時(shí)打開(kāi)的一個(gè)小窗口網(wǎng)頁(yè),焦點(diǎn)離開(kāi)網(wǎng)頁(yè)就立即關(guān)閉,一般用來(lái)做一些臨時(shí)性的交互。

popup可以包含任意你想要的HTML內(nèi)容,并且會(huì)自適應(yīng)大小。可以通過(guò)default_popup字段來(lái)指定popup頁(yè)面,也可以調(diào)用setPopup()方法。

配置方式:

{
	"browser_action":
	{
		"default_icon": "img/icon.png",
		// 圖標(biāo)懸停時(shí)的標(biāo)題,可選
		"default_title": "這是一個(gè)示例Chrome插件",
		"default_popup": "popup.html"
	}
}

需要特別注意的是,由于單擊圖標(biāo)打開(kāi)popup,焦點(diǎn)離開(kāi)又立即關(guān)閉,所以popup頁(yè)面的生命周期一般很短,需要長(zhǎng)時(shí)間運(yùn)行的代碼千萬(wàn)不要寫(xiě)在popup里面。

在權(quán)限上,它和background非常類(lèi)似,它們之間最大的不同是生命周期的不同,popup中可以直接通過(guò)chrome.extension.getBackgroundPage()獲取background的window對(duì)象。

injected-script

這里的injected-script是我給它取的,指的是通過(guò)DOM操作的方式向頁(yè)面注入的一種JS。為什么要把這種JS單獨(dú)拿出來(lái)討論呢?又或者說(shuō)為什么需要通過(guò)這種方式注入JS呢?

這是因?yàn)?code>content-script有一個(gè)很大的“缺陷”,也就是無(wú)法訪(fǎng)問(wèn)頁(yè)面中的JS,雖然它可以操作DOM,但是DOM卻不能調(diào)用它,也就是無(wú)法在DOM中通過(guò)綁定事件的方式調(diào)用content-script中的代碼(包括直接寫(xiě)onclickaddEventListener2種方式都不行),但是,“在頁(yè)面上添加一個(gè)按鈕并調(diào)用插件的擴(kuò)展API”是一個(gè)很常見(jiàn)的需求,那該怎么辦呢?其實(shí)這就是本小節(jié)要講的。

content-script中通過(guò)DOM方式向頁(yè)面注入inject-script代碼示例:

// 向頁(yè)面注入JS
function injectCustomJs(jsPath)
{
	jsPath = jsPath || 'js/inject.js';
	var temp = document.createElement('script');
	temp.setAttribute('type', 'text/javascript');
	// 獲得的地址類(lèi)似:chrome-extension://ihcokhadfjfchaeagdoclpnjdiokfakg/js/inject.js
	temp.src = chrome.extension.getURL(jsPath);
	temp.onload = function()
	{
		// 放在頁(yè)面不好看,執(zhí)行完后移除掉
		this.parentNode.removeChild(this);
	};
	document.head.appendChild(temp);
}

你以為這樣就行了?執(zhí)行一下你會(huì)看到如下報(bào)錯(cuò):

Denying load of chrome-extension://efbllncjkjiijkppagepehoekjojdclc/js/inject.js. Resources must be listed in the web_accessible_resources manifest key in order to be loaded by pages outside the extension.

意思就是你想要在web中直接訪(fǎng)問(wèn)插件中的資源的話(huà)必須顯示聲明才行,配置文件中增加如下:

{
	// 普通頁(yè)面能夠直接訪(fǎng)問(wèn)的插件資源列表,如果不設(shè)置是無(wú)法直接訪(fǎng)問(wèn)的
	"web_accessible_resources": ["js/inject.js"],
}

至于inject-script如何調(diào)用content-script中的代碼,后面我會(huì)在專(zhuān)門(mén)的一個(gè)消息通信章節(jié)詳細(xì)介紹。

homepage_url

開(kāi)發(fā)者或者插件主頁(yè)設(shè)置,一般會(huì)在如下2個(gè)地方顯示:

Chrome插件的8種展示形式

browserAction(瀏覽器右上角)

通過(guò)配置browser_action可以在瀏覽器的右上角增加一個(gè)圖標(biāo),一個(gè)browser_action可以擁有一個(gè)圖標(biāo),一個(gè)tooltip,一個(gè)badge和一個(gè)popup。

示例配置如下:

"browser_action":
{
	"default_icon": "img/icon.png",
	"default_title": "這是一個(gè)示例Chrome插件",
	"default_popup": "popup.html"
}

圖標(biāo)

browser_action圖標(biāo)推薦使用寬高都為19像素的圖片,更大的圖標(biāo)會(huì)被縮小,格式隨意,一般推薦png,可以通過(guò)manifest中default_icon字段配置,也可以調(diào)用setIcon()方法。

tooltip

修改browser_action的manifest中default_title字段,或者調(diào)用setTitle()方法。

badge

所謂badge就是在圖標(biāo)上顯示一些文本,可以用來(lái)更新一些小的擴(kuò)展?fàn)顟B(tài)提示信息。因?yàn)閎adge空間有限,所以只支持4個(gè)以下的字符(英文4個(gè),中文2個(gè))。badge無(wú)法通過(guò)配置文件來(lái)指定,必須通過(guò)代碼實(shí)現(xiàn),設(shè)置badge文字和顏色可以分別使用setBadgeText()setBadgeBackgroundColor()。

chrome.browserAction.setBadgeText({text: 'new'});
chrome.browserAction.setBadgeBackgroundColor({color: [255, 0, 0, 255]});

效果:

pageAction(地址欄右側(cè))

所謂pageAction,指的是只有當(dāng)某些特定頁(yè)面打開(kāi)才顯示的圖標(biāo),它和browserAction最大的區(qū)別是一個(gè)始終都顯示,一個(gè)只在特定情況才顯示。

需要特別說(shuō)明的是早些版本的Chrome是將pageAction放在地址欄的最右邊,左鍵單擊彈出popup,右鍵單擊則彈出相關(guān)默認(rèn)的選項(xiàng)菜單:

而新版的Chrome更改了這一策略,pageAction和普通的browserAction一樣也是放在瀏覽器右上角,只不過(guò)沒(méi)有點(diǎn)亮?xí)r是灰色的,點(diǎn)亮了才是彩色的,灰色時(shí)無(wú)論左鍵還是右鍵單擊都是彈出選項(xiàng):

具體是從哪一版本開(kāi)始改的沒(méi)去仔細(xì)考究,反正知道v50.0的時(shí)候還是前者,v58.0的時(shí)候已改為后者。

調(diào)整之后的pageAction我們可以簡(jiǎn)單地把它看成是可以置灰的browserAction。

  • chrome.pageAction.show(tabId) 顯示圖標(biāo);
  • chrome.pageAction.hide(tabId) 隱藏圖標(biāo);

示例(只有打開(kāi)百度才顯示圖標(biāo)):

// manifest.json
{
	"page_action":
	{
		"default_icon": "img/icon.png",
		"default_title": "我是pageAction",
		"default_popup": "popup.html"
	},
	"permissions": ["declarativeContent"]
}

// background.js
chrome.runtime.onInstalled.addListener(function(){
	chrome.declarativeContent.onPageChanged.removeRules(undefined, function(){
		chrome.declarativeContent.onPageChanged.addRules([
			{
				conditions: [
					// 只有打開(kāi)百度才顯示pageAction
					new chrome.declarativeContent.PageStateMatcher({pageUrl: {urlContains: 'baidu.com'}})
				],
				actions: [new chrome.declarativeContent.ShowPageAction()]
			}
		]);
	});
});

效果圖:

右鍵菜單

通過(guò)開(kāi)發(fā)Chrome插件可以自定義瀏覽器的右鍵菜單,主要是通過(guò)chrome.contextMenusAPI實(shí)現(xiàn),右鍵菜單可以出現(xiàn)在不同的上下文,比如普通頁(yè)面、選中的文字、圖片、鏈接,等等,如果有同一個(gè)插件里面定義了多個(gè)菜單,Chrome會(huì)自動(dòng)組合放到以插件名字命名的二級(jí)菜單里,如下:

最簡(jiǎn)單的右鍵菜單示例

// manifest.json
{"permissions": ["contextMenus"]}

// background.js
chrome.contextMenus.create({
	title: "測(cè)試右鍵菜單",
	onclick: function(){alert('您點(diǎn)擊了右鍵菜單!');}
});

效果:

添加右鍵百度搜索

// manifest.json
{"permissions": ["contextMenus", "tabs"]}

// background.js
chrome.contextMenus.create({
	title: '使用度娘搜索:%s', // %s表示選中的文字
	contexts: ['selection'], // 只有當(dāng)選中文字時(shí)才會(huì)出現(xiàn)此右鍵菜單
	onclick: function(params)
	{
		// 注意不能使用location.href,因?yàn)閘ocation是屬于background的window對(duì)象
		chrome.tabs.create({url: 'https://www.baidu.com/s?ie=utf-8&wd=' + encodeURI(params.selectionText)});
	}
});

效果如下:

語(yǔ)法說(shuō)明

這里只是簡(jiǎn)單列舉一些常用的,完整API參見(jiàn):https://developer.chrome.com/extensions/contextMenus

chrome.contextMenus.create({
	type: 'normal', // 類(lèi)型,可選:["normal", "checkbox", "radio", "separator"],默認(rèn) normal
	title: '菜單的名字', // 顯示的文字,除非為“separator”類(lèi)型否則此參數(shù)必需,如果類(lèi)型為“selection”,可以使用%s顯示選定的文本
	contexts: ['page'], // 上下文環(huán)境,可選:["all", "page", "frame", "selection", "link", "editable", "image", "video", "audio"],默認(rèn)page
	onclick: function(){}, // 單擊時(shí)觸發(fā)的方法
	parentId: 1, // 右鍵菜單項(xiàng)的父菜單項(xiàng)ID。指定父菜單項(xiàng)將會(huì)使此菜單項(xiàng)成為父菜單項(xiàng)的子菜單
	documentUrlPatterns: 'https://*.baidu.com/*' // 只在某些頁(yè)面顯示此右鍵菜單
});
// 刪除某一個(gè)菜單項(xiàng)
chrome.contextMenus.remove(menuItemId);
// 刪除所有自定義右鍵菜單
chrome.contextMenus.removeAll();
// 更新某一個(gè)菜單項(xiàng)
chrome.contextMenus.update(menuItemId, updateProperties);

override(覆蓋特定頁(yè)面)

使用override頁(yè)可以將Chrome默認(rèn)的一些特定頁(yè)面替換掉,改為使用擴(kuò)展提供的頁(yè)面。

擴(kuò)展可以替代如下頁(yè)面:

  • 歷史記錄:從工具菜單上點(diǎn)擊歷史記錄時(shí)訪(fǎng)問(wèn)的頁(yè)面,或者從地址欄直接輸入 chrome://history
  • 新標(biāo)簽頁(yè):當(dāng)創(chuàng)建新標(biāo)簽的時(shí)候訪(fǎng)問(wèn)的頁(yè)面,或者從地址欄直接輸入 chrome://newtab
  • 書(shū)簽:瀏覽器的書(shū)簽,或者直接輸入 chrome://bookmarks

注意:

  • 一個(gè)擴(kuò)展只能替代一個(gè)頁(yè)面;
  • 不能替代隱身窗口的新標(biāo)簽頁(yè);
  • 網(wǎng)頁(yè)必須設(shè)置title,否則用戶(hù)可能會(huì)看到網(wǎng)頁(yè)的URL,造成困擾;

下面的截圖是默認(rèn)的新標(biāo)簽頁(yè)和被擴(kuò)展替換掉的新標(biāo)簽頁(yè)。

代碼(注意,一個(gè)插件只能替代一個(gè)默認(rèn)頁(yè),以下僅為演示):

"chrome_url_overrides":
{
	"newtab": "newtab.html",
	"history": "history.html",
	"bookmarks": "bookmarks.html"
}

devtools(開(kāi)發(fā)者工具)

預(yù)熱

使用過(guò)vue的應(yīng)該見(jiàn)過(guò)這種類(lèi)型的插件:

是的,Chrome允許插件在開(kāi)發(fā)者工具(devtools)上動(dòng)手腳,主要表現(xiàn)在:

  • 自定義一個(gè)和多個(gè)和Elements、ConsoleSources等同級(jí)別的面板;
  • 自定義側(cè)邊欄(sidebar),目前只能自定義Elements面板的側(cè)邊欄;

先來(lái)看2張簡(jiǎn)單的demo截圖,自定義面板(判斷當(dāng)前頁(yè)面是否使用了jQuery):

自定義側(cè)邊欄(獲取當(dāng)前頁(yè)面所有圖片):

devtools擴(kuò)展介紹

主頁(yè):https://developer.chrome.com/extensions/devtools

來(lái)一張官方圖片:

每打開(kāi)一個(gè)開(kāi)發(fā)者工具窗口,都會(huì)創(chuàng)建devtools頁(yè)面的實(shí)例,F(xiàn)12窗口關(guān)閉,頁(yè)面也隨著關(guān)閉,所以devtools頁(yè)面的生命周期和devtools窗口是一致的。devtools頁(yè)面可以訪(fǎng)問(wèn)一組特有的DevTools API以及有限的擴(kuò)展API,這組特有的DevTools API只有devtools頁(yè)面才可以訪(fǎng)問(wèn),background都無(wú)權(quán)訪(fǎng)問(wèn),這些API包括:

  • chrome.devtools.panels:面板相關(guān);
  • chrome.devtools.inspectedWindow:獲取被審查窗口的有關(guān)信息;
  • chrome.devtools.network:獲取有關(guān)網(wǎng)絡(luò)請(qǐng)求的信息;

大部分?jǐn)U展API都無(wú)法直接被DevTools頁(yè)面調(diào)用,但它可以像content-script一樣直接調(diào)用chrome.extensionchrome.runtimeAPI,同時(shí)它也可以像content-script一樣使用Message交互的方式與background頁(yè)面進(jìn)行通信。

實(shí)例:創(chuàng)建一個(gè)devtools擴(kuò)展

首先,要針對(duì)開(kāi)發(fā)者工具開(kāi)發(fā)插件,需要在清單文件聲明如下:

{
	// 只能指向一個(gè)HTML文件,不能是JS文件
	"devtools_page": "devtools.html"
}

這個(gè)devtools.html里面一般什么都沒(méi)有,就引入一個(gè)js:

<!DOCTYPE html>
<html>
<head></head>
<body>
	<script type="text/javascript" src="js/devtools.js"></script>
</body>
</html>

可以看出來(lái),其實(shí)真正代碼是devtools.js,html文件是“多余”的,所以這里覺(jué)得有點(diǎn)坑,devtools_page干嘛不允許直接指定JS呢?

再來(lái)看devtools.js的代碼:

// 創(chuàng)建自定義面板,同一個(gè)插件可以創(chuàng)建多個(gè)自定義面板
// 幾個(gè)參數(shù)依次為:panel標(biāo)題、圖標(biāo)(其實(shí)設(shè)置了也沒(méi)地方顯示)、要加載的頁(yè)面、加載成功后的回調(diào)
chrome.devtools.panels.create('MyPanel', 'img/icon.png', 'mypanel.html', function(panel)
{
	console.log('自定義面板創(chuàng)建成功!'); // 注意這個(gè)log一般看不到
});

// 創(chuàng)建自定義側(cè)邊欄
chrome.devtools.panels.elements.createSidebarPane("Images", function(sidebar)
{
	// sidebar.setPage('../sidebar.html'); // 指定加載某個(gè)頁(yè)面
	sidebar.setExpression('document.querySelectorAll("img")', 'All Images'); // 通過(guò)表達(dá)式來(lái)指定
	//sidebar.setObject({aaa: 111, bbb: 'Hello World!'}); // 直接設(shè)置顯示某個(gè)對(duì)象
});

setPage時(shí)的效果:

以下截圖示例的代碼:

// 檢測(cè)jQuery
document.getElementById('check_jquery').addEventListener('click', function()
{
	// 訪(fǎng)問(wèn)被檢查的頁(yè)面DOM需要使用inspectedWindow
	// 簡(jiǎn)單例子:檢測(cè)被檢查頁(yè)面是否使用了jQuery
	chrome.devtools.inspectedWindow.eval("jQuery.fn.jquery", function(result, isException)
	{
		var html = '';
		if (isException) html = '當(dāng)前頁(yè)面沒(méi)有使用jQuery。';
		else html = '當(dāng)前頁(yè)面使用了jQuery,版本為:'+result;
		alert(html);
	});
});

// 打開(kāi)某個(gè)資源
document.getElementById('open_resource').addEventListener('click', function()
{
	chrome.devtools.inspectedWindow.eval("window.location.href", function(result, isException)
	{
		chrome.devtools.panels.openResource(result, 20, function()
		{
			console.log('資源打開(kāi)成功!');
		});
	});
});

// 審查元素
document.getElementById('test_inspect').addEventListener('click', function()
{
	chrome.devtools.inspectedWindow.eval("inspect(document.images[0])", function(result, isException){});
});

// 獲取所有資源
document.getElementById('get_all_resources').addEventListener('click', function()
{
	chrome.devtools.inspectedWindow.getResources(function(resources)
	{
		alert(JSON.stringify(resources));
	});
});

調(diào)試技巧

修改了devtools頁(yè)面的代碼時(shí),需要先在 chrome://extensions 頁(yè)面按下Ctrl+R重新加載插件,然后關(guān)閉再打開(kāi)開(kāi)發(fā)者工具即可,無(wú)需刷新頁(yè)面(而且只刷新頁(yè)面不刷新開(kāi)發(fā)者工具的話(huà)是不會(huì)生效的)。

由于devtools本身就是開(kāi)發(fā)者工具頁(yè)面,所以幾乎沒(méi)有方法可以直接調(diào)試它,直接用 chrome-extension://extid/devtools.html"的方式打開(kāi)頁(yè)面肯定報(bào)錯(cuò),因?yàn)椴恢С窒嚓P(guān)特殊API,只能先自己寫(xiě)一些方法屏蔽這些錯(cuò)誤,調(diào)試通了再放開(kāi)。

option(選項(xiàng)頁(yè))

所謂options頁(yè),就是插件的設(shè)置頁(yè)面,有2個(gè)入口,一個(gè)是右鍵圖標(biāo)有一個(gè)“選項(xiàng)”菜單,還有一個(gè)在插件管理頁(yè)面:

在Chrome40以前,options頁(yè)面和其它普通頁(yè)面沒(méi)什么區(qū)別,Chrome40以后則有了一些變化。

我們先看老版的options

{
	// Chrome40以前的插件配置頁(yè)寫(xiě)法
	"options_page": "options.html",
}

這個(gè)頁(yè)面里面的內(nèi)容就隨你自己發(fā)揮了,配置之后在插件管理頁(yè)就會(huì)看到一個(gè)選項(xiàng)按鈕入口,點(diǎn)進(jìn)去就是打開(kāi)一個(gè)網(wǎng)頁(yè),沒(méi)啥好講的。

效果:

再來(lái)看新版的optionsV2

{
	"options_ui":
	{
    	"page": "options.html",
		// 添加一些默認(rèn)的樣式,推薦使用
    	"chrome_style": true
	},
}

options.html的代碼我們沒(méi)有任何改動(dòng),只是配置文件改了,之后效果如下:

看起來(lái)是不是高大上了?

幾點(diǎn)注意:

  • 為了兼容,建議2種都寫(xiě),如果都寫(xiě)了,Chrome40以后會(huì)默認(rèn)讀取新版的方式;
  • 新版options中不能使用alert;
  • 數(shù)據(jù)存儲(chǔ)建議用chrome.storage,因?yàn)闀?huì)隨用戶(hù)自動(dòng)同步;

omnibox

omnibox是向用戶(hù)提供搜索建議的一種方式。先來(lái)看個(gè)gif圖以便了解一下這東西到底是個(gè)什么鬼:

注冊(cè)某個(gè)關(guān)鍵字以觸發(fā)插件自己的搜索建議界面,然后可以任意發(fā)揮了。

首先,配置文件如下:

{
	// 向地址欄注冊(cè)一個(gè)關(guān)鍵字以提供搜索建議,只能設(shè)置一個(gè)關(guān)鍵字
	"omnibox": { "keyword" : "go" },
}

然后background.js中注冊(cè)監(jiān)聽(tīng)事件:

// omnibox 演示
chrome.omnibox.onInputChanged.addListener((text, suggest) => {
	console.log('inputChanged: ' + text);
	if(!text) return;
	if(text == '美女') {
		suggest([
			{content: '中國(guó)' + text, description: '你要找“中國(guó)美女”嗎?'},
			{content: '日本' + text, description: '你要找“日本美女”嗎?'},
			{content: '泰國(guó)' + text, description: '你要找“泰國(guó)美女或人妖”嗎?'},
			{content: '韓國(guó)' + text, description: '你要找“韓國(guó)美女”嗎?'}
		]);
	}
	else if(text == '微博') {
		suggest([
			{content: '新浪' + text, description: '新浪' + text},
			{content: '騰訊' + text, description: '騰訊' + text},
			{content: '搜狐' + text, description: '搜索' + text},
		]);
	}
	else {
		suggest([
			{content: '百度搜索 ' + text, description: '百度搜索 ' + text},
			{content: '谷歌搜索 ' + text, description: '谷歌搜索 ' + text},
		]);
	}
});

// 當(dāng)用戶(hù)接收關(guān)鍵字建議時(shí)觸發(fā)
chrome.omnibox.onInputEntered.addListener((text) => {
    console.log('inputEntered: ' + text);
	if(!text) return;
	var href = '';
    if(text.endsWith('美女'))  + text;
	else if(text.startsWith('百度搜索'))  + text.replace('百度搜索 ', '');
	else if(text.startsWith('谷歌搜索'))  + text.replace('谷歌搜索 ', '');
	else  + text;
	openUrlCurrentTab(href);
});
// 獲取當(dāng)前選項(xiàng)卡ID
function getCurrentTabId(callback)
{
	chrome.tabs.query({active: true, currentWindow: true}, function(tabs)
	{
		if(callback) callback(tabs.length ? tabs[0].id: null);
	});
}

// 當(dāng)前標(biāo)簽打開(kāi)某個(gè)鏈接
function openUrlCurrentTab(url)
{
	getCurrentTabId(tabId => {
		chrome.tabs.update(tabId, {url: url});
	})
}

桌面通知

Chrome提供了一個(gè)chrome.notificationsAPI以便插件推送桌面通知,暫未找到chrome.notifications和HTML5自帶的Notification的顯著區(qū)別及優(yōu)勢(shì)。

在后臺(tái)JS中,無(wú)論是使用chrome.notifications還是Notification都不需要申請(qǐng)權(quán)限(HTML5方式需要申請(qǐng)權(quán)限),直接使用即可。

最簡(jiǎn)單的通知:

代碼:

chrome.notifications.create(null, {
	type: 'basic',
	iconUrl: 'img/icon.png',
	title: '這是標(biāo)題',
	message: '您剛才點(diǎn)擊了自定義右鍵菜單!'
});

通知的樣式可以很豐富:

這個(gè)沒(méi)有深入研究,有需要的可以去看官方文檔。

5種類(lèi)型的JS對(duì)比

Chrome插件的JS主要可以分為這5類(lèi):injected scriptcontent-script、popup jsbackground jsdevtools js,

權(quán)限對(duì)比

 

JS種類(lèi) 可訪(fǎng)問(wèn)的API DOM訪(fǎng)問(wèn)情況 JS訪(fǎng)問(wèn)情況 直接跨域
injected script 和普通JS無(wú)任何差別,不能訪(fǎng)問(wèn)任何擴(kuò)展API 可以訪(fǎng)問(wèn) 可以訪(fǎng)問(wèn) 不可以
content script 只能訪(fǎng)問(wèn) extension、runtime等部分API 可以訪(fǎng)問(wèn) 不可以 不可以
popup js 可訪(fǎng)問(wèn)絕大部分API,除了devtools系列 不可直接訪(fǎng)問(wèn) 不可以 可以
background js 可訪(fǎng)問(wèn)絕大部分API,除了devtools系列 不可直接訪(fǎng)問(wèn) 不可以 可以
devtools js 只能訪(fǎng)問(wèn) devtools、extension、runtime等部分API 可以 可以 不可以

調(diào)試方式對(duì)比

JS類(lèi)型 調(diào)試方式 圖片說(shuō)明
injected script 直接普通的F12即可 懶得截圖
content-script 打開(kāi)Console,如圖切換
popup-js popup頁(yè)面右鍵審查元素
background 插件管理頁(yè)點(diǎn)擊背景頁(yè)即可
devtools-js 暫未找到有效方法 -

消息通信

通信主頁(yè):https://developer.chrome.com/extensions/messaging

前面我們介紹了Chrome插件中存在的5種JS,那么它們之間如何互相通信呢?下面先來(lái)系統(tǒng)概況一下,然后再分類(lèi)細(xì)說(shuō)。需要知道的是,popup和background其實(shí)幾乎可以視為一種東西,因?yàn)樗鼈兛稍L(fǎng)問(wèn)的API都一樣、通信機(jī)制一樣、都可以跨域。

互相通信概覽

注:-表示不存在或者無(wú)意義,或者待驗(yàn)證。

injected-script content-script popup-js background-js
injected-script - window.postMessage - -
content-script window.postMessage - chrome.runtime.sendMessage chrome.runtime.connect chrome.runtime.sendMessage chrome.runtime.connect
popup-js - chrome.tabs.sendMessage chrome.tabs.connect - chrome.extension. getBackgroundPage()
background-js - chrome.tabs.sendMessage chrome.tabs.connect chrome.extension.getViews -
devtools-js chrome.devtools. inspectedWindow.eval - chrome.runtime.sendMessage chrome.runtime.sendMessage

7.2. 通信詳細(xì)介紹

popup和background

popup可以直接調(diào)用background中的JS方法,也可以直接訪(fǎng)問(wèn)background的DOM:

// background.js
function test()
{
	alert('我是background!');
}

// popup.js
var bg = chrome.extension.getBackgroundPage();
bg.test(); // 訪(fǎng)問(wèn)bg的函數(shù)
alert(bg.document.body.innerHTML); // 訪(fǎng)問(wèn)bg的DOM

小插曲,今天碰到一個(gè)情況,發(fā)現(xiàn)popup無(wú)法獲取background的任何方法,找了半天才發(fā)現(xiàn)是因?yàn)閎ackground的js報(bào)錯(cuò)了,而你如果不主動(dòng)查看background的js的話(huà),是看不到錯(cuò)誤信息的,特此提醒。

至于background訪(fǎng)問(wèn)popup如下(前提是popup已經(jīng)打開(kāi)):

var views = chrome.extension.getViews({type:'popup'});
if(views.length > 0) {
	console.log(views[0].location.href);
}

popup或者bg向content主動(dòng)發(fā)送消息

background.js或者popup.js:

function sendMessageToContentScript(message, callback)
{
	chrome.tabs.query({active: true, currentWindow: true}, function(tabs)
	{
		chrome.tabs.sendMessage(tabs[0].id, message, function(response)
		{
			if(callback) callback(response);
		});
	});
}
sendMessageToContentScript({cmd:'test', value:'你好,我是popup!'}, function(response)
{
	console.log('來(lái)自content的回復(fù):'+response);
});

content-script.js接收:

chrome.runtime.onMessage.addListener(function(request, sender, sendResponse)
{
	// console.log(sender.tab ?"from a content script:" + sender.tab.url :"from the extension");
	if(request.cmd == 'test') alert(request.value);
	sendResponse('我收到了你的消息!');
});

雙方通信直接發(fā)送的都是JSON對(duì)象,不是JSON字符串,所以無(wú)需解析,很方便(當(dāng)然也可以直接發(fā)送字符串)。

網(wǎng)上有些老代碼中用的是chrome.extension.onMessage,沒(méi)有完全查清二者的區(qū)別(貌似是別名),但是建議統(tǒng)一使用chrome.runtime.onMessage

content-script主動(dòng)發(fā)消息給后臺(tái)

content-script.js:

chrome.runtime.sendMessage({greeting: '你好,我是content-script呀,我主動(dòng)發(fā)消息給后臺(tái)!'}, function(response) {
	console.log('收到來(lái)自后臺(tái)的回復(fù):' + response);
});

background.js 或者 popup.js:

// 監(jiān)聽(tīng)來(lái)自content-script的消息
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse)
{
	console.log('收到來(lái)自content-script的消息:');
	console.log(request, sender, sendResponse);
	sendResponse('我是后臺(tái),我已收到你的消息:' + JSON.stringify(request));
});

注意事項(xiàng):

  • content_scripts向popup主動(dòng)發(fā)消息的前提是popup必須打開(kāi)!否則需要利用background作中轉(zhuǎn);
  • 如果background和popup同時(shí)監(jiān)聽(tīng),那么它們都可以同時(shí)收到消息,但是只有一個(gè)可以sendResponse,一個(gè)先發(fā)送了,那么另外一個(gè)再發(fā)送就無(wú)效;

injected script和content-script

content-script和頁(yè)面內(nèi)的腳本(injected-script自然也屬于頁(yè)面內(nèi)的腳本)之間唯一共享的東西就是頁(yè)面的DOM元素,有2種方法可以實(shí)現(xiàn)二者通訊:

  • 可以通過(guò)window.postMessagewindow.addEventListener來(lái)實(shí)現(xiàn)二者消息通訊;
  • 通過(guò)自定義DOM事件來(lái)實(shí)現(xiàn);

第一種方法(推薦):

injected-script中:

window.postMessage({"test": '你好!'}, '*');

content script中:

window.addEventListener("message", function(e)
{
	console.log(e.data);
}, false);

第二種方法:

injected-script中:

var customEvent = document.createEvent('Event');
customEvent.initEvent('myCustomEvent', true, true);
function fireCustomEvent(data) {
	hiddenDiv = document.getElementById('myCustomEventDiv');
	hiddenDiv.innerText = data
	hiddenDiv.dispatchEvent(customEvent);
}
fireCustomEvent('你好,我是普通JS!');

content-script.js中:

var hiddenDiv = document.getElementById('myCustomEventDiv');
if(!hiddenDiv) {
	hiddenDiv = document.createElement('div');
	hiddenDiv.style.display = 'none';
	document.body.appendChild(hiddenDiv);
}
hiddenDiv.addEventListener('myCustomEvent', function() {
	var eventData = document.getElementById('myCustomEventDiv').innerText;
	console.log('收到自定義事件消息:' + eventData);
});

長(zhǎng)連接和短連接

其實(shí)上面已經(jīng)涉及到了,這里再單獨(dú)說(shuō)明一下。Chrome插件中有2種通信方式,一個(gè)是短連接(chrome.tabs.sendMessagechrome.runtime.sendMessage),一個(gè)是長(zhǎng)連接(chrome.tabs.connectchrome.runtime.connect)。

短連接的話(huà)就是擠牙膏一樣,我發(fā)送一下,你收到了再回復(fù)一下,如果對(duì)方不回復(fù),你只能重新發(fā),而長(zhǎng)連接類(lèi)似WebSocket會(huì)一直建立連接,雙方可以隨時(shí)互發(fā)消息。

短連接上面已經(jīng)有代碼示例了,這里只講一下長(zhǎng)連接。

popup.js:

getCurrentTabId((tabId) => {
	var port = chrome.tabs.connect(tabId, {name: 'test-connect'});
	port.postMessage({question: '你是誰(shuí)啊?'});
	port.onMessage.addListener(function(msg) {
		alert('收到消息:'+msg.answer);
		if(msg.answer && msg.answer.startsWith('我是'))
		{
			port.postMessage({question: '哦,原來(lái)是你?。?});
		}
	});
});

content-script.js:

// 監(jiān)聽(tīng)長(zhǎng)連接
chrome.runtime.onConnect.addListener(function(port) {
	console.log(port);
	if(port.name == 'test-connect') {
		port.onMessage.addListener(function(msg) {
			console.log('收到長(zhǎng)連接消息:', msg);
			if(msg.question == '你是誰(shuí)?。?) port.postMessage({answer: '我是你爸!'});
		});
	}
});

其它補(bǔ)充

動(dòng)態(tài)注入或執(zhí)行JS

雖然在backgroundpopup中無(wú)法直接訪(fǎng)問(wèn)頁(yè)面DOM,但是可以通過(guò)chrome.tabs.executeScript來(lái)執(zhí)行腳本,從而實(shí)現(xiàn)訪(fǎng)問(wèn)web頁(yè)面的DOM(注意,這種方式也不能直接訪(fǎng)問(wèn)頁(yè)面JS)。

示例manifest.json配置:

{
	"name": "動(dòng)態(tài)JS注入演示",
	...
	"permissions": [
		"tabs", "http://*/*", "https://*/*"
	],
	...
}

JS:

// 動(dòng)態(tài)執(zhí)行JS代碼
chrome.tabs.executeScript(tabId, {code: 'document.body.style.backgroundColor="red"'});
// 動(dòng)態(tài)執(zhí)行JS文件
chrome.tabs.executeScript(tabId, {file: 'some-script.js'});

動(dòng)態(tài)注入CSS

示例manifest.json配置:

{
	"name": "動(dòng)態(tài)CSS注入演示",
	...
	"permissions": [
		"tabs", "http://*/*", "https://*/*"
	],
	...
}

JS代碼:

// 動(dòng)態(tài)執(zhí)行CSS代碼,TODO,這里有待驗(yàn)證
chrome.tabs.insertCSS(tabId, {code: 'xxx'});
// 動(dòng)態(tài)執(zhí)行CSS文件
chrome.tabs.insertCSS(tabId, {file: 'some-style.css'});

獲取當(dāng)前窗口ID

chrome.windows.getCurrent(function(currentWindow)
{
	console.log('當(dāng)前窗口ID:' + currentWindow.id);
});

獲取當(dāng)前標(biāo)簽頁(yè)ID

一般有2種方法:

// 獲取當(dāng)前選項(xiàng)卡ID
function getCurrentTabId(callback)
{
	chrome.tabs.query({active: true, currentWindow: true}, function(tabs)
	{
		if(callback) callback(tabs.length ? tabs[0].id: null);
	});
}

獲取當(dāng)前選項(xiàng)卡id的另一種方法,大部分時(shí)候都類(lèi)似,只有少部分時(shí)候會(huì)不一樣(例如當(dāng)窗口最小化時(shí))

// 獲取當(dāng)前選項(xiàng)卡ID
function getCurrentTabId2()
{
	chrome.windows.getCurrent(function(currentWindow)
	{
		chrome.tabs.query({active: true, windowId: currentWindow.id}, function(tabs)
		{
			if(callback) callback(tabs.length ? tabs[0].id: null);
		});
	});
}

本地存儲(chǔ)

本地存儲(chǔ)建議用chrome.storage而不是普通的localStorage,區(qū)別有好幾點(diǎn),個(gè)人認(rèn)為最重要的2點(diǎn)區(qū)別是:

  • chrome.storage是針對(duì)插件全局的,即使你在background中保存的數(shù)據(jù),在content-script也能獲取到;
  • chrome.storage.sync可以跟隨當(dāng)前登錄用戶(hù)自動(dòng)同步,這臺(tái)電腦修改的設(shè)置會(huì)自動(dòng)同步到其它電腦,很方便,如果沒(méi)有登錄或者未聯(lián)網(wǎng)則先保存到本地,等登錄了再同步至網(wǎng)絡(luò);

需要聲明storage權(quán)限,有chrome.storage.syncchrome.storage.local2種方式可供選擇,使用示例如下:

// 讀取數(shù)據(jù),第一個(gè)參數(shù)是指定要讀取的key以及設(shè)置默認(rèn)值
chrome.storage.sync.get({color: 'red', age: 18}, function(items) {
	console.log(items.color, items.age);
});
// 保存數(shù)據(jù)
chrome.storage.sync.set({color: 'blue'}, function() {
	console.log('保存成功!');
});

webRequest

通過(guò)webRequest系列API可以對(duì)HTTP請(qǐng)求進(jìn)行任性地修改、定制,這里通過(guò)beforeRequest來(lái)簡(jiǎn)單演示一下它的冰山一角:

//manifest.json
{
	// 權(quán)限申請(qǐng)
	"permissions":
	[
		"webRequest", // web請(qǐng)求
		"webRequestBlocking", // 阻塞式web請(qǐng)求
		"storage", // 插件本地存儲(chǔ)
		"http://*/*", // 可以通過(guò)executeScript或者insertCSS訪(fǎng)問(wèn)的網(wǎng)站
		"https://*/*" // 可以通過(guò)executeScript或者insertCSS訪(fǎng)問(wèn)的網(wǎng)站
	],
}


// background.js
// 是否顯示圖片
var showImage;
chrome.storage.sync.get({showImage: true}, function(items) {
	showImage = items.showImage;
});
// web請(qǐng)求監(jiān)聽(tīng),最后一個(gè)參數(shù)表示阻塞式,需單獨(dú)聲明權(quán)限:webRequestBlocking
chrome.webRequest.onBeforeRequest.addListener(details => {
	// cancel 表示取消本次請(qǐng)求
	if(!showImage && details.type == 'image') return {cancel: true};
	// 簡(jiǎn)單的音視頻檢測(cè)
	// 大部分網(wǎng)站視頻的type并不是media,且視頻做了防下載處理,所以這里僅僅是為了演示效果,無(wú)實(shí)際意義
	if(details.type == 'media') {
		chrome.notifications.create(null, {
			type: 'basic',
			iconUrl: 'img/icon.png',
			title: '檢測(cè)到音視頻',
			message: '音視頻地址:' + details.url,
		});
	}
}, {urls: ["<all_urls>"]}, ["blocking"]);

國(guó)際化

插件根目錄新建一個(gè)名為_locales的文件夾,再在下面新建一些語(yǔ)言的文件夾,如en、zh_CNzh_TW,然后再在每個(gè)文件夾放入一個(gè)messages.json,同時(shí)必須在清單文件中設(shè)置default_locale。

_locales\en\messages.json內(nèi)容:

{
	"pluginDesc": {"message": "A simple chrome extension demo"},
	"helloWorld": {"message": "Hello World!"}
}

_locales\zh_CN\messages.json內(nèi)容:

{
	"pluginDesc": {"message": "一個(gè)簡(jiǎn)單的Chrome插件demo"},
	"helloWorld": {"message": "你好啊,世界!"}
}

manifest.jsonCSS文件中通過(guò)__MSG_messagename__引入,如:

{
	"description": "__MSG_pluginDesc__",
	// 默認(rèn)語(yǔ)言
	"default_locale": "zh_CN",
}

JS中則直接chrome.i18n.getMessage("helloWorld")

測(cè)試時(shí),通過(guò)給chrome建立一個(gè)不同的快捷方式chrome.exe --lang=en來(lái)切換語(yǔ)言,如:

英文效果:

中文效果:

API總結(jié)

比較常用用的一些API系列:

  • chrome.tabs
  • chrome.runtime
  • chrome.webRequest
  • chrome.window
  • chrome.storage
  • chrome.contextMenus
  • chrome.devtools
  • chrome.extension

經(jīng)驗(yàn)總結(jié)

查看已安裝插件路徑

已安裝的插件源碼路徑:C:\Users\用戶(hù)名\AppData\Local\Google\Chrome\User Data\Default\Extensions,每一個(gè)插件被放在以插件ID為名的文件夾里面,想要學(xué)習(xí)某個(gè)插件的某個(gè)功能是如何實(shí)現(xiàn)的,看人家的源碼是最好的方法了:

如何查看某個(gè)插件的ID?進(jìn)入 chrome://extensions ,然后勾線(xiàn)開(kāi)發(fā)者模式即可看到了。

特別注意background的報(bào)錯(cuò)

很多時(shí)候你發(fā)現(xiàn)你的代碼會(huì)莫名其妙的失效,找來(lái)找去又找不到原因,這時(shí)打開(kāi)background的控制臺(tái)才發(fā)現(xiàn)原來(lái)某個(gè)地方寫(xiě)錯(cuò)了導(dǎo)致代碼沒(méi)生效,正式由于background報(bào)錯(cuò)的隱蔽性(需要主動(dòng)打開(kāi)對(duì)應(yīng)的控制臺(tái)才能看到錯(cuò)誤),所以特別注意這點(diǎn)。

如何讓popup頁(yè)面不關(guān)閉

在對(duì)popup頁(yè)面審查元素的時(shí)候popup會(huì)被強(qiáng)制打開(kāi)無(wú)法關(guān)閉,只有控制臺(tái)關(guān)閉了才可以關(guān)閉popup,原因很簡(jiǎn)單:如果popup關(guān)閉了控制臺(tái)就沒(méi)用了。這種方法在某些情況下很實(shí)用!

不支持內(nèi)聯(lián)JavaScript的執(zhí)行

也就是不支持將js直接寫(xiě)在html中,比如:

<input id="btn" type="button" value="收藏" onclick="test()"/>

報(bào)錯(cuò)如下:

Refused to execute inline event handler because it violates the following Content Security Policy directive: "script-src 'self' blob: filesystem: chrome-extension-resource:". Either the 'unsafe-inline' keyword, a hash ('sha256-...'), or a nonce ('nonce-...') is required to enable inline execution.

解決方法就是用JS綁定事件:

$('#btn').on('click', function(){alert('測(cè)試')});

另外,對(duì)于A標(biāo)簽,這樣寫(xiě)href="javascript:;" rel="external nofollow" rel="external nofollow" 然后用JS綁定事件雖然控制臺(tái)會(huì)報(bào)錯(cuò),但是不受影響,當(dāng)然強(qiáng)迫癥患者受不了的話(huà)只能寫(xiě)成href="#" rel="external nofollow" 了。

如果這樣寫(xiě):

<a href="javascript:;" rel="external nofollow"  rel="external nofollow"  id="get_secret">請(qǐng)求secret</a>

報(bào)錯(cuò)如下:

Refused to execute JavaScript URL because it violates the following Content Security Policy directive: "script-src 'self' blob: filesystem: chrome-extension-resource:". Either the 'unsafe-inline' keyword, a hash ('sha256-...'), or a nonce ('nonce-...') is required to enable inline execution.

注入CSS的時(shí)候必須小心

由于通過(guò)content_scripts注入的CSS優(yōu)先級(jí)非常高,幾乎僅次于瀏覽器默認(rèn)樣式,稍不注意可能就會(huì)影響一些網(wǎng)站的展示效果,所以盡量不要寫(xiě)一些影響全局的樣式。

之所以強(qiáng)調(diào)這個(gè),是因?yàn)檫@個(gè)帶來(lái)的問(wèn)題非常隱蔽,不太容易找到,可能你正在寫(xiě)某個(gè)網(wǎng)頁(yè),昨天樣式還是好好的,怎么今天就突然不行了?然后你辛辛苦苦找來(lái)找去,找了半天才發(fā)現(xiàn)竟然是因?yàn)椴寮锩娴囊粋€(gè)樣式影響的!

打包與發(fā)布

打包的話(huà)直接在插件管理頁(yè)有一個(gè)打包按鈕:

然后會(huì)生成一個(gè).crx文件,要發(fā)布到Google應(yīng)用商店的話(huà)需要先登錄你的Google賬號(hào),然后花5個(gè)$注冊(cè)為開(kāi)發(fā)者,本人太窮,就懶得親自驗(yàn)證了,有發(fā)布需求的自己去整吧。

參考

官方資料

推薦查看官方文檔,雖然是英文,但是全且新,國(guó)內(nèi)的中文資料都比較舊(注意以下全部需要翻墻):

Chrome插件官方文檔主頁(yè)

Chrome插件官方示例

manifest清單文件

permissions權(quán)限

chrome.xxx.api文檔模糊匹配規(guī)則語(yǔ)法詳解

第三方資料

部分中文資料,不是特別推薦:

360安全瀏覽器開(kāi)發(fā)文檔

360極速瀏覽器

Chrome擴(kuò)展開(kāi)發(fā)文檔

Chrome擴(kuò)展開(kāi)發(fā)極客系列博客

附圖

附圖:Chrome高清png格式logo:

以上就是Chrome插件(擴(kuò)展)開(kāi)發(fā)全攻略的詳細(xì)內(nèi)容,更多關(guān)于Chrome插件開(kāi)發(fā)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論