日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
Chrome插件開發(fā)指南

豐富的 chrome 插件極大的提升我們的工作效率和辛福感,比如大名鼎鼎的 adblock 廣告屏蔽、GoFullPage 網(wǎng)頁長截圖、evernote web clipper 收藏網(wǎng)頁。

一般來說,插件的原理是向頁面中注入 javascript 腳本,對(duì)頁面進(jìn)行處理,比如屏蔽頁面中可能的廣告元素,改變某些元素的樣式,增加一些 UI。

開發(fā)插件需要使用前端技術(shù):html css javascript。

本文就從入門開始講述如何開發(fā)一款 chrome 插件。

注意:chrome 插件機(jī)制本身也在更新,本文講述的是目前普遍使用的 V2 插件的開發(fā)。

Manifest V3 is available beginning with Chrome 88, and the Chrome Web Store begins accepting MV3 extensions in January 2021.

插件構(gòu)成

chrome 插件通常由以下幾部分組成:

manifest.json:相當(dāng)于插件的 meta 信息,包含插件的名稱、版本號(hào)、圖標(biāo)、腳本文件名稱等,這個(gè)文件是每個(gè)插件都必須提供的,其他幾部分都是可選的。

background script:可以調(diào)用全部的 chrome 插件 API,實(shí)現(xiàn)跨域請(qǐng)求、網(wǎng)頁截屏、彈出 chrome 通知消息等功能。相當(dāng)于在一個(gè)隱藏的瀏覽器頁面內(nèi)默默運(yùn)行。

功能頁面:包括點(diǎn)擊插件圖標(biāo)彈出的頁面(簡(jiǎn)稱 popup)、插件的配置頁面(簡(jiǎn)稱 options)。

content script:早期也被稱為 injected script,是插件注入到頁面的腳本,但是不會(huì)體現(xiàn)在頁面 DOM 結(jié)構(gòu)里。content script 可以操作 DOM,但是它和頁面其他的腳本是隔離的,訪問不到其他腳本定義的變量、函數(shù)等,相當(dāng)于運(yùn)行在單獨(dú)的沙盒里。content script 可以調(diào)用有限的 chrome 插件 API,網(wǎng)絡(luò)請(qǐng)求收到同源策略限制。

插件的架構(gòu)可以參考:https://developer.chrome.com/docs/extensions/mv2/architecture-overview/

重點(diǎn)說明以下幾點(diǎn):

  1. browser action 和 page action:這倆我們可以理解為插件的按鈕。browser action 會(huì)固定在 chrome 的工具欄。而 page action 可以設(shè)置特定的網(wǎng)頁才顯示圖標(biāo),在地址欄的右端,如下圖:

大部分插件點(diǎn)擊之后會(huì)顯示 UI,也就是上文描述的插件功能頁面部分,一般稱為 popup 頁面,如下圖:

popup 無法通過程序打開,只能由用戶點(diǎn)擊打開。點(diǎn)擊 popup 之外的區(qū)域會(huì)導(dǎo)致 popup 收起。

page action 和 browser action 分別由 manifest.json 的 page_action 和 browser_action 字段配置。

  1. 由于 content script 受到同源策略的限制,所以一般網(wǎng)絡(luò)請(qǐng)求都交給 background script 處理。
  2. content script、插件功能頁面、background script 之間的通信架構(gòu)如下圖:

chrome 可以打開多個(gè)瀏覽器窗口,而一個(gè)窗口會(huì)有多個(gè) tab,所以插件的結(jié)構(gòu)大致如下:

如上圖,功能頁面是每個(gè) window 一份,但是每個(gè) tab 都會(huì)注入 content script。

manifest.json

下文簡(jiǎn)稱 manifest ,其中有這么幾個(gè)字段可以重點(diǎn)說明:

content_scripts

content_scripts 可以使用以下兩種方式注入頁面,這兩種方式并不沖突,可以結(jié)合使用。

聲明式注入

舉例如下:

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

在 manifest 中聲明要加載的腳本,各個(gè)字段都比較直觀。其中:

  1. matches 表示頁面 url 匹配時(shí)才加載
  2. run_at? 表示在什么時(shí)機(jī)加載,一般是 document_idle,避免 content_scripts 影響頁面加載性能。

需要注意的是,如果用戶已經(jīng)打開了 N 個(gè)頁面,然后再安裝插件,這 N 個(gè)頁面除非重新刷新,否則是不會(huì)加載 manifest 聲明的 content_scripts。安裝插件之后新打開的頁面是可以加載 content_scripts 的。

所以需要在用戶點(diǎn)擊插件圖標(biāo)時(shí),探測(cè)頁面中的 content_scripts 是否存在(發(fā)送消息是否有響應(yīng)/出錯(cuò)),再提示用戶刷新頁面。

程序注入

還可以使用程序動(dòng)態(tài)注入腳本,代碼如下:

