国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

Chrome 插件開發指南

原創 精選
系統 瀏覽器 開發
開發插件需要使用前端技術:html css javascript。本文就從入門開始講述如何開發一款 chrome 插件。
豐富的 chrome 插件極大的提升我們的工作效率和辛福感,比如大名鼎鼎的 adblock 廣告屏蔽、GoFullPage 網頁長截圖、evernote web clipper 收藏網頁。

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

開發插件需要使用前端技術:html css javascript。

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

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

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

插件構成

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

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

background script:可以調用全部的 chrome 插件 API,實現跨域請求、網頁截屏、彈出 chrome 通知消息等功能。相當于在一個隱藏的瀏覽器頁面內默默運行。

功能頁面:包括點擊插件圖標彈出的頁面(簡稱 popup)、插件的配置頁面(簡稱 options)。

content script:早期也被稱為 injected script,是插件注入到頁面的腳本,但是不會體現在頁面 DOM 結構里。content script 可以操作 DOM,但是它和頁面其他的腳本是隔離的,訪問不到其他腳本定義的變量、函數等,相當于運行在單獨的沙盒里。content script 可以調用有限的 chrome 插件 API,網絡請求收到同源策略限制。

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

重點說明以下幾點:

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

圖片

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

圖片

popup 無法通過程序打開,只能由用戶點擊打開。點擊 popup 之外的區域會導致 popup 收起。

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

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

圖片

chrome 可以打開多個瀏覽器窗口,而一個窗口會有多個 tab,所以插件的結構大致如下:

圖片

如上圖,功能頁面是每個 window 一份,但是每個 tab 都會注入 content script。

manifest.json

下文簡稱 manifest ,其中有這么幾個字段可以重點說明:

content_scripts

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

聲明式注入

舉例如下:

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

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

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

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

所以需要在用戶點擊插件圖標時,探測頁面中的 content_scripts 是否存在(發送消息是否有響應/出錯),再提示用戶刷新頁面。

程序注入

還可以使用程序動態注入腳本,代碼如下:

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

比如用戶點擊插件圖標時執行注入腳本,則無需刷新頁面,代碼如下:

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

值得注意的是,采用以上方式,用戶每次點擊插件圖標時,content.js 都會被執行,可能會引起錯誤。

// content.js
let loaded = false;

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

console.log(loaded);

第一次執行 content.js 會打印 false,而第二次執行 content.js 則會報錯,提示 loaded 變量已經聲明了。

由此可見 content.js 的執行會影響其所在的沙盒。

我們可以這么做:

// content.js

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

console.log(window.contentLoaded);

使用沙盒內的全局變量則可以避免 content.js 重復執行帶來的問題。

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

permissions

該字段是一個字符串數組,用來聲明插件需要的權限,這樣才能調用某些 chrome API,常見的有:

  1. tabs
  2. activeTab
  3. contextMenus:網頁右鍵菜單,browser_action 右鍵菜單
  4. cookies:操作 cookie,和用戶登錄態相關的功能可能會用到該權限
  5. storage:插件存儲,不是 localStorage
  6. web_accessible_resources:網頁能訪問的插件內部資源,比如插件提供 SDK 給頁面使用,如 ethereum 的 metamask 錢包插件。或者是修改 DOM 結構用到了插件的樣式、圖片、字體等資源。

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

background script

下文簡稱 background,可以理解它是在一個隱藏的 tab 中執行,所在的頁面域名為空,這會影響對 document.cookie 的使用。

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

當發送網絡請求時,瀏覽器會自動帶上 a.com 的 cookie,服務器的 set-cookie 也會對瀏覽器生效。這是符合預期的。

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

background 設置 document.cookie 時,不能指定域名,否則會設置失敗。比如:

// 會失敗,因為指定的域名和 background 所在的域名不符
document.cookie = `session=xxxxxxx; domain=a.com; max-age=9999999999; path=/`;

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

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

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

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

