写一些浏览器脚本或者插件方便平时的自动化操作.
油猴脚本编写不难,但考虑到浏览器插件可能涉及功能多同时并不是所有人会去安装一个类似油猴的插件,所以这里选择开发插件.
现在Edge浏览器和Chrome浏览器都差不太多,需要修改一些API即可进行移植,这里选择Chrome浏览器,因为谷歌官方教程写得更详细一些,这里使用的是最新的V3版本.
如果要从谷歌浏览器插件迁移到Edge,也只需要修改一些不支持的API即可
Microsoft Edge 不支持以下扩展 API:
chrome.gcm
.chrome.identity.getAccounts
.chrome.identity.getAuthToken
- 作为替代项,可以使用launchWebAuthFlow
提取 OAuth2 令牌来对用户进行身份验证。chrome.instanceID
.
本地开发时,直接本地导入然后编辑文件能同步的.
文件结构
The manifest
扩展的manifest是唯一必需的文件,它必须有一个特定的文件名:manifest.json。它还必须位于扩展的根目录中。清单记录了重要的元数据、定义了资源、声明了权限,并确定了要在后台和页面上运行的文件。
The service worker
扩展服务程序负责处理和监听浏览器事件。事件有很多种,例如导航到新页面、删除书签或关闭标签页。它可以使用所有 Chrome API,但不能直接与网页内容交互;这是内容脚本的工作。
content script
内容脚本在网页上下文中执行 Javascript。它们还可以读取和修改注入页面的 DOM。内容脚本只能使用 Chrome API 的一个子集,但可以通过与扩展服务 Worker 交换信息来间接访问其余 API。
The popup and other pages
扩展可以包含各种 HTML 文件,如弹出窗口、选项页面和其他 HTML 页面。所有这些页面都可以访问 Chrome API。
常用文件结构
Manifest.json
清单文件1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17{
"manifest_version": 3,
"name": "HightLight",
"version": "1.0",
"action": {
"default_popup": "index.html",
"default_icon": "img/icon.png"
},
"default_locale": "en",
"description": "highlight what you select",
"icons": {
"16": "img/icon.png",
"48": "img/icon.png",
"128": "img/icon.png"
}
}
其中,action键指定 Chrome 浏览器应作为扩展的操作图标使用的图片,以及在点击扩展的操作图标时弹出的 HTML 页面.icons里的选项对应不同的展示.
Icon size | Icon use |
---|---|
16x16 | Favicon on the extension’s pages and context menu icon. |
32x32 | Windows computers often require this size. |
48x48 | Displays on the Extensions page. |
128x128 | Displays on installation and in the Chrome Web Store. |
功能性文件如下,常使用conten-script,popopjs以及background.js
其中injected script
其实就是使用content-script
向DOM中插入js.
而devtools.js
在vue以及react的开发者工具中使用到.
Popup文件
1 | { |
由于单击图标打开popup,焦点离开又立即关闭,所以popup页面的生命周期一般很短,需要长时间运行的代码不要写在popup里面。
content-script
content-script
设置扩展可以运行读取和修改页面内容的脚本。
这些脚本被称为内容脚本。它们是孤立的,这意味着它们可以更改自己的 JavaScript 环境,而不会与其主机页面或其他扩展的内容脚本发生冲突。1
2
3
4
5
6
7
8
9
10
11
12{
...
"content_scripts": [
{
"js": ["scripts/content.js"],
"matches": [
"https://developer.chrome.com/docs/extensions/*",
"https://developer.chrome.com/docs/webstore/*"
]
}
]
}
内容脚本可以使用标准文档对象模型(DOM)来读取和更改页面内容.
此外还可以注入css以及设置代码注入时间1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17{
// 需要直接注入页面的JS
"content_scripts":
[
{
//"matches": ["http://*/*", "https://*/*"],
// "<all_urls>" 表示匹配所有地址
"matches": ["<all_urls>"],
// 多个JS按顺序注入
"js": ["js/jquery-1.8.3.js", "js/content-script.js"],
// JS的注入可以随便一点,但是CSS的注意就要千万小心了,因为一不小心就可能影响全局样式
"css": ["css/custom.css"],
// 代码注入的时间,可选值: "document_start", "document_end", or "document_idle",最后一个表示页面空闲时,默认document_idle
"run_at": "document_start"
}
],
}
content-scripts
不能访问绝大部分chrome.xxx.api
,除了下面这4种:
- chrome.extension(getURL , inIncognitoContext , lastError , onRequest , sendRequest)
- chrome.i18n
- chrome.runtime(connect , getManifest , getURL , id , onConnect , onMessage , sendMessage)
- chrome.storage
content-scripts
和原始页面共享DOM,但是不共享JS,如要访问页面JS(例如某个JS变量),只能通过injected js
来实现
background
- 不使用时终止,需要时重新启动(类似于事件页面)。
- 无权访问 DOM。(service worker独立于页面)
执行js代码以及css
可以注册事件hrome.action.onClicked
,但是这与action的default_popup
冲突,只能使用其中一个.
此外,manifest.json
中有permissions
以及host_permissions
用于权限申请1
2
3
4
5
6{
"permissions": ["activeTab", "storage", "scripting"],
"host_permissions": [
"https://developer.chrome.com/*"
],
}
activeTab 权限允许用户有目的地选择在重点标签页上运行扩展;这样可以保护用户的隐私。另一个好处是它不会触发权限警告。
主机权限允许我们向特定网站授予更高的权限,从而保护用户隐私。这将授予对标题和 URL 属性以及其他功能的访问权限。
JS种类 | 可访问的API | DOM访问情况 | JS访问情况 | 直接跨域 |
---|---|---|---|---|
injected script | 和普通JS无任何差别,不能访问任何扩展API | 可以访问 | 可以访问 | 不可以 |
content script | 只能访问 extension、runtime等部分API | 可以访问 | 不可以 | 不可以 |
popup js | 可访问绝大部分API,除了devtools系列 | 不可直接访问 | 不可以 | 可以 |
background js | 可访问绝大部分API,除了devtools系列 | 不可直接访问 | 不可以 | 可以 |
devtools js | 只能访问 devtools、extension、runtime等部分API | 可以访问devtools | 可以访问devtools | 不可以 |
注册快捷键
1 | { |
这里的_execute_action
相当于服务活动进程里的action.onClicked
通信机制
经常使用的通信包括popup向content-script发送,因为前者不能直接访问DOM,而后者用户不能直接与其交互,同时popup与background.js相互交互也很常用.
对于popup与background之间通信,可以使用chrome.extension.getBackgroundPage
与chrome.extension.getViews
.而其他之间的通信一般chrome.runtime.onMessage
以及对应的sendMessage还有popup的chrome.tabs.sendMessage
,此外还可以使用socket实现长通信chrome.tabs.connect和
chrome.runtime.connect.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51// background.js
function test()
{
alert('我是background!');
}
// popup.js
var bg = chrome.extension.getBackgroundPage();
bg.test(); // 访问bg的函数
// background.js
function test()
{
alert('我是background!');
}
--------------------------
var views = chrome.extension.getViews({type:'popup'});
if(views.length > 0) {
console.log(views[0].location.href);
}
--------------------------
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('来自content的回复:'+response);
});
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('我收到了你的消息!');
});
-----------------------------------
chrome.runtime.sendMessage({greeting: '你好,我是content-script呀,我主动发消息给后台!'}, function(response) {
console.log('收到来自后台的回复:' + response);
});
// 监听来自content-script的消息
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse)
{
console.log('收到来自content-script的消息:');
console.log(request, sender, sendResponse);
sendResponse('我是后台,我已收到你的消息:' + JSON.stringify(request));
});
发布程序
如果只需要自己本地把插件放在浏览器上使用,就不需要发布了.如果想要发布的话,需要注册谷歌开发者账号Chrome 应用商店 - 开发者协议 (google.com),需要5美元.事实上如果你只需要把插件给周围的人用的话,就不需要发布到插件商城了.
本地的话,edge://extensions/
页面可以加载解压缩的扩展或者打包扩展.
插件根目录新建一个名为_locales
的文件夹,再在下面新建一些语言的文件夹,如en
、zh_CN
、zh_TW
,然后再在每个文件夹放入一个messages.json
,同时必须在清单文件中设置default_locale
。
_locales\en\messages.json
内容:1
2
3
4{
"pluginDesc": {"message": "A simple chrome extension demo"},
"helloWorld": {"message": "Hello World!"}
}
_locales\zh_CN\messages.json
内容:1
2
3
4{
"pluginDesc": {"message": "一个简单的Chrome插件demo"},
"helloWorld": {"message": "你好啊,世界!"}
}
在manifest.json
中设置默认语言1
2
3{
"default_locale": "zh_CN",
}
最后我写了一个简单的划重点的扩展,用户点击扩展后可以自己选择某种预定的颜色或者自定义某种颜色,然后光标选中的文字就会根据选择变色作为画出的重点.
遇到的一些问题
- 扩展图标
default_icon
对png格式支持比较好,其他格式官网说支持,但我感觉支持得并不好. - 改动pop.html以及其引入的css,js文件不需要重新加载,其他的文件改动需要重新加载.