chrome.tabs.executeScript({
file: "content.js",
});

比如用戶點(diǎn)擊插件圖標(biāo)時(shí)執(zhí)行注入腳本,則無需刷新頁面,代碼如下:

// 監(jiān)聽插件圖標(biāo)點(diǎn)擊事件
chrome.browserAction.onClicked.addListener(() => {
chrome.tabs.executeScript({
file: 'content.js',
});
});

值得注意的是,采用以上方式,用戶每次點(diǎn)擊插件圖標(biāo)時(shí),content.js 都會(huì)被執(zhí)行,可能會(huì)引起錯(cuò)誤。

// content.js
let loaded = false;

if (!loaded) {
// do something
loaded = true;
}

console.log(loaded);

第一次執(zhí)行 content.js 會(huì)打印 false,而第二次執(zhí)行 content.js 則會(huì)報(bào)錯(cuò),提示 loaded 變量已經(jīng)聲明了。

由此可見 content.js 的執(zhí)行會(huì)影響其所在的沙盒。

我們可以這么做:

// content.js

if (!window.contentLoaded) {
// do something
window.contentLoaded = true;
}

console.log(window.contentLoaded);

使用沙盒內(nèi)的全局變量則可以避免 content.js 重復(fù)執(zhí)行帶來的問題。

綜上所述:聲明式只會(huì)注入一次,缺點(diǎn)是可能需要刷新頁面。程序式不需要刷新頁面,缺點(diǎn)是可能會(huì)注入多次。

permissions

該字段是一個(gè)字符串?dāng)?shù)組,用來聲明插件需要的權(quán)限,這樣才能調(diào)用某些 chrome API,常見的有:

  1. tabs
  2. activeTab
  3. contextMenus:網(wǎng)頁右鍵菜單,browser_action 右鍵菜單
  4. cookies:操作 cookie,和用戶登錄態(tài)相關(guān)的功能可能會(huì)用到該權(quán)限
  5. storage:插件存儲(chǔ),不是 localStorage
  6. web_accessible_resources:網(wǎng)頁能訪問的插件內(nèi)部資源,比如插件提供 SDK 給頁面使用,如 ethereum 的 metamask 錢包插件。或者是修改 DOM 結(jié)構(gòu)用到了插件的樣式、圖片、字體等資源。

permissions 中還可以聲明多個(gè) url patterns,表示插件需要訪問這些 url,比如和 API 通信。

background script

下文簡(jiǎn)稱 background,可以理解它是在一個(gè)隱藏的 tab 中執(zhí)行,所在的頁面域名為空,這會(huì)影響對(duì) document.cookie 的使用。

比如 background 需要和 a.com 通信。首先應(yīng)該把 *://*.a.com/* 加入到 manifest 的 permissions 數(shù)組中。

當(dāng)發(fā)送網(wǎng)絡(luò)請(qǐng)求時(shí),瀏覽器會(huì)自動(dòng)帶上 a.com 的 cookie,服務(wù)器的 set-cookie 也會(huì)對(duì)瀏覽器生效。這是符合預(yù)期的。

但是讀取 document.cookie 時(shí),由于 background 所在的域名為空,a.com 被認(rèn)為是第三方 cookie,會(huì)讀取不到。所以需要使用 chrome.cookies API 來讀取 cookie。

background 設(shè)置 document.cookie 時(shí),不能指定域名,否則會(huì)設(shè)置失敗。比如:

// 會(huì)失敗,因?yàn)橹付ǖ挠蛎?background 所在的域名不符
document.cookie = `session=xxxxxxx; domain=a.com; max-age=9999999999; path=/`;

// 正確的做法,不要指定域名
document.cookie = `session=xxxxxxx; max-age=9999999999`;

一般不需要這么操作 cookie,但是可能依賴的 npm 包會(huì)操作 document.cookie,所以這里說明一下。

background 使用 tabs 接口操作瀏覽器的 tab 窗口,比如:

// 打開新 tab
async function open(url: string): Promise {
return new Promise((resolve) => {
chrome.tabs.create(
{
url,
},
(tab) => resolve(tab.id!)
);
});
}

// 獲取活躍的 tab,通常是用戶正在瀏覽的頁面
async function getActiveTab(): Promise {
return new Promise((resolve) => {
chrome.tabs.query(
{
active: true,
currentWindow: true,
},
(tabs) => {
if (tabs.length > 0) {
resolve(tabs[0]);
} else {
resolve(null);
}
}
);
});
}

// 將指定的 tab 變成活躍的
async function activate(
tabId?: number,
url?: string
): Promise {
if (typeof tabId === "undefined") {
return tabId;
}

// firefox 不支持 selected 參數(shù)
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs/update#parameters
const options: chrome.tabs.UpdateProperties = IS_FIREFOX
? { active: true }
: { selected: true };
if (url) {
options.url = url;
}

return new Promise((resolve) => {
chrome.tabs.update(tabId, options, () => resolve(tabId));
});
}

