谷歌浏览器插件开发是指开发可以在谷歌浏览器中运行的扩展程序,可以为用户提供额外的功能和定制化的体验。谷歌浏览器插件通常由HTML、CSS和JavaScript组成,非常利于前端开发者。 开发者可以利用这些技术在浏览器中添加新的功能、修改现有功能或者与网页进行交互。
要开发谷歌浏览器插件,开发者通常需要创建一个包含*清单文件(manifest.json)、背景脚本(background script)、内容脚本(content script)*等文件的项目结构。清单文件是插件的配置文件,包含插件的名称、版本、描述、权限以及其他相关信息。背景脚本用于处理插件的后台逻辑,而内容脚本则用于在网页中执行JavaScript代码。
谷歌浏览器插件可以实现各种功能,例如添加新的工具栏按钮、修改网页内容、捕获用户输入、与后台服务器进行通信等。开发者可以通过谷歌浏览器插件API来访问浏览器的各种功能和数据,实现各种定制化的需求。 插件开发涉及的要点:
基础配置
开发谷歌浏览器插件,最重要的文件 manifest.json
1{
2 "name": "Getting Started Example",
3 "description": "Build an Extension!",
4 "version": "1.0",
5 "manifest_version": 3,
6 "background": {
7 "service_worker": "background.js"
8 },
9 "action": {
10 "default_popup": "popup.html",
11 "default_icon": {
12 "16": "/images/icon16.png",
13 "32": "/images/icon32.png",
14 "48": "/images/icon48.png",
15 "128": "/images/icon128.png"
16 }
17 },
18 "icons": {
19 "16": "/images/icon16.png",
20 "32": "/images/icon32.png",
21 "48": "/images/icon48.png",
22 "128": "/images/icon128.png"
23 },
24 "permissions": [],
25 "options_page": "options.html",
26 "content_scripts": [
27 {
28 "js": [ "content.js"],
29 "css":[ "content.css" ],
30 "matches": ["<all_urls>"]
31 }
32 ]
33}
- name: 插件名称
manifest_version:对应chrome API插件版本,浏览器插件采用的版本,目前共2种版本,是2和最新版3
- version: 本插件的版本,和发布相关
- action:点击图标时,设置一些交互
- default_icon:展示图标
- 16、32、48、128
- default_popup:popup.html,一个弹窗页面
- default_title:显示的标题
- default_icon:展示图标
- permissions:拥有的权限
- tabs:监听浏览器tab切换事件
- options_ui
- background:
- service_worker:设置打开独立页面
打开pop弹窗页面
设置action的default_popup属性
1{
2 "name": "Hello world",
3 "description": "show 'hello world'!",
4 "version": "1.0",
5 "manifest_version": 3,
6 "action": {
7 "default_popup": "popup.html",
8 "default_icon": {
9 "16": "/images/icon16.png",
10 "32": "/images/icon32.png",
11 "48": "/images/icon48.png",
12 "128": "/images/icon128.png"
13 }
14 },
15 "permissions":["tabs", "storage", "activeTab", "idle"],
16 "background": {
17 "service_worker": "background.js"
18 },
19 "content_scripts": [
20 {
21 "js": [ "content.js"],
22 "css":[ "content.css" ],
23 "matches": ["<all_urls>"]
24 }
25 ]
26}
创建popup.html
1<!DOCTYPE html>
2<html lang="en">
3
4 <head>
5 <meta charset="UTF-8">
6 <meta name="viewport" content="width=device-width, initial-scale=1.0">
7 <title>显示出hello world</title>
8 <link rel="stylesheet" type="text/css" href="popup.css">
9 </head>
10
11 <body>
12 <h1>显示出hello world</h1>
13 <button id="clickBtn">点击按钮</button>
14 <script src="popup.js"></script>
15 </body>
16</html>
文件可以通过链接引入css、js。
1body {
2 width: 600px;
3 height: 300px;
4}
5h1 {
6 background-color: antiquewhite;
7 font-weight: 100;
8}
9
1console.log(document.getElementById('clickBtn'));
2document.getElementById('clickBtn').addEventListener('click', function () {
3 console.log('clicked');
4});
点击插件图标
点击图标可以看到如下的popup的页面。
调试popup.js的方法
通过background打开独立页面
基于background
的service_worker
API可以打开一个独立后台运行脚本。此脚本会随着插件安装,初始化执行一次,然后一直在后台运行。可以用来存储浏览器的全局状态数据。 background脚本是长时间运行在后台,随着浏览器打开就运行,直到浏览器关闭而结束运行。通常把需要一直运行的、启动就运行的、全局公用的数据放到background脚本。
1chrome.action.onClicked.addListener(function () {
2 chrome.tabs.create({
3 url: chrome.runtime.getURL('newPage.html')
4 });
5});
6
为了打开独立页面,需要修改manifest.json
1{
2 "name": "newPage",
3 "description": "Demonstrates the chrome.tabs API and the chrome.windows API by providing a user interface to manage tabs and windows.",
4 "version": "0.1",
5 "permissions": ["tabs"],
6 "background": {
7 "service_worker": "service-worker.js"
8 },
9 "action": {
10 "default_title": "Show tab inspector"
11 },
12 "manifest_version": 3
13}
为了实现打开独立页面,在manifest.json中就不能在配置 action:default_popup
在newPage.js
文件中可以使用*chrome.tabs*和chrome.windowsAPI; 可以使用 chrome.runtime.getUrl 跳转一个页面。
1chrome.runtime.onInstalled.addListener(async () => {
2 chrome.tabs.create(
3 {
4 url: chrome.runtime.getURL('newPage.html'),
5 }
6 );
7});
content内容脚本
content-scripts(内容脚本)是在网页上下文中运行的文件。通过使用标准的文档对象模型(DOM),它能够读取浏览器访问的网页的详细信息
,可以对打开的页面进行更改,还可以将DOM信息传递给其父级插件。内容脚本相对于background还是有一些访问API上的限制,它可以直接访问以下chrome的API
- i18n
- storage
- runtime:
- connect
- getManifest
- getURL
- id
- onConnect
- onMessage
- sendMessage
content.js
运行于一个独立、隔离的环境,它不会和主页面的脚本或者其他插件的内容脚本发生冲突 有2种方式添加content脚本
在配置中设置
1"content_scripts": [
2 {
3 "js": [ "content.js"],
4 "css":[ "content.css" ],
5 "matches": ["<all_urls>"]
6 }
7]
content_scripts属性除了配置js,还可以设置css样式,来实现修改页面的样式。 matches表示需要匹配的页面; 除了这3个属性,还有
- run_at: 脚本运行时刻,有以下3个选项
- document_idle,默认;浏览器会选择一个合适的时间注入,并是在dom完成加载
- document_start;css加载完成,dom和脚本加载之前注入。
- document_end:dom加载完成之后
- exclude_matches:排除匹配到的url地址。作用和matches相反。
动态配置注入
在特定时刻才进行注入,比如点击了某个按钮,或者指定的时刻 需要在popup.js
或background.js
中执行注入的代码。
1chrome.tabs.executeScript(tabs[0].id, {
2 code: 'document.body.style.backgroundColor = "red";',
3});
也可以将整个content.js进行注入
1chrome.tabs.executeScript(tabs[0].id, {
2 file: "content.js",
3});
利用content制作一个弹窗工具
某天不小心让你的女神生气了,为了能够道歉争取到原谅,你是否可以写一个道歉信贴到每一个页面上,当女神打开网站,看到每个页面都会有道歉内容。
道歉信内容自己写哈,这个具体看你的诚意。 下面设置2个按钮,原谅和不原谅。 点击原谅,就可以关闭弹窗。 点击不原谅,这个弹窗调整css布局位置继续显示。(有点像恶意贴片广告了)
下面设置content.js的内容
1let newDiv = document.createElement('div');
2newDiv.innerHTML = `<div id="wrapper">
3 <h3>小仙女~消消气</h3>
4 <div><button id="cancel">已消气</button>
5 <button id="reject">不原谅</button></div>
6</div>`;
7newDiv.id = 'newDiv';
8document.body.appendChild(newDiv);
9const cancelBtn = document.querySelector('#cancel');
10const rejectBtn = document.querySelector('#reject');
11cancelBtn.onclick = function() {
12 document.body.removeChild(newDiv);
13 chrome.storage.sync.set({ state: 'cancel' }, (data) => {
14 });
15}
16rejectBtn.onclick = function() {
17 newDiv.style.bottom = Math.random() * 200 + 10 + "px";
18 newDiv.style.right = Math.random() * 800 + 10 + "px";
19}
20
21
22
23
24
content.css布局样式
1#newDiv {
2 font-size: 36px;
3 color: burlywood;
4 position: fixed;
5 bottom: 20px;
6 right: 0;
7 width: 300px;
8 height: 200px;
9 background-color: rgb(237, 229, 216);
10 text-align: center;
11 z-index: 9999;
12}
打开option页面
options页,就是插件的设置页面,有2个入口
- 1:点击插件详情,找到扩展程序选项入口
- 2插件图标,点击右键,选择 ‘选项’ 菜单
可以看到设置的option.html页面
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>插件的option配置</title>
7</head>
8<body>
9 <h3>插件的option配置</h3>
10</body>
11</html>
此页面也可以进行js、css的引入。
替换浏览器默认页面
override功能,是可以替换掉浏览器默认功能的页面,可以替换newtab、history、bookmark三个功能,将新开页面、历史记录页面、书签页面设置为自定义的内容。 修改manifest.json
配置
1{
2 "chrome_url_overrides": {
3 "newtab": "newtab.html",
4 "history": "history.html",
5 "bookmarks": "bookmarks.html"
6 }
7}
创建一个newtab的html页面
1<!DOCTYPE html>
2<html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Document</title>
7 </head>
8 <body>
9 <h1>new tab</h1>
10 </body>
11</html>
插件更新后,点开新的tab,就会出现我们自定义的页面。第一次的情况会让用户进行选择,是进行更换还是保留原来的配置。
很多插件都是使用newtab进行自定义打开的tab页,比如掘金的浏览器插件,打开新页面就是掘金网站插件。
页面之间进行数据通信
如需将单条消息发送到扩展程序的其他部分并选择性地接收响应,请调用 runtime.sendMessage() 或 tabs.sendMessage()。通过这些方法,您可以从内容脚本向扩展程序发送一次性 JSON 可序列化消息,或者从扩展程序向内容脚本发送。如需处理响应,请使用返回的 promise。 来源地址:developer.chrome.com/docs/extens…
content中脚本发送消息
chrome.runtime.sendMessage
只能放在content的脚本中。
1(async () => {
2 const response = await chrome.runtime.sendMessage({greeting: "hello"});
3
4 console.log(response);
5})();
其他页面发送消息
其他页面需向内容脚本发送请求,请指定请求应用于哪个标签页,如下所示。此示例适用于 Service Worker、弹出式窗口和作为标签页打开的 chrome-extension:// 页面
1(async () => {
2 const [tab] = await chrome.tabs.query({active: true, lastFocusedWindow: true});
3 const response = await chrome.tabs.sendMessage(tab.id, {greeting: "hello"});
4
5 console.log(response);
6})();
接收消息使用onMessage
在扩展程序和内容脚本中使用相同的代码
1chrome.runtime.onMessage.addListener(
2 function(request, sender, sendResponse) {
3 console.log(sender.tab ?
4 "from a content script:" + sender.tab.url :
5 "from the extension");
6 if (request.greeting === "hello")
7 sendResponse({farewell: "goodbye"});
8 }
9);
添加右键菜单
创建菜单
首先在manifest.json
的权限中添加配置
1{
2 "permissions": ["contextMenus"]
3}
4
在background.js
中添加创建菜单的代码
1let menu1 = chrome.contextMenus.create({
2 type: 'radio',
3 title: 'click me',
4 id: "myMenu1Id",
5 contexts:['image']
6}, function(){
7
8})
9
10let menu2 = chrome.contextMenus.create({
11 type: 'normal',
12 title: 'click me222',
13 id: "myMenu222Id",
14 contexts:['all']
15}, function(){
16
17})
18
19let menu3 = chrome.contextMenus.create({
20 id: 'baidusearch1',
21 title: '使用百度搜索:%s',
22 contexts: ['selection'],
23});
24
25
26chrome.contextMenus.remove('myMenu222Id');
27
28chrome.contextMenus.removeAll();
29
30
31chrome.contextMenus.onClicked.addListener(function(info, tab){
32 if(info.menuItemId == 'myMenu222Id'){
33 console.log('xxx')
34 }
35})
以下是其他可以使用的api
1
2chrome.contextMenus.remove(menuItemId);
3
4chrome.contextMenus.removeAll();
5
6chrome.contextMenus.update(menuItemId, updateProperties);
7
8chrome.contextMenus.onClicked.addListener(function(info, tab)) {
9
10});
绑定点击事件,发送接口请求
首先需要在manifest.json
的hosts_permissions
中添加配置
1{
2 "host_permissions": ["http://*/*", "https://*/*"]
3}
创建node服务器,返回json数据
1
2const { createServer } = require('node:http');
3const url = require('url');
4
5const server = createServer((req, res) => {
6 var pathname = url.parse(req.url).pathname;
7
8 if (pathname.includes('api')) {
9 res.writeHead(200, { 'Content-Type': 'application/json' });
10 res.write(
11 JSON.stringify({
12 name: 'John Doe',
13 age: 30,
14 })
15 );
16 res.end();
17 } else {
18 res.writeHead(200, { 'Content-Type': 'text/plain' });
19 res.end('Hello World!\n' + pathname);
20 }
21});
22
23server.listen(8080, '127.0.0.1', () => {
24 console.log('Listening on 127.0.0.1:8080');
25});
编辑background.js
文件
1
2
3chrome.contextMenus.onClicked.addListener(function (info, tab) {
4 if (info.menuItemId === 'group1') {
5 console.log('分组文字1', info);
6 }
7 if (info.menuItemId === 'group2') {
8 console.log('分组文字2');
9 }
10
11 if (info.menuItemId === 'fetch') {
12 console.log('fetch 获取数据');
13 const res = fetch('http://localhost:8080/api', {
14 method: 'GET',
15 headers: {
16 'Content-Type': 'application/json',
17 },
18 }).then((res) => {
19 console.log(res, '获取到http://localhost:8080/api接口数据');
20 chrome.storage.sync.set({ color: 'red' }, function (err, data) {
21 console.log('store success!');
22 });
23 });
24 }
25
26 if (info.menuItemId === 'baidusearch1') {
27
28
29 chrome.tabs.create({
30 url:
31 'https://www.baidu.com/s?ie=utf-8&wd=' + encodeURI(info.selectionText),
32 });
33 }
34});
35
36
37chrome.runtime.onInstalled.addListener(function () {
38
39 let contexts = [
40 'page',
41 'selection',
42 'link',
43 'editable',
44 'image',
45 'video',
46 'audio',
47 ];
48
49
50
51
52
53
54
55
56
57
58
59 let parent = chrome.contextMenus.create({
60 title: '操作数据分组',
61 id: 'parent',
62 });
63 chrome.contextMenus.create({
64 title: '分组1',
65 parentId: parent,
66 id: 'group1',
67 });
68 chrome.contextMenus.create({
69 title: '分组2',
70 parentId: parent,
71 id: 'group2',
72 });
73 chrome.contextMenus.create({
74 title: '获取远程数据',
75 parentId: parent,
76 id: 'fetch',
77 });
78
79
80 chrome.contextMenus.create({
81 title: '创建单选按钮1',
82 type: 'radio',
83 id: 'radio1',
84 });
85 chrome.contextMenus.create({
86 title: '创建单选按钮2',
87 type: 'radio',
88 id: 'radio2',
89 });
90
91
92 chrome.contextMenus.create({
93 title: '可以多选的复选框1',
94 type: 'checkbox',
95 id: 'checkbox',
96 });
97 chrome.contextMenus.create({
98 title: '可以多选的复选框2',
99 type: 'checkbox',
100 id: 'checkbox2',
101 });
102
103
104 chrome.contextMenus.create({
105 id: 'baidusearch1',
106 title: '使用百度搜索:%s',
107 contexts: ['selection'],
108 });
109
110
111
112 chrome.contextMenus.create(
113 { title: 'Oops', parentId: 999, id: 'errorItem' },
114 function () {
115 if (chrome.runtime.lastError) {
116 console.log('Got expected error: ' + chrome.runtime.lastError.message);
117 }
118 }
119 );
120});
点击鼠标右键,效果如下
如果在页面选择几个文字,那么就显示出百度搜索快捷键,
缓存,数据存储
首先在manifest.json
的权限中添加storage
配置
1{
2 "permissions": ["storage"]
3}
1chrome.storage.sync.set({color: 'red'}, function(){
2 console.log('background js storage set data ok!')
3})
然后就可以在content.js或popup.js中获取到数据
1
2chrome.storage.sync.get({color: 'yellow'}, function(){
3 console.log('background js storage set data ok!')
4})
tabs创建页签
首先在manifest.json
的权限中添加tabs配置
1{
2 "permissions": ["tabs"]
3}
添加tabs的相关操作
1chrome.tabs.query({}, function(tabs){
2 console.log(tabs)
3})
4function getCurrentTab(){
5 let [tab] = chrome.tabs.query({active: true, lastFocusedWindow: true});
6 return tab;
7}
notifications消息通知
Chrome提供chrome.notifications的API来推送桌面通知;首先在manifest.json中配置权限
1{
2 "permissions": [
3 "notifications"
4 ],
5}
然后在background.js脚本中进行创建
1
2chrome.notifications.create(null, {
3 type: "basic",
4 iconUrl: "drink.png",
5 title: "喝水小助手",
6 message: "看到此消息的人可以和我一起来喝一杯水",
7});
8
devtools开发扩展工具
在manifest中配置一个devtools.html
1{
2 "devtools_page": "devtools.html",
3}
devtools.html中只引用了devtools.js,如果写了其他内容也不会展示
1<!DOCTYPE html>
2<html lang="en">
3 <head> </head>
4 <body>
5 <script type="text/javascript" src="./devtools.js"></script>
6 </body>
7</html>
创建devtools.js文件
1
2
3chrome.devtools.panels.create(
4
5 "DevPanel",
6
7 "panel.png",
8
9 "Panel.html",
10 function (panel) {
11 console.log("自定义面板创建成功!");
12 }
13);
14
15
16chrome.devtools.panels.elements.createSidebarPane(
17 "Sidebar",
18 function (sidebar) {
19 sidebar.setPage("sidebar.html");
20 }
21);
然后在创建自定的Panel.html和sidebar.html页面。