创建谷歌插件
AprilTong 8/10/2021 工具
# 入门谷歌插件
一个谷歌扩展其实就是一个配置(入口)文件 manifest.json 和一系列 html、css、js、图片文件的集合
- manifest.json 说明
{
// 插件的名称
"name": "测试插件",
// 插件的描述
"description": "april的谷歌插件",
"version": 0.1,
"manifest_version": 2,
// 图标位置
"icons": {
"19": "icons/icon19.png",
"38": "icons/icon38.png",
"48": "icons/icon48.png",
"128": "icons/icon128.png"
},
// 设置扩展的图标放在 Chrome 工具栏中
"browser_action": {
"default_title": "april",
// 点击扩展图标所显示的页面位置
"default_popup": "popup/popup.html"
},
// 可以使拓展常驻后台,比较常用的是指定子属性scripts,表示在拓展启动时自动创建一个包含所有指定脚本的页面。
"background": {
"scripts": [
"background.js"
]
},
// 定义拓展需要向chorme申请的权限
"permissions": [
// 可以跨域请求的接口
"https://www.baidu.com/",
// 存储
"storage",
// 浏览器选项卡
"tabs",
// 当前活动选项卡
"activeTab",
// 浏览器通知
"notifications",
// 右键菜单
"contextMenus",
],
// 自定义页面替换chrome相应的页面,新标签页(newTab)、书签页面(bookmarks)、历史记录(history)
"chrome_url_overrides": {
"newtab": "tab.html"
},
// 需要直接注入页面的JS
"content_scripts": [
{
// 匹配的URL,<all_urls>表示匹配所有
"matches": [
"<all_urls>"
],
"js": [
"content-script.js"
],
// 通过插件向页面中注入脚本。content-scripts 和原始页面共享 DOM,但是不共享JS。代码注入的时间,可选值: "document_start", "document_end", or "document_idle",最后一个表示页面空闲时,默认document_idle
"run_at": "document_start"
}
]
}
}
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
52
53
54
55
56
57
58
59
60
61
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
52
53
54
55
56
57
58
59
60
61
- 调试
├── images/
├── popup.html
├── manifest.json
├── content-script.js
1
2
3
4
5
2
3
4
5
打开拓展页面(chrome://extensions/),打开开发者模式,点击如图 加载正在开发的扩展程序,选择你的文件夹,就可以看到了。每次更改配置文件之后,都需要点击刷新按钮重新加载。
# 遇到的主要问题
- 消息通信 popup 向 content 互相通信
popup.js
// 向content-script发送信息
function sendMessageToContentScript(message, callback) {
getCurrentTabId((tab) => {
// 在参数中添加当前页面的URL
chrome.tabs.sendMessage(tab.id, { ...message, url: tab.url }, function(response) {
if (callback) callback(response)
})
})
},
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
// 获取当前选项卡ID
function getCurrentTabId(callback) {
chrome.tabs.query({ active: true, currentWindow: true }, function(tabs) {
if (callback) callback(tabs.length ? tabs[0] : null)
})
}
1
2
3
4
5
6
2
3
4
5
6
// 测试通讯事件
document.getElementById('getSume').onclick = function() {
sendMessageToContentScript({ cmd: 'update_font_size', size: 42 }, function(response) {
// response就是content-script发过来的消息
alert(response)
})
}
1
2
3
4
5
6
7
2
3
4
5
6
7
content-script.js
// 接收来自后台的消息
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
console.log(
'收到来自 ' + (sender.tab ? 'content-script(' + sender.tab.url + ')' : 'popup或者background') + ' 的消息:',
request
)
sendResponse('我收到你的消息了:' + JSON.stringify(request))
})
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
- 带有异步的 chrome.runtime.onMessage 响应
错误的代码,传到 popup.js 中一直获取的是 undefined
chrome.runtime.onMessage.addListener(async (request, sender, sendResponse) => {
let key = await getKey()
sendResponse(key)
})
1
2
3
4
2
3
4
解决:
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) => {
(async () => {
let key = await getKey()
sendResponse(key)
})()
return true
})
1
2
3
4
5
6
7
2
3
4
5
6
7
content-script.js 向 popup 传递 Blob 类型数据 问题:在传参为 Blob 类型,在 content-script.js 打印出来没问题,但传给后端就变成了[object object]。如图所示:
解决: 谷歌扩展只能传输和 JSON 兼容的数据,可通过 JSON.parse(JSON.stringify(form_data))判断兼容与否。 Blob 和 JSON 不兼容。要上传文件,可通过发送方将 Blob 对象转换为 dataURL,接收方解析回 Blob 对象,再生成 FormData 对象上传文件
发送方:
let file = null let reader = new FileReader() // 将blob转换为dataURL reader.readAsDataURL(data) reader.onload = function(e) { file = e.target.result sendResponse({ file, }) }
1
2
3
4
5
6
7
8
9
10接收方:
this.sendMessageToContentScript(item, async function(response) { // dataURL转换为Blob const dataURLtoBlob = (dataUrl) => { let arr = dataUrl.split(','), mime = arr[0].match(/:(.*?);/)[1], bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n) while (n--) { u8arr[n] = bstr.charCodeAt(n) } return new Blob([u8arr], { type: mime, }) } const { file } = response let formData = new FormData() formData.append('file', dataURLtoBlob(file)) let { data } = await axios.post('XXX', formData, { headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', }, }) })
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
--- 接到需求的时候,觉得自己搞不定,了解一番之后也没有想象的那么难~勇敢牛牛怎么会害怕困难呢~完