// 打開新窗口,或者是激活窗口
async function openOrActivate(url: string): Promise {
const pattern = getUrlPattern(url);
return new Promise((resolve) => {
chrome.tabs.query(
{
url: pattern,
},
(tabs) => {
if (tabs.length > 0 && tabs[0].id) {
return Tabs.activate(tabs[0].id);
} else {
this.open(url).then((id) => resolve(id));
}
}
);
});
}

content scripts

下文簡(jiǎn)稱 content,它只能使用有限的 chrome API。

由于 content 可以訪問 DOM,可以用它來選擇、修改、刪除、增加網(wǎng)頁元素。

但是 content 是運(yùn)行在隔離的空間(類似沙盒),所以如果需要和頁面的其他腳本通信,需要采用 window.postMessage 的方式。

比如頁面內(nèi)容如下:








content 內(nèi)容如下:

// 成功
document.getElementById("app").innerHTML = "hello chrome";

// window.globalData 是 undefined
console.log(window.globalData);

資源注入

content 可以向頁面中注入 ,xxxxxxxx 表示插件的 id,由 chrome 生成。

注意,注入的 sdk.js 腳本是可以被頁面內(nèi)其他腳本訪問到的(可以看作是頁面自己的腳本,只是 origin 是 chrome-extensions://xxxxxxxxxxxxx),如下:

document.getElementById("btn").addEventListener(
"click",
() => {
console.log(window.jsbridge.version);
},
false
);

通信

content 可以和 background、popup、options 使用 chrome API 通信,參考官方文檔:https://developer.chrome.com/docs/extensions/mv2/background_pages/

常用的通信 API 是 chrome.runtime.sendMessage。

UI

content 可以向頁面中注入 UI,比如 evernote 的剪輯插件。

前面提到過,點(diǎn)擊 popup 之外的區(qū)域會(huì)導(dǎo)致 popup 收起,操作 DOM 會(huì)導(dǎo)致 popup 隱藏,而 popup 無法用代碼主動(dòng)打開,所以 evernote 的剪輯插件的 UI 就無法用 popup 來實(shí)現(xiàn)了。

這時(shí)候可以把 UI 作為 iframe 插入頁面,比如:

// content
const app = document.createElement("iframe");
app.src = chrome.runtime.getURL("app.html");
document.body.appendChild(app);

神奇的是 iframe 里的 javascript 是可以像 content 一樣和 background 通信的。

background 給 iframe 發(fā)送消息時(shí),不僅需要指定所在 tab 的 id,還需要指定 iframe 的 id。這里說的 iframe id 類似 tab id,是 chrome 分配的,而不是 iframe 標(biāo)簽的 id 屬性。

功能頁面

popup/options 和 background 的關(guān)系很親密,它們甚至可以通過 chrome.extension.getBackgroundPage()? 獲取到 background 的全局變量。所以它們直接的通信花樣很多,不過一般也是用 chrome.runtime 通信。

popup/options 和 content 之間的通信方式,可以 background -> content 通信類似。

options 用來設(shè)置插件,所以一般需要調(diào)用 chrome.storage 存儲(chǔ)配置。

適配其他瀏覽器

目前 chrome 插件適配工作量是比較小的,因?yàn)?edge、opera 都已經(jīng)切換到 chromium 內(nèi)核,firefox 也支持 chrome API。

不過需要查看用到的 API 是否支持,以及 API 的入?yún)ⅰ⒊鰠⑹欠褚恢?。比如前文提?firefox chrome.tabs.update 方法第一個(gè)參數(shù)不支持 selected 屬性。

firefox 還支持 browser API,和 chrome API 不同的是 browser API 不使用回調(diào)函數(shù),而是返回 promise。比如:

browser.tabs.query({ currentWindow: true }).then((res) => console.log(res));

chrome.tabs.query({ currentWindow: true }, (res) => {
console.log(res);
});

可以參考各瀏覽器的開發(fā)文檔:

  • firefox: https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Build_a_cross_browser_extension
  • edge: https://docs.microsoft.com/zh-cn/microsoft-edge/extensions-chromium/developer-guide/port-chrome-extension
  • 360: http://open.se.#/open/extension_dev/overview.html
  • 搜狗: http://ie.sogou.com/open/doc/

發(fā)布

  • chrome 發(fā)布插件需要花費(fèi) 5 美元開通賬號(hào):https://developer.chrome.com/docs/webstore/register/
  • firefox 發(fā)布文檔:https://addons.mozilla.org/en-US/developers/
  • edge:https://docs.microsoft.com/zh-cn/microsoft-edge/extensions-chromium/publish/create-dev-account

總結(jié)

總體來說,chrome 插件開發(fā)對(duì)前端工程師來說還是比較容易的。


名稱欄目:Chrome插件開發(fā)指南
鏈接URL:http://www.5511xx.com/article/dhsedpo.html