chrome 扩展开发(2)
上一篇: chrome 扩展开发
书接上回
上一篇大概介绍了 chrome 扩展的基本机制。基本上就是 3 个部分(还有一些其他的, chrome 开放能力相关的页面,就先不讲了)
- 一个后台常驻的页面,类似于本地的 server
- content 页面,可以插入到普通网页中执行的代码,有页面内嵌代码的权限。
- popup 页面,点击插件按钮弹出的弹层,可以提供一些交互和展示功能。
举例的插件原理很简单,content 页面上报数据,background 统计,popup 负责展示结果。 但是如果真的像上一篇所讲的那样写,那使用这个时间统计插件的时候,就会发现一些问题。 那就是计时是会有很大的误差(取决于你的浏览器有多卡,我的 chrome 一分钟下来能差 5 秒左右)。
通信机制
问题其实就出在统计时使用的通信机制上。要统计特定页面打开的时间,如果不能获取其关闭时间, 那就只能采用发心跳包的方式。像之前那种方法,其实是很不靠谱的,因为 js 的定时器, 并不能保证时间周期的严格准确,而 background 页面又根据心跳包来进行统计, 自然会出现偏差。
当然,使用心跳的方式,也可以做好这件事情。只是 background 页面中须要加入一些额外的逻辑, 总体来说还是去重,对每个页面维持一个类似长连接的结构,设置心跳的过期时间, 再考虑上一些后台页面被挂起的边界情况之类的,应该也是 OK 的。
但其实是不用我们自己维持这样的长连接结构的,Chrome 26 之后的版本中,都提供了官方的长连接机制, Chrome 官方文档可以参考这里
PS: 看惯了 MDN 的文档之后,不得不吐槽 chrome 这个文档,搜索和目录都做得特别差。 这时候切实体验到了 typescript 的优势,types/chrome 的描述文档用起来都比官方文档方便。
创建连接的方法如下
// 在 content 页面中
let connectPort = chrome.runtime.connect({
'name': 'connect name'
});
connectPort.postMessage({
// 发送的信息
});
// 在 background 页面中
chrome.runtime.onConnect.addListener((externalPort) => {
// ...
// 在建立连接时执行的代码
// ...
externalPort.onMessage.addListener((port) => {
// 收到消息时执行的代码
});
externalPort.onDisconnect.addListener((port) => {
// 在断开连接是执行的代码
});
});
这样即使我们没有关闭页面的事件,也可以简单地使用 connection 的 disconnect 事件来代替。
时间统计
使用这样的通信机制之后,在 background 统计时间的方法就很简单了。只要记录每个页面连接的起始时间, 还有断开连接的时间,就可以了。当然为了同时打开多个页面的时候不重复计算,须要做一点点去重处理。 大概是这样
background 页面
/** 当天累计已稳定时间 */
let totalTime: number = 0;
/** 开始计时时间 */
let startTime: number = 0;
// 处理页面链接
chrome.runtime.onConnect.addListener((externalPort) => {
// 记录页面连接
connectionList[String(externalPort.sender.tab.id)] = {
startTime: Number(externalPort.name),
url: externalPort.sender.tab.url,
title: externalPort.sender.tab.title
};
// 设定本段计时开始时间
if (!startTime) {
startTime = Number(externalPort.name);
}
// 断开连接
externalPort.onDisconnect.addListener((port) => {
delete connectionList[port.sender.tab.id];
if (!Object.keys(connectionList).length) {
// 没有活动的连接了
totalTime += new Date().getTime() - startTime;
startTime = 0;
}
});
});
// 页面输出
chrome.runtime.onMessage.addListener(function msgListener(req, sender, res) {
if (req.type == 'get time') {
let exTime = startTime ? new Date().getTime() - startTime : 0;
// 输出当天累计时间
res({
// 累积时间加当前还活跃的连接时间
time: (totalTime + exTime) / 1000,
});
}
});
content 页面
let startTime: number = new Date().getTime();
let connectPort = chrome.runtime.connect({
'name': String(startTime)
});
具体可以看这里, 额外加了历史纪录,按天切分时间统计之类的一些小逻辑
Summary
这次试着用 TypeScript 和 VSCode 开发,感觉真是爽到啊。完美从vim 切换过来了, 除了折叠功能不是很正常之类的几个小问题,总体来说比用 ctag 和Ctr-n 的方式方便很多。 特别是像这种三方库的文档功能和只能提示,体验很棒。
果然前端的工程化还差得远啊,还是微软搞的牛逼,语法分析确实才是更进一步的基础, 单纯地拼接文件来实现的模块化,还是太简陋了。