chrome V3插件入門(mén)到放棄,Plasmo不完全使用指南
沒(méi)有插件的瀏覽器是沒(méi)有靈魂的。今天來(lái)近距離感受一下chrome的靈魂
開(kāi)始之前了解一下靈魂chrome插件的版本。
Chrome 瀏覽器從88版本開(kāi)始支持MV3啦(即Manifest Version 3),現(xiàn)在瀏覽器版本都100+了。而MV2(即Manifest Version 2)將會(huì)在2023年 退休
。所以今天要講的就是MV3版本
后續(xù)的文章中,因?yàn)槲覜](méi)有魔法,所以貼出來(lái)的文檔地址都是國(guó)內(nèi)可以訪問(wèn)的文檔(有條件的同學(xué)可以直接看谷歌的原文檔 https://developer.chrome.com/docs/extensions/mv3/)
版本變更的變動(dòng)
manifest.json 作為插件的配置清單最能體現(xiàn)相關(guān)的變動(dòng)了 從manifest.json 參考文檔 可以很清楚地看到配置升級(jí)其實(shí)主要加了2個(gè) 「action」和 「host_permissions」
比較小的變動(dòng)
Host Permissions
在V2中,有兩種方法為你的api或任何主機(jī)獲得權(quán)限,要么在 permissions 數(shù)組或 optional_permissions 數(shù)組。
{
"permissions": ["https://xxxx.com/*"]
}
在V3中,所有主機(jī)權(quán)限現(xiàn)在都單獨(dú)存在一個(gè)新數(shù)組中,該數(shù)組的鍵為 host_permissions。主機(jī)權(quán)限不再與其他權(quán)限一起添加。
{
"host_permissions": ["https://xxx.com/*"]
}
Actions
在V2中,分為 browser_action
和 page_action
。
-
browser_action
更多是負(fù)責(zé)插件的icon的切換等操作。參考文檔: API-browserAction - 而
page_action
更多是針對(duì)某個(gè)頁(yè)面進(jìn)行地址欄的操作 參考文檔:API-pageAction
感興趣的可以在MDN插件開(kāi)發(fā)文檔里面看一看。
在V3中,都統(tǒng)一合并為 action
。參考文檔:API-action
content_security_policy 變動(dòng)
在V2的manifest.json 的 content_security_policy
配置是一個(gè)字符串類(lèi)型。升級(jí)到 V3 后變成了一個(gè)對(duì)象類(lèi)型。詳細(xì)的變更看文檔會(huì)更加清晰:content_security_policy 參考文檔
web_accessible_resources
詳細(xì)變更參考 web_accessible_resources
// v2 寫(xiě)法
{
"web_accessible_resources": ["images/my-image.png"]
}
// v3 寫(xiě)法
{
// …
"web_accessible_resources": [
{
"resources": [ "test1.png", "test2.png" ],
"matches": [ "https://web-accessible-resources-1.glitch.me/*" ]
}, {
"resources": [ "test3.png", "test4.png" ],
"matches": [ "https://web-accessible-resources-2.glitch.me/*" ],
"use_dynamic_url": true
}
],
// …
}
不在允許遠(yuǎn)程托管代碼
以前一些功能可以依賴(lài)于網(wǎng)絡(luò)請(qǐng)求動(dòng)態(tài)加載,V3 則不允許這樣的操作了
- 不再支持加載遠(yuǎn)程托管的代碼主要出于兩個(gè)原因:
- 安全因素,遠(yuǎn)程代碼總是有不安全因素存在
- Chrome 在審核提交的插件時(shí)更可靠,更高效,不需要再去關(guān)注遠(yuǎn)程代碼,只需要審核包內(nèi)的代碼即可。
只能把以前通過(guò)鏈接加載的js下載到插件包中,改改資源引入就好~
Promises
V3 現(xiàn)在原生支持 Promise。許多常用 API 現(xiàn)在都支出,最終所有合適的 API 都會(huì)支持 Promise。
如果使用 callback,就不會(huì)返回 Promise,優(yōu)先執(zhí)行 callback。
較大的變動(dòng)
將 Background Scripts 改造成 Service Workers
在V2中,Background是可以通過(guò) persistent
配置來(lái)確保頁(yè)面時(shí)候需要 持久化
。而且還能支持 .html
"background": {
"scripts": ["background-script.js"],
"persistent": false
}
// 或
"background": {
"page": "background-page.html",
"persistent": false
}
很多小技巧都依賴(lài)于 html 這特性,把數(shù)據(jù)掛載在 background 的 window
對(duì)象上進(jìn)行數(shù)據(jù)中轉(zhuǎn)
V3 則是強(qiáng)制使用了 Service Workers,禁止了持久化。background只能使用js文件
"background": { "scripts": ["background.js"] },
網(wǎng)絡(luò)攔截,使用新的declarativeNetRequest來(lái)修改請(qǐng)求
這個(gè)變動(dòng)非常的大,在本文后面詳細(xì)講這一塊的內(nèi)容。而且 MDN 文檔還沒(méi)更新 declarativeNetRequest 相關(guān)的內(nèi)容,等下要找個(gè)新文檔來(lái)看
棄用的API
chrome.extension.getExtensionTabs()
chrome.extension.getURL()
chrome.extension.lastError
chrome.extension.onRequest
chrome.extension.onRequestExternal
chrome.extension.sendRequest()
chrome.tabs.getAllInWindow()
chrome.tabs.getSelected()
chrome.tabs.onActiveChanged
chrome.tabs.onHighlightChanged
chrome.tabs.onSelectionChanged
chrome.tabs.sendRequest()
chrome.tabs.selected
在查看 MDN 文檔時(shí)會(huì)有相關(guān)的提示
使用chrome官方文檔時(shí)的提示
查閱官方文檔時(shí),那些標(biāo)簽也能幫助到我們。
Promise
標(biāo)簽:支持 Promise<=MV2
標(biāo)簽:該API僅在V2前支持>=MV3標(biāo)簽
:該API在V3后支持Deprecated
標(biāo)簽:已廢棄的 API
使用 Plasmo 開(kāi)發(fā)
都2022年了,或許每次開(kāi)發(fā)一些新東西的時(shí)候你都會(huì)在想:
- 我要用什么技術(shù)棧?Vue3/React?
- 開(kāi)發(fā)插件,能不能用npm上的包啊,如果能的話(huà)我是不是還得配webpack或者vite?
- webpack的話(huà),我是不是得配置多個(gè)入口和出口才能滿(mǎn)足插件的入口要求
好麻煩啊,上github找找有沒(méi)有現(xiàn)成的。
好像都還可以,收藏吃灰,下次在開(kāi)發(fā)把
不要下次了!就這次把,強(qiáng)烈推薦 Plasmo
官網(wǎng): plasmo
github: PlasmoHQ/plasmo
官方自己的介紹(說(shuō)的非常的樸素,我一個(gè)路人都覺(jué)得這功能寫(xiě)少了)
作為一個(gè)過(guò)來(lái)人的感受,我只能說(shuō)用 Plasmo
很舒服~
Plasmo 上手體驗(yàn)
想體驗(yàn)先安裝Plasom,快速上手文檔: getting-started
注意下自己的 pnpm 版本或者 npm 版本,我用的是pnpm
# 使用下面的命令進(jìn)行項(xiàng)目初始化
pnpm dlx plasmo init
# OR npm v7
npm x plasmo init
這時(shí)候如果你是新手,建議直接從 PlasmoHQ/examples 找一個(gè)模版看一下他的目錄結(jié)構(gòu),然后找到自己想要的功能進(jìn)行開(kāi)發(fā)
比如我想基于vue,開(kāi)發(fā)一個(gè) popup 的界面。可以在示例中直接找到 examples/with-vue
還有各種技術(shù)棧(React,svelte,tailwindcss,nextjs…)
不止 popup 頁(yè),還有 background, devtool, options 頁(yè)面都能在 examples 倉(cāng)庫(kù)找到相關(guān)的模版
Plasmo 有一個(gè)很方便的地方在于:我開(kāi)發(fā) popup 的頁(yè)面,我只需要有一個(gè)叫 popup.(tsx | vue)
的文件,開(kāi)發(fā)background,只需要有一個(gè) background.ts
文件。
這些作為對(duì)應(yīng)的入口文件我們只需要按命名規(guī)范寫(xiě)好(甚至可以寫(xiě)成 popup/index.vue
),剩下的 manifest.json
配置就交給 Plasmo
從安裝腳手架到現(xiàn)在,我們都沒(méi)見(jiàn)到 manifest.json
文件,更加說(shuō)明了這些入口不需要我們顯示聲明
Plasmo 修改 manifest.json 配置
雖然沒(méi)有 manifest.json
,但是該要寫(xiě)的配置還是得寫(xiě)的
比如我們開(kāi)發(fā)一個(gè)針對(duì) http://xxxx.com
網(wǎng)頁(yè)的插件,首先得申請(qǐng)權(quán)限 host_permissions
這部分配置寫(xiě)在了 package.json
中的 "manifest"
下。包括申請(qǐng)權(quán)限,注入資源都在 "manifest"
中去配置。
// package.json
{
// ...
"manifest": {
"permissions":["declarativeNetRequest"], // 獲取攔截網(wǎng)絡(luò)請(qǐng)求的權(quán)限
// 頁(yè)面注入靜態(tài)資源
"web_accessible_resources": [
{
"resources": [
"inject.js"
],
// 針對(duì)全部界面注入
"matches": [
"<all_urls>"
]
}
],
// 針對(duì)哪些頁(yè)面生效
"host_permissions": [
"https://xxxx.com/*",
"http://xxxx.com/*"
]
}
// ...
}
有個(gè)例外就是 content.ts (注入到網(wǎng)頁(yè)的那部分內(nèi)容)
因?yàn)?content.ts 對(duì)應(yīng)的配置是 MDN文檔:manifest.json/content_scripts
正常的配置應(yīng)該是這樣的
"content_scripts": [
{
"matches": ["*://*.mozilla.org/*"],
"js": ["content.js"]
}
]
因?yàn)?content.ts 是動(dòng)態(tài)入口,也就是說(shuō) content_scripts[0].js
的內(nèi)容是框架去生成的,而不是我們自己手動(dòng)填的
這也就造成了 content_scripts
的配置只能是寫(xiě)在 content.ts
這個(gè)頁(yè)面中。這樣 Plasmo 才能既知道入口路徑,也知道對(duì)應(yīng)的配置
以下示例代碼來(lái)自: with-content-script/content.ts
// file - content.ts
import type { PlasmoContentScript } from "plasmo"
// 進(jìn)行 content_scripts 的配置
export const config: PlasmoContentScript = {
matches: ["https://www.plasmo.com/*"]
}
window.addEventListener("load", () => {
console.log("content script loaded")
document.body.style.background = "pink"
})
// 運(yùn)行后出來(lái)的配置可能就是
// "content_scripts": [
// {
// "matches": ["https://www.plasmo.com/*"],
// "js": ["content.[hash].js"]
// }
// ]
- 可能又有同學(xué)有疑問(wèn),就是這樣的配置寫(xiě)完,那我豈不是只能寫(xiě)一個(gè)
content.ts
? 如果我想一個(gè)插件針對(duì)不用的站點(diǎn)做不同的操作呢?
好問(wèn)題,去example找找模版就知道了 example/with-many-content-scripts。這里提供了多個(gè) content.ts
的示例,這樣就能針對(duì)不同頁(yè)面注入不同的 content.ts
了
框架也提供了 自定義 manifest.json 的能力。更多的配置可以看 官方文檔: plasmo customization 這部分
Plasmo 提供的一些庫(kù)和功能
- @plasmohq/storage
參考文檔 https://docs.plasmo.com/framework-api/storage
@plasmohq/storage 是一個(gè)來(lái)自 plasmo 的實(shí)用程序庫(kù),它抽象了瀏覽器擴(kuò)展可用的持久存儲(chǔ) API。當(dāng)擴(kuò)展存儲(chǔ) API 不可用時(shí),它會(huì)回退到本地存儲(chǔ),允許在彈出窗口 - 選項(xiàng) - 內(nèi)容 - 背景之間進(jìn)行狀態(tài)同步。
官網(wǎng)還說(shuō)了一句,如果使用了這個(gè)庫(kù),配置會(huì)自動(dòng)把 storage
的權(quán)限加上
我覺(jué)得還是挺好的,這樣依賴(lài)抹平了不同平臺(tái)之間存儲(chǔ)的差異,也做了保底方案
- 一些比較特殊的標(biāo)記符
url:
data-text:~
data-base64:~
這部分標(biāo)記符可以在這些文檔中找到 content-scripts、content-scripts-ui、assets
用起來(lái)就類(lèi)似這樣的:
import cssText from "data-text:~/contents/plasmo-overlay.css"
import someCoolImage from "data-base64:~assets/some-cool-image.png"
import myJavascriptFile from "url:./path/to/my/file/something.js"
這部分更多的可能是為了相對(duì)路徑,或者引入一些特殊的內(nèi)容。比如 data-text:~
這個(gè)就很有用,我可以在 .css
文件中更好的編寫(xiě)我的內(nèi)容,然后通過(guò) data-text:~
把文件的內(nèi)容以 text 引入,用于我注入到頁(yè)面上
url:
這個(gè)也是為了獲取這個(gè)文件在打包后所處的位置。
比方說(shuō)我們按正常模式寫(xiě)文件,寫(xiě)完后可能要給 content.js 動(dòng)態(tài)注入到頁(yè)面去,這時(shí)候可以動(dòng)態(tài)創(chuàng)建script標(biāo)簽,src = chrome.runtime.getURL('xxx.js')
不過(guò)因?yàn)槲覀冞@個(gè)是進(jìn)過(guò)了 Plasmo 打包的,有可能對(duì)應(yīng)的資源被加上了hash值,這時(shí)候 url:
就是獲取文件的路徑了(類(lèi)似 chrome.runtime.getURL(‘xxx.js’) 的功能了)
在示例倉(cāng)庫(kù) examples/with-devtools/devtools.tsx 就有這么一段代碼:
import fontPickerHTML from "url:./panels/font-picker/index.html"
import fontPropertiesHTML from "url:./panels/font-properties/index.html"
chrome.devtools.panels.create(
"Font Picker",
null,
// See: https://github.com/PlasmoHQ/plasmo/issues/106#issuecomment-1188539625
fontPickerHTML.split("/").pop()
)
可以自己打印一下 fontPropertiesHTML
變量,其實(shí)是一個(gè)網(wǎng)頁(yè)的路徑。(使用.split(“/”)是為了處理一個(gè)bug,issuse鏈接也在備注里了,可以看看了解了解)
文檔鏈接在都貼出來(lái)了,更多的用法就自己去摸索了
插件運(yùn)行和打包
不管是運(yùn)行 npm run dev 還是 run build,都會(huì)生成一個(gè) build/xxxx
目錄。里面就是存放著可以運(yùn)行的chrome插件代碼
默認(rèn)是 chrome-mv3-dev
代表開(kāi)發(fā) chrome 插件,v3 版本,dev環(huán)境
當(dāng)然你也可以用 --target
指定是開(kāi)發(fā) firefox 版本/開(kāi)發(fā) mv2版本, –target-flag 畢竟都不推薦開(kāi)發(fā) mv2 的東西了。就不細(xì)說(shuō)了
運(yùn)行 npm run dev 后,把 build/chrome-mv3-dev
這個(gè)文件夾拖到瀏覽器安裝插件的位置,就能看到了。不知道怎么操作的建議看下文檔: loading-the-extension
build/chrome-mv3-dev 目錄下也有 manifest.json 文件,也就是我們?cè)?package.json 里面 + content.ts 的配置,所有的配置都匯總在這里了。想看配置有沒(méi)有生效看這里就行
插件打包,打包為zip
pnpm build -- --zip
# OR
npm run build -- --zip
# OR
plasmo build --zip
打包到firefox
plasmo build --target=firefox-mv2 --zip
插件在多平臺(tái)的兼容性問(wèn)題
build --target=firefox
的作用體現(xiàn)在哪里?
說(shuō)實(shí)話(huà)我也沒(méi)發(fā)現(xiàn),可能是為了多區(qū)分開(kāi)一個(gè)目錄,或者 firefox 沒(méi)升級(jí)到 mv3 版本,又或者是同樣的配置 firefox 有細(xì)微差別,Plasmo就可以自動(dòng)處理掉
至于代碼兼容性
在開(kāi)發(fā)過(guò)程中,我們都是用 chrome
作為插件API,比如 chrome.runtime.sendMessage
- chrome 這個(gè)標(biāo)識(shí)各大平臺(tái)也都識(shí)別,比如 360瀏覽器,包括火狐也兼容
chrome.xxx.xxx
。正常來(lái)說(shuō)不用特別的適配,寫(xiě)的話(huà)也按chrome
來(lái)寫(xiě)即可
開(kāi)頭也提到,我因?yàn)椴荒芸赐饩W(wǎng)的chrome插件開(kāi)發(fā)文檔,好在國(guó)內(nèi)還可以訪問(wèn)
MDN
,API這一塊同步的還是比較快的,甚至有些頁(yè)面有中文翻譯了,平時(shí)查API可以到這里查
- MDN 的文檔的API是用的
browser
開(kāi)頭,兼容性可以看對(duì)應(yīng)文檔下面的表格。(如果你用browser
,在開(kāi)發(fā)過(guò)程是沒(méi)有智能提示的,畢竟我們裝的ts包是@types/chrome
)。
也會(huì)真的發(fā)生有兼容性問(wèn)題,畢竟chrome更新一直都很快的
- 用尤大開(kāi)發(fā)的 vue-devtool 插件來(lái)看看尤大都是如何處理兼容性問(wèn)題的(雖然尤大用的不是 Plasmo,不過(guò)不影響我們學(xué)習(xí)代碼)
判斷運(yùn)行環(huán)境: /packages/shared-utils/src/env.ts
// env.ts 節(jié)選代碼
export const isBrowser = typeof navigator !== 'undefined'
export const target: any = isBrowser
? window
: typeof global !== 'undefined'
? global
: {}
export const isChrome = typeof target.chrome !== 'undefined' && !!target.chrome.devtools
export const isFirefox = isBrowser && navigator.userAgent.indexOf('Firefox') > -1
export const isWindows = isBrowser && navigator.platform.indexOf('Win') === 0
export const isMac = isBrowser && navigator.platform === 'MacIntel'
export const isLinux = isBrowser && navigator.platform.indexOf('Linux') === 0
根據(jù)環(huán)境,使用不同的處理方式,比如網(wǎng)頁(yè)快照
/packages/app-frontend/src/features/timeline/composable/screenshot.ts
在chrome中,能直接使用 chrome.tabs.captureVisibleTab
當(dāng)然還有隨處可見(jiàn)的這樣的判斷
- 如果一定要說(shuō)框架有幫我們處理什么兼容問(wèn)題,那可能就是本地存儲(chǔ)了
提供了 @plasmohq/storage 抹平各個(gè)平臺(tái)的存儲(chǔ)api差異,還提供了快捷的方式然我們更新本地存儲(chǔ)的內(nèi)容
處理兼容性問(wèn)題從來(lái)都不是一件容易的事情,搞不好開(kāi)發(fā)人員都處理的很頭大,所以更加別指望框架能自動(dòng)處理。
總的看下來(lái) --target
好像更多是用于發(fā)布到不同平臺(tái)的時(shí)候有用,而不是幫我們處理不同瀏覽器的兼容問(wèn)題(我的插件不發(fā)布到商店去,所以暫時(shí)找不到用途)
Plasmo 的介紹就到這里了。我也沒(méi)開(kāi)發(fā)什么出名的插件(很慚愧)都是處理公司需要的內(nèi)容,所以可能還有很多好玩的功能沒(méi)發(fā)掘到
Plasmo 還能一鍵發(fā)布到各個(gè)平臺(tái)之類(lèi)的功能,等著你們自己去探索了
如何應(yīng)對(duì) MV3 版本中“較大的變動(dòng)”
一開(kāi)始介紹的時(shí)候有提到版本變動(dòng)有較小的,還有2個(gè)較大的。在我看來(lái)較小的變動(dòng)可能只是改一下配置,不用影響太多業(yè)務(wù)邏輯代碼就能運(yùn)行的。
而較大的變動(dòng)就影響挺大的
background 變動(dòng)的影響
說(shuō)一個(gè)場(chǎng)景,比如我們都很熟悉的瀏覽器攔截插件,或者其他的插件,下面都有角標(biāo)。關(guān)鍵是這些角標(biāo)是根據(jù)當(dāng)前的域名記錄的。
怎么做到的呢?依賴(lài) popup 的頁(yè)面記錄嗎?
popup 幾乎不可能,因?yàn)樵谖议_(kāi)發(fā)過(guò)程中,popup 在每次打開(kāi)的時(shí)候其實(shí)都會(huì)重新運(yùn)行一遍。同一個(gè)站點(diǎn)如果打開(kāi)2次popup.tsx對(duì)應(yīng)的組件就會(huì)在執(zhí)行2次
所以這部分的數(shù)據(jù)就得留給 background.ts
或者 content.ts
去做
為了搞懂這其中的技巧,我看了一下 貓抓
這個(gè)插件的代碼
以下代碼節(jié)選自 貓抓
插件
// js/popup.js
var BG = chrome.extension.getBackgroundPage();
var tabid;
chrome.windows.getCurrent(function(wnd) {
chrome.tabs.getSelected(wnd.id, function(tab) {
tabid = tab.id;
var id = "tabid" + tab.id;
ShowMedia(BG.mediaurls[id]);
});
});
// js/background.js
//初始化
if (typeof mediaurls === 'undefined') {
var mediaurls = new Array();
}
// ...
// 中間的代碼用了 chrome.webRequest.onResponseStarted 監(jiān)聽(tīng)請(qǐng)求
// 然后篩選出 .m3u8 和 分析出對(duì)應(yīng)的 .ts 文件,感興趣的自己在看看
// ...
//標(biāo)簽更新,清除該標(biāo)簽之前記錄
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo) {
if (changeInfo.status == "loading") //在載入之前清除之前記錄
{
var id = "tabid" + tabId; //記錄當(dāng)前請(qǐng)求所屬標(biāo)簽的id
if (mediaurls[id])
mediaurls[id] = [];
}
});
//標(biāo)簽關(guān)閉,清除該標(biāo)簽之前記錄
chrome.tabs.onRemoved.addListener(function(tabId) {
var id = "tabid" + tabId; //記錄當(dāng)前請(qǐng)求所屬標(biāo)簽的id
if (mediaurls[id])
delete mediaurls[id];
});
可以看到,在 popup.js 里面獲取了一個(gè)BG
因?yàn)?MV2的background是有window對(duì)象的。所以 BG 可以理解為 background.html 的 window對(duì)象 var BG = chrome.extension.getBackgroundPage();
從 window 對(duì)象中獲取 mediaurls 參數(shù),獲取對(duì)應(yīng)tab要顯示的角標(biāo)數(shù),然后給到 popup 顯示
如果 background 變成了一個(gè) Service Worker ,那就不存在 window
對(duì)象了
解決方案就是改用通信的方式,popup發(fā)起一個(gè)sendMessage。background來(lái)監(jiān)聽(tīng),并且進(jìn)行回調(diào)給popup
整體的思路還是用 background 來(lái)存儲(chǔ)和轉(zhuǎn)發(fā)消息,background 收到的內(nèi)容后存儲(chǔ)到本地去。
【非常嚴(yán)重】網(wǎng)絡(luò)請(qǐng)求攔截的變動(dòng) declarativeNetRequest
只能感嘆一句,V2 版本的攔截請(qǐng)求還是很好用的。
推薦一篇教程: 小茗同學(xué): 【干貨】Chrome插件(擴(kuò)展)開(kāi)發(fā)全攻略 這里面的的攻略很多都沒(méi)過(guò)時(shí),除了上面說(shuō)的改動(dòng)其他都很值得參考學(xué)習(xí)。我也是看這個(gè)入的門(mén)
可以看到教程的 8.6. webRequest 看看v2版本的攔截網(wǎng)絡(luò)請(qǐng)求寫(xiě)法
之前是通過(guò)聲明 webRequest
、 webRequestBlocking
等權(quán)限來(lái)進(jìn)行網(wǎng)絡(luò)請(qǐng)求的攔截。不過(guò)現(xiàn)在聲明了也沒(méi)用了 issues/1163
都要改為 declarativeNetRequest 來(lái)攔截
雖然新版的API也能攔截請(qǐng)求,修改head頭之類(lèi)的操作
但是,這些操作都沒(méi)有回調(diào)?。?/strong>(V2版本是有回調(diào)的,貓抓就是基于回調(diào)才抓的請(qǐng)求地址)
不過(guò)進(jìn)過(guò)一通瞎找,找到另外一個(gè)文檔(MDN還沒(méi)更新 declarativeNetRequest 的內(nèi)容)
onRuleMatchedDebug
這個(gè)方法有這么一段話(huà)
Fired when a rule is matched with a request. Only available for unpacked extensions with the declarativeNetRequestFeedback permission as this is intended to be used for debugging purposes only.
當(dāng)規(guī)則與請(qǐng)求匹配時(shí)觸發(fā)。 僅適用于具有 declarativeNetRequestFeedback 權(quán)限的解壓擴(kuò)展,因?yàn)檫@僅用于調(diào)試目的。
注意是 解壓擴(kuò)展,意思就是必須是解壓的包/zip包,并且聲明了這個(gè)權(quán)限才能用。
如果你的插件是想發(fā)布到應(yīng)用市場(chǎng),或者生成 .crx
后綴的插件包,一樣是用不了 onRuleMatchedDebug
滴(累了)
雖然background不能直接監(jiān)聽(tīng)返回的內(nèi)容,不過(guò) devtool 面板可以啊 devtools/network。但是如果你想用devtool面板的API話(huà),你得打開(kāi)F12才能用 (累了*2)
所以目前攔截回調(diào)的這一塊還沒(méi)有想出非常通用的方案,或許這就是chrome口中的安全,隱私…
如果想粗暴點(diǎn)解決的話(huà)其實(shí)可以把要攔截的源文件下載下來(lái),然后手動(dòng)添加一個(gè) window.postMessage(xxx) 主動(dòng)給 content.ts 發(fā)消息,然后 content.ts 在轉(zhuǎn)發(fā)到后臺(tái)去
background 部分就攔截網(wǎng)絡(luò)請(qǐng)求,redirect 到插件下載的源文件那邊去(其實(shí)就是針對(duì)性很強(qiáng)針對(duì)某個(gè)網(wǎng)頁(yè)的某個(gè)js可以這么搞)
如果是想篡改某些js的內(nèi)容,而且自己會(huì)本地開(kāi)一個(gè)服務(wù)的話(huà),用 redirect 真的是很方便的
既然都講到攔截了,順便講講 如何攔截網(wǎng)頁(yè)發(fā)出的請(qǐng)求。原理就是用 content.js
注入js,修改 window.XMLHttpRequest
和 window.fetch
方法就能攔截到了
推薦直接學(xué)習(xí): YGYOOO/ajax-interceptor 這里面的代碼
唯一的問(wèn)題可能就是 content.js 注入的速度沒(méi)有頁(yè)面發(fā)起請(qǐng)求的快,就會(huì)有幾條漏網(wǎng)之魚(yú)。
最后
這篇文章主要還是想介紹下 Plasmo。個(gè)人感覺(jué)用下來(lái)還是挺好用的
至于chrome 插件要升級(jí)到 MV3 最嚴(yán)重的其實(shí)還是網(wǎng)絡(luò)請(qǐng)求相關(guān)的。其余的應(yīng)該都還好(最起碼有解決方案)
講了那么多其實(shí)沒(méi)有講到一些開(kāi)發(fā)的技巧類(lèi)東西,主要是一些需要注意的坑。所以匯總一下鏈接方便查找學(xué)習(xí)
文檔類(lèi)
-
chrome原汁原味文檔:https://developer.chrome.com/docs/extensions/mv3/)
-
MDN文檔插件開(kāi)發(fā)文檔:https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions
-
熱心網(wǎng)友同步的原文文檔(MDN找不到的時(shí)候就來(lái)這里看看):https://sunnyzhou-1024.github.io/chrome-extension-docs/extensions/api_index.html
入門(mén)教程推薦
- 小茗同學(xué) 【干貨】Chrome插件(擴(kuò)展)開(kāi)發(fā)全攻略 (http://blog.haoji.me/chrome-plugin-develop.html)[http://blog.haoji.me/chrome-plugin-develop.html]
插個(gè)題外話(huà),如果你既沒(méi)有魔法,又想看原汁原味文檔(挺好的,很有追求)
可以上 github https://github.com/GoogleChrome/developer.chrome.com
把整個(gè) developer.chrome.com 搞下來(lái)(下面的命令不用我細(xì)說(shuō)了把)文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-480910.html
# 安裝依賴(lài)的
npm run ci
# dev 后 打開(kāi) http://localhost:8080/ 就可以看到
npm run dev
# 如果你想同步一份到自己服務(wù)器,就運(yùn)行把
npm run production && npm start
插件開(kāi)發(fā)介紹就到這了,如果你有好的插件記得也推薦給我文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-480910.html
到了這里,關(guān)于chrome V3插件入門(mén)到放棄,Plasmo不完全使用指南的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!