原文地址: https://segmentfault.com/a/11…
转载请在文首注明起源
TL;DR
- 背景介绍
- 需要剖析
- 常识储备
- 文档举荐
- 构造组成
- 组件通信
- 开发调试
- 打包公布
- 问题解决
背景介绍
本文历时较久,2020 年写了一半, 写到迷茫, 索性搁置,2021 年翻出来持续写的; 通过了半年多的工夫再看, 会有一些不一样的了解, 存在局部配图格调不统一. 然而我会确保内容的正确和表白的统一.
2020 年的某一天我把之前写的 QQ 空间批量删除说说 & 留言的脚本写了个 chrome 插件版
, 名字也改成了 QQ 空间小助手.
因为也是第一次写插件, 所以记录这个过程, 分享开发过程和期间遇到的问题, 并做一些关键点的梳理.
所以,这并不是一篇大而全的 chrome extension 开发教程, 这是一篇以 QQ 空间助手为实战来切入插件开发然而偏重讲 chrome extension 开发的文章.
需要剖析
QQ 空间小助手性能很简略, 外围性能 (其实也就这点性能) 是批量删除 qq 空间的说说和留言.
实现上也很简略, 只须要两个步骤.
- 获取说说或者留言列表
- 顺次遍历 id 删除说说或者留言
这外面的艰难一个在于找到获取 & 删除 留言和说说 所须要的参数 (这个咱们不开展介绍),
另外一个艰难是这些性能怎么在 chrome extension
中实现.
依据下面这些需要, 我了解大略操作流程是这样的,
点击 icon
, 或者点击icon
弹出来的页面而后就开始获取列表, 而后遍历列表开始删说说.
外面大略须要用到, 组件, 组件间通信这些内容.
然而因为是没有接触过这块, 所以得搞清楚都须要有什么常识储备, 那就先从这里开始介绍.
常识储备
整体上, 插件开发这个货色没那么难, 须要的技术也比较简单. 基本上就是前端那些常识:
- JavaScript
- HTML + CSS
外加一些软技能
chrome devtools
的应用和根本的调试能力- 文档浏览能力
基本上做过我的项目的前端都能 hold 住.
如果你在开发之前有些不确定的问题的话须要提前理解的话, 也能够看这篇文档高频问题 Q &A.
介绍完了常识储备, 咱们就得看文档了.
文档举荐
开发文档举荐看这份官网文档 加上一些 官网 demo.
接触新的技术, 文档必定是不可少的, 然而网上各种教程 + 文档, 泥沙俱下, 咱们到底应该看教程还是看文档.
我集体了解是, 不论教程还是文档, 都能够看, 然而要优先看官网的文档和教程, 看一手的材料, 因为我看文档的过程中发现, 非官方的货色 (非一手文档) 信息传播不残缺, 甚至有纰漏. 所以尽着官网的来, 除非有很好的非官方教程或者其余的起因.
也有一份非官方文档不错, 然而不举荐间接看, 倡议联合官网文档作为对照来看, 因为我在开发的时候发现这份文档有些内容和官网最新的文档不统一 (比方对于browser action
和page action
的定义).
另外, 在理论的浏览过程中, 还会有些中央看文档看不明确的, 这时候有针对的搜一些博客 / 教程就能够. 比方这次组件通信的中央有困惑, 就找了些不错的文档看.
- 一篇文章教你顺利入门和开发 chrome 扩大程序(插件)(文章中的代码有笔误, 不要间接拿来用)
- Chrome 插件中 popup,background,contantscript 消息传递机制
还有一些别的文章, 都整顿下来能够参考.
- 官网文档(举荐) | 官网 Demo
- 举荐的非官方文档
-
插件通信相干辅助
- 一篇文章教你顺利入门和开发 chrome 扩大程序(插件)
- Chrome 插件中 popup,background,contantscript 消息传递机制
-
其余
- Chrome 插件开发全攻略
- 调试相干 | Debugging extensions
- 打包公布相干 | 创立和公布自定义 Chrome 利用和扩大程序
看完文档咱们大抵会失去一些信息. 上面就来讲讲.
构造组成
UI 组成
在这一部分你只须要理解一个插件会由哪些局部组成就能够了, 具体性能前面介绍.
失常状况下咱们看到的浏览器和插件大略长这样
当咱们鼠标点击插件 icon 之后就变成了这样
从下面的图上来看, 次要由
icon
(任务栏的图标) 和 点击之后的弹窗两个大件组成, 局部插件还会有一个专门的用于配置的页面(这里没用到, 就不介绍了).
另外, 在 UI 之外, 还会 background
和content_script
存在.
再加上这两个重要的概念之后整个构造就是这样的
上面看看文件构造.
文件构造
文件构造比拟能直观的反馈组件的组成和性能. 从文件构造来看。个别一个插件的文件构造长这样的(之所以说个别是因为不是每个插件都能用到全副的性能).
qzone_helper_extension
│ background.js
│ manifest.json
│ popup.html
│ popup.js
│ qq_icon.png
└─ content_script.js
其中各个文件的性能如下:
manifest.json
是咱们最先须要理解的局部, 这个文件的性能相似于package.json
文件,这里定义插件的配置信息,这些信息小到插件名称,图标,版本号等形容信息, 大到你须要申请的权限和各种引入的资源,入口文件等重要配置。所以这里应该是咱们最先须要理解的局部。qq_icon.png
是最不值得咱们了解的局部,这就是个图标文件而已。popup.html
是比拟直观的一个文件,这个就是你点击插件图标之后弹出来的界面,那个界面是 html 写的, 同样的, 对应的popup.js
用来解决popup
页面的交互和popup
模块和别的模块之间的通信。background.js
和content_script.js
是外围. 其中,background.js
能够了解为我的项目的app.js
文件, 而content_script.js
是和网页是一伙的, 能够简略的了解为是咱们在网页那边的代理, 帮咱们做些网页相干的操作.
加上这些构造和文件之间的对应关系是这样的
到这里, 你大略理解的一个插件的根本组成构造. 理解完构造, 咱们分来看看各局部的具体情况.
插件各局部性能
Icon(按钮)
一般来说,Icon
是咱们插件的性能入口, 也能够显示一些徽标.
在 chrome 插件中, 这种 icon 按钮是分两种 的:
- page action
- browser action
他们的区别与应用场景如下
类型 | 反对性能 | 利用场景 |
---|---|---|
page action | 不能应用徽章(就是揭示你有多少未读音讯的红点和数字) | 非应用在所有页面的状况, 比方我这次开发的 QQ 空间小助手就只是利用在 user.qzone.qq.com 的一个插件, 应用的就是 page action |
browser action | 领有残缺的性能(tooltip / popup / badges 对于这些性能不再赘述) | 对所有页面都能够应用的插件, 比方 adblock / vimium 这种 |
你可能有点困惑, 我理解了这个区别了, 然而有什么用吗? 有用, 在定义 manifest.json
的时候会用到.page action
对应的定义字段是 page_action
,browser action
对应的定义字段是browser_action
.
pupop(弹出页面)
pupop
这个页面次要承载简略的配置和信息展现性能.
这就是个一般的 html
文件, 外面写你的 css
和js
逻辑.
须要留神的是, 这里的 js
只能操作 pupop
外面的DOM
.
回到咱们的需要, 咱们能够在
popup
页面搁置一些用于触发操作的按钮. 比方删除说说按钮
manifest.json
这个文件很重要, 然而一上来就放这个文件, 让人有点摸不着头脑, 所以, 放在这里.
然而这个文件没必要每个字段都分明啥意思, 搞清楚你用到的就行了.
{
// Require
"manifest_version": 2, // 不同的 manifest 版本会有不同的性能
"name": "My Extension",
"version": "versionString",
// Recommended
"default_locale": "en",
"description": "A plain text description",
"icons": {...}, // icon 文件门路
// Pick one (or none)
"browser_action": {...},
"page_action": {...},
// Optional
"action": ...,
"author": ...,
"automation": ...,
"background": { // background 对应的配置
// Recommended
"persistent": false,
// Optional
"service_worker":
},
"chrome_settings_overrides": {...},
"chrome_ui_overrides": {
"bookmarks_ui": {
"remove_bookmark_shortcut": true,
"remove_button": true
}
},
"chrome_url_overrides": {...},
"commands": {...},
"content_capabilities": ...,
"content_scripts": [{...}], // content_script 对应的配置
"content_security_policy": "policyString",
"converted_from_user_script": ...,
"current_locale": ...,
"declarative_net_request": ...,
"devtools_page": "devtools.html",
"event_rules": [{...}],
"externally_connectable": {"matches": ["*://*.example.com/*"]
},
"file_browser_handlers": [...],
"file_system_provider_capabilities": {
"configurable": true,
"multiple_mounts": true,
"source": "network"
},
"homepage_url": "http://path/to/homepage",
"import": [{"id": "aaaa"}],
"incognito": "spanning, split, or not_allowed",
"input_components": ...,
"key": "publicKey",
"minimum_chrome_version": "versionString",
"nacl_modules": [...],
"oauth2": ...,
"offline_enabled": true,
"omnibox": {"keyword": "aString"},
"optional_permissions": ["tabs"],
"options_page": "options.html",
"options_ui": {
"chrome_style": true,
"page": "options.html"
},
"permissions": ["tabs"], // 须要申请的权限
"platforms": ...,
"replacement_web_app": ...,
"requirements": {...},
"sandbox": [...],
"short_name": "Short Name",
"signature": ...,
"spellcheck": ...,
"storage": {"managed_schema": "schema.json"},
"system_indicator": ...,
"tts_engine": {...},
"update_url": "http://path/to/updateInfo.xml",
"version_name": "aString",
"web_accessible_resources": [...]
}
background.js
这根本能够了解为是一个常驻后盾的 js
文件, 在外面能够解决一些事件监听. 比方监听页面初始化的事件 (chrome.runtime.onInstalled
), 监听通信事件(chrome.runtime.onMessage
).
另外, 从 background
收回去的申请能够跨域.
在 V3 的实现中,background 引入了 service worker 的概念.
在 chrome 的 设置 - 更多工具 - 工作管理器 外面能够看到咱们的 background
工作过程.
content_script.js
content_script
文件是和浏览器关上的页面一块加载的, 能够操作关上页面的 DOM.
例如, 点击插件的图标, 而后页面扭转色彩. 这种状况是不能在 background.js
中间接扭转页面色彩的, 而是须要通过事件发送音讯到 content_script.js
中, 通过 content_script
来操作页面DOM
.
理解完这些咱们大略晓得了, 咱们获取发申请的哪些参数包含发申请都能够在
content_script
中实现. 因为只有之类能够操作页面的 DOM.
组件通信
下面介绍完了各个组成部分, 上面介绍这些局部之间的通信.chrome extension
的通信是通过事件来进行的, 通信的内容是无效的 JSON
对象. 共有三种通信形式:
Simple one-time requests
(就像短连贯)Long-lived connections
(就像长连贯)Cross-extension messaging
(多个插件间通信)
咱们这里只开展这次用到的 Simple one-time requests
.
插件内的通信分为这几种:
content script => background
content script => popup
background => content script
background => popup
popup => content script
popup => background
content script => background
/content script => popup
/background => popup
/popup => background
这么发送事件
chrome.runtime.sendMessage({greeting: "hello"}, function(response) {console.log(response.farewell);
});
content script
发送的事件popup
和background
都能够收到, 所以再发送的时候须要加上发给谁的标识, 而后收到音讯的时候解决popup => background
的通信其实也能够通过chrome.extension.getBackgroundPage()
来获取到 background 的所有办法, 间接调用
background => content script
/popup => content script
这么发送事件
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {chrome.tabs.sendMessage(tabs[0].id, {greeting: "hello"}, function(response) {console.log(response.farewell);
});
});
- 因为可能关上了多个同一个 url 的 tab, 所以须要辨别 tab.
- 通过 chrome.tabs.sendMessage 发送的事件只有指定页面的 content 能够收到
而对于事件的监听解决是一样的
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
console.log(sender.tab ?
"from a content script:" + sender.tab.url :
"from the extension");
if (request.greeting == "hello")
sendResponse({farewell: "goodbye"});
}
);
整个通信用一张图示意就是
加上通信的局部, 咱们大略分明了, 咱们在
popup
中触发操作, 而后popup
发送事件给content_script
,content_script
开始获取申请参数, 申请数据列表, 而后遍历数据做删除操作. 整个过程大略就这样, 没用到background
.
开发调试
开发时候如何运行插件
在地址栏关上 chrome://extensions/
, 而后开启右上角的 开发者模式
而后点击 加载已解压的拓展程序
, 在关上的文件抉择框中抉择你的目录就算装置你开发的插件了.
如何关上控制台
background / popup / content script
的控制台都是独立的, 能够通过上面的形式关上
- 关上
background
的控制台
在chrome://extensions/
页面, 点击对应的插件 背景页 三个字即可关上background
的控制台 - 关上
popup
的控制台
点击icon
在弹出的popup 页面
上右键, 而后点击查看, 就能够关上popup
的控制台. 须要留神的是popup
页面一旦敞开, 控制台也会随之敞开. - 关上
content script
的控制台content script
的控制台其实就是 tab 页的控制台, 然而须要切换一下
如何 debug
插件的调试和一般前端开发的 debug 形式是一样的.
更多详情能够参考文档:Debugging extensions
打包公布
咱们的插件开发实现之后能够抉择打包成 crx
文件公布到 chrome 网上利用店, 上传到 chrome 是须要注册开发者的, 注册账号须要 5$, 注册之后能够公布多个 chrome 插件.
更多详情能够参考文档创立和公布自定义 Chrome 利用和扩大程序
打包性能在 加载已解压的拓展程序
按钮的旁边, 而后依照提醒走就能够了
也能够间接打成压缩包放到网上让他人下载应用, 之前能够本地装置 crx
文件, 当初不反对了, 都是通过 加载已解压的拓展程序
在本地应用.
问题解决
chrome.tabs.query 失去空的 tabs
开发的过程中发现有的时候 chrome.tabs.query
失去的后果为[]
, 起初发现这是 chrome 的一个 bug, 在 2015 年就存在了, 始终没有修复. 解决办法如下:
var activeTabId;
chrome.tabs.onActivated.addListener(function(activeInfo) {activeTabId = activeInfo.tabId;});
// https://bugs.chromium.org/p/chromium/issues/detail?id=462939
function getActiveTab(callback) {chrome.tabs.query({currentWindow: true, active: true}, function (tabs) {let tab = tabs[0];
if (tab) {callback(tab.id);
} else {chrome.tabs.get(activeTabId, function (tab) {if (tab) {callback(tab.id);
} else {console.log('No active tab identified.');
}
});
}
});
}
更多相干探讨见: Why doesn’t chrome.tabs.query() return the tab’s URL when called using RequireJS in a Chrome extension?
让插件只在特定页面可用性能实现
需要是在某些页面插件的 icon
才亮起来, 点击 icon
才会展现 popup 页面
.
这个性能的实现思路比拟多, 这里讲两种
一种是通过监听 conect 事件, 在事件的解决办法中 setIcon
和初始化对应的 popup
页面.
Vimium 是这么做的, 能够看这里.
还有一些利用 chrome
的onPageChanged
事件的规定来实现, 这种解决只有在合乎规定的时候才展现 popup
页面.
chrome.runtime.onInstalled.addListener(function() {chrome.declarativeContent.onPageChanged.removeRules(undefined, function() {
chrome.declarativeContent.onPageChanged.addRules([{
conditions: [
new chrome.declarativeContent.PageStateMatcher({
pageUrl: {hostContains: 'qzone.qq.com'}
})
],
actions: [new chrome.declarativeContent.ShowPageAction()]
}]);
});
});
操作插件 (点击或者别的操作) 没有响应
这种状况要注意插件治理页面你的插件那里有没有报错提醒, 像这样
这里的报错必须点进去革除了, 能力持续向下进行, 不然就会呈现操作没有响应的状况.