chrome extension 开发实战之 QQ 空间助手
SegmentFault
共 13617字,需浏览 28分钟
·
2021-03-02 13:19
TL;DR
背景介绍 需求分析 知识储备 文档推荐 结构组成 组件通信 开发调试 打包发布 问题解决
背景介绍
本文历时较久,2020年写了一半,写到迷茫,索性搁置,2021年翻出来继续写的;经过了半年多的时间再看,会有一些不一样的理解,存在部分配图风格不一致.但是我会确保内容的正确和表达的一致.
因为也是第一次写插件,所以记录这个过程,分享开发过程和期间遇到的问题,并做一些关键点的梳理.
所以,这并不是一篇大而全的chrome extension开发教程,这是一篇以QQ空间助手为实战来切入插件开发但是侧重讲chrome extension开发的文章.(注:文中有大量链接,建议点击阅读原文进行阅读~)
需求分析
实现上也很简单,只需要两个步骤.
获取说说或者留言列表 依次遍历id删除说说或者留言
知识储备
JavaScript HTML + CSS
chrome devtools的使用和基本的调试能力 文档阅读能力
文档推荐
接触新的技术,文档肯定是不可少的,但是网上各种教程 + 文档,鱼龙混杂,我们到底应该看教程还是看文档.
我个人理解是,不管教程还是文档,都可以看,但是要优先看官方的文档和教程,看一手的资料,因为我看文档的过程中发现,非官方的东西(非一手文档)信息传达不完整,甚至有纰漏.所以尽着官方的来,除非有很好的非官方教程或者其他的原因.
一篇文章教你顺利入门和开发chrome扩展程序(插件)(文章中的代码有笔误,不要直接拿来用) Chrome插件中 popup,background,contantscript消息传递机制
官方文档(推荐) | 官方Demo 推荐的非官方文档 插件通信相关辅助 一篇文章教你顺利入门和开发chrome扩展程序(插件) Chrome插件中 popup,background,contantscript消息传递机制 其他 Chrome插件开发全攻略 调试相关 | Debugging extensions 打包发布相关 | 创建和发布自定义 Chrome 应用和扩展程序
结构组成
UI组成
从上面的图上来看,主要由 icon(任务栏的图标) 和 点击之后的弹窗两个大件组成,部分插件还会有一个专门的用于配置的页面(这里没用到,就不介绍了).
再加上这两个重要的概念之后整个结构就是这样的
文件结构
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(按钮)
在chrome插件中,这种icon按钮是分两种的:
page 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
另外,从background发出去的请求可以跨域.
在V3的实现中,background引入了service worker的概念.
content_script.js
例如,点击插件的图标,然后页面改变颜色.这种情况是不能在background.js中直接改变页面颜色的,而是需要通过事件发送消息到content_script.js中,通过content_script来操作页面DOM.
了解完这些我们大概知道了,我们获取发请求的哪些参数包括发请求都可以在content_script中实现.因为只有之类可以操作页面的DOM.
组件通信
chrome extension的通信是通过事件来进行的,通信的内容是有效的JSON对象.共有三种通信方式:
Simple one-time requests (就像短连接) Long-lived connections (就像长连接) Cross-extension messaging (多个插件间通信)
插件内的通信分为这几种:
content script => background content script => popup background => content script background => popup popup => content script popup => background
chrome.runtime.sendMessage({greeting: "hello"}, function(response) {
console.log(response.farewell);
});
content script发送的事件popup和background都可以收到,所以再发送的时候需要加上发给谁的标识,然后收到消息的时候处理 popup => background的通信其实也可以通过chrome.extension.getBackgroundPage()来获取到background的所有方法,直接调用
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.
开发调试
开发时候如何运行插件
如何打开控制台
打开background的控制台
在chrome://extensions/页面,点击对应的插件背景页三个字即可打开background的控制台
打开popup的控制台
点击icon在弹出的popup页面上右键,然后点击检查,就可以打开popup的控制台.需要注意的是popup页面一旦关闭,控制台也会随之关闭.
打开content script的控制台
content script的控制台其实就是tab页的控制台,但是需要切换一下
如何debug
https://developer.chrome.com/extensions/tut_debugging
打包发布
更多详情可以参考文档创建和发布自定义 Chrome 应用和扩展程序
问题解决
chrome.tabs.query得到空的tabs
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.');
}
});
}
});
}
让插件只在特定页面可用功能实现
这个功能的实现思路比较多,这里讲两种
一种是通过监听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()]
}]);
});
});
操作插件(点击或者别的操作)没有响应
评论