// 獲取活躍的 tab,通常是用戶正在瀏覽的頁面
async function getActiveTab(): Promise<chrome.tabs.Tab | null> {
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<number | undefined> {
if (typeof tabId === "undefined") {
return tabId;
}

// firefox 不支持 selected 參數
// 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<number> {
const pattern = getUrlPattern(url);
return new Promise<number>((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

下文簡稱 content,它只能使用有限的 chrome API。

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

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

比如頁面內容如下:

<!-- index.html -->
<html>
<body>
<div id="app"></div>
<button id="btn" type="button">submit</button>
</body>
<script>
window.globalData = {
userId: 12345,
};
</script>
</html>

content 內容如下:

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

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

資源注入

content 可以向頁面中注入 <script>,由此給頁面提供 SDK 等功能,注入的腳本和頁面自己的腳本一樣,都無法和 content 直接通信。

注意:注入的資源要先在 menifest 的 web_accessible_resources 字段中聲明。

// content 內容
const script = document.createElement("script");
script.src = chrome.runtime.getURL("sdk.js");
document.body.appendChild(script);
// sdk.js
window.jsbridge = {
version: "1.0.1",
// ...
};

content 執行之后,可以看到頁面結構多了個 <script src="chrome-extension://xxxxxxxxxxxxx/sdk.js"></script>,xxxxxxxx 表示插件的 id,由 chrome 生成。

注意,注入的 sdk.js 腳本是可以被頁面內其他腳本訪問到的(可以看作是頁面自己的腳本,只是 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 的剪輯插件。

圖片

前面提到過,點擊 popup 之外的區域會導致 popup 收起,操作 DOM 會導致 popup 隱藏,而 popup 無法用代碼主動打開,所以 evernote 的剪輯插件的 UI 就無法用 popup 來實現了。

這時候可以把 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 發送消息時,不僅需要指定所在 tab 的 id,還需要指定 iframe 的 id。這里說的 iframe id 類似 tab id,是 chrome 分配的,而不是 iframe 標簽的 id 屬性。

功能頁面

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

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

options 用來設置插件,所以一般需要調用 chrome.storage 存儲配置。

適配其他瀏覽器

目前 chrome 插件適配工作量是比較小的,因為 edge、opera 都已經切換到 chromium 內核,firefox 也支持 chrome API。

不過需要查看用到的 API 是否支持,以及 API 的入參、出參是否一致。比如前文提到 firefox chrome.tabs.update 方法第一個參數不支持 selected 屬性。

firefox 還支持 browser API,和 chrome API 不同的是 browser API 不使用回調函數,而是返回 promise。比如:

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

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

可以參考各瀏覽器的開發文檔:

  • 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.360.cn/open/extension_dev/overview.html
  • 搜狗: http://ie.sogou.com/open/doc/

發布

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

總結

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

責任編輯:未麗燕 來源: 字節跳動技術團隊
相關推薦

2012-01-04 16:21:11

2014-08-01 09:57:52

Node.jsNode.js插件

2011-07-25 16:21:22

Sencha touc

2023-11-20 09:33:43

開發指南

2023-08-17 10:20:18

RabbitMQ系統

2009-06-24 16:30:21

JSF組件模型

2012-03-26 09:27:40

谷歌安卓開發谷歌安卓

2011-06-09 18:24:36

QT Wince

2023-05-15 18:44:07

前端開發

2012-05-18 10:08:56

TitaniumAndroid

2011-04-18 11:00:34

使用音頻BlackBerry

2021-08-09 09:47:34

Blazor 路由開發

2011-12-29 10:48:49

移動Web

2021-06-21 15:21:52

鴻蒙HarmonyOS應用開發

2015-11-12 16:14:52

Python開發實踐

2019-10-31 08:00:00

機器學習人工智能AI

2015-12-16 10:30:18

前端開發指南

2010-06-13 09:27:56

Widget開發

2025-03-11 14:45:31

2014-08-26 10:01:18

Windows Pho平臺開發指南
點贊
收藏

51CTO技術棧公眾號

欧美国产第一页| 欧美成人sm免费视频| 色综合av综合无码综合网站| 日韩中文字幕亚洲一区二区va在线| 欧亚精品中文字幕| 久久不卡日韩美女| 国产偷亚洲偷欧美偷精品| 在线午夜影院| 日韩欧美一二区| 中文字幕在线免费| 在线国产亚洲欧美| 男女视频在线观看免费| 亚洲男人的天堂av| 天天爽天天爽夜夜爽| 久久人人超碰精品| 无码精品国产一区二区三区免费| 东方欧美亚洲色图在线| 妺妺窝人体色www看人体| 国内精品免费在线观看| 偷拍自拍在线视频| 成人污版视频| 国产精品视频| 91精品国产色综合久久不卡电影| 999www人成免费视频| 成人黄页毛片网站| 91亚洲精品国产| 日韩中文字幕1| 91精品久久久久久久久青青| av成人资源| 久久久久久久久91| 成人动漫视频在线观看完整版| 日韩三级网址| 久久成人免费视频| 精品久久福利| 久久精品在线视频| 日韩欧美高清一区二区三区| 色综合久久悠悠| 老司机aⅴ在线精品导航| 欧美亚洲第一页| 国产欧美高清视频在线| 国产成人精品在线观看| 欧美精品一二| av免费观看久久| 天堂蜜桃一区二区三区| 懂色av粉嫩av蜜臀av| 99国产精品视频免费观看| 欧美成人黄色网址| 亚洲图片欧美综合| 欧美午夜电影一区二区三区| 亚洲精品aⅴ中文字幕乱码 | 国模精品视频| 尤物九九久久国产精品的分类| 国产精品欧美一区二区三区不卡| 久久免费在线观看| 午夜亚洲福利| 这里只有精品66| 日韩一区欧美一区| 成人午夜电影在线观看| 精品亚洲aⅴ在线观看| 国产精品网址| 久久国产一区二区| 91视频在线观看免费| 亚洲精品视频区| 亚洲性生活视频| 91免费精品| 青草全福视在线| 婷婷成人激情在线网| 超碰一区二区| 国产精品国产三级国产aⅴ9色 | 丝袜诱惑制服诱惑色一区在线观看| 久久久久久www| 精品成人乱色一区二区| 欧美极品免费| 成人在线视频网| 高清不卡一二三区| 免费福利在线观看| 欧美成人精品在线播放| 亚洲国产影院| 国产九九热视频| 日韩av综合网| 天天做天天爱天天综合网| 成人av在线不卡| 欧美日韩国产成人在线91| 精品亚洲二区| 免费av在线一区二区| 国产精品卡一卡二| 男女在线视频| 国产精品中文字幕久久久| 国产成人精品免费一区二区| 国产精品99999| 日韩av快播网址| 不卡av免费在线观看| 午夜看片在线免费| 欧美激情小视频| 国产成人在线观看| 九义人在线观看完整免费版电视剧| 日本国产一区二区三区| 懂色av一区二区三区免费看| 国产免费视频在线| 91麻豆文化传媒在线观看| 一级二级在线观看| 亚州av一区二区| 99久久精品国产一区| 欧美极品视频| 国产在线精品一区免费香蕉 | 粉嫩av一区二区三区天美传媒 | 欧美专区在线| 在线手机福利影院| 欧美激情视频在线免费观看 欧美视频免费一 | 久久精品.com| 日韩av在线高清| 在线高清一区| 精品欧美不卡一区二区在线观看| 欧洲亚洲在线视频| 亚洲国产精品国自产拍av| 国产一区二区三区影视| 少妇熟女一区二区| 精品国产乱码久久| 蜜臀精品久久久久久蜜臀 | 色综合天天综合色综合av| 九九热精品视频在线观看| 性生活免费在线观看| 欧美成人精品在线| 91首页免费视频| 国产成人久久精品一区二区三区| 黄色a级片免费看| 在线播放亚洲激情| 不卡的看片网站| 成人短视频软件网站大全app| 国产一区二区三区小说| 亚洲三级av在线| 91女厕偷拍女厕偷拍高清| 日本少妇精品亚洲第一区| 天天操天天爱天天爽| 97国产精品免费视频| 综合在线观看色| 全球成人免费直播| 毛片免费在线| 日本精品一区二区| 亚洲图片在区色| 欧美国产激情一区二区三区蜜月| 日韩精品免费一区二区夜夜嗨 | 成人中文字幕+乱码+中文字幕| 欧美日韩国产色视频| 在线成人h网| 牛牛电影国产一区二区| 400部精品国偷自产在线观看| 中国日韩欧美久久久久久久久| 久久精品免费在线观看| 曰本一区二区三区视频| ga∨成人网| 国产一级特黄a大片99| 精品卡一卡二卡三卡四在线| 国产成人在线网站| 麻豆视频久久| 一区二区三区区四区播放视频在线观看 | 羞羞小视频视频| 成人乱人伦精品视频在线观看| 欧美午夜在线一二页| 日韩不卡手机在线v区| 成人国产一区| 人人在线97| 欧美综合激情| 欧美精品亚州精品| 欧美日韩免费在线| 毛片av一区二区| 中文字幕区一区二区三| 青青青手机在线视频观看| 一区二区视频在线观看| 97色在线播放视频| 欧美一区二区在线观看| 91香蕉视频mp4| 欧美电影《睫毛膏》| 超碰在线cao| 8x8x视频在线| 日韩免费一区二区三区| 欧美另类99xxxxx| 狠狠色狠狠色综合日日五| 精品中文字幕一区二区小辣椒| 蜜桃久久久久| 欧美性video| 精产国产伦理一二三区| 亚洲国产精品综合| 欧美综合激情网| 精品国产髙清在线看国产毛片| 久久女同性恋中文字幕| 影音先锋在线一区| 国产成人av毛片| 免费网站在线观看人| 好紧好硬好湿我太爽了| 国产高清免费在线| 成人女保姆的销魂服务| 久久久黄色av| 欧美一区中文字幕| 一区二区在线免费观看| 国产成人av电影在线播放| 黄色成人在线网站| 亚洲精品aaaaa| 素人啪啪色综合| gogogogo高清视频在线|