最近做了一个浏览器插件的项目,私以为还是比较有意思的,在此记录一下经历。

这个浏览器插件的主要功能是,提供一个拨号盘界面,可以进行呼入、呼出的功能。再额外添加一些围绕着呼叫的辅助功能,比如:联络记录,通讯录,弹屏等等。

WXT 介绍

为了提高开发效率,我直接查找了可以用的插件开发框架,经过一番搜索最终选择了 WXT。

它可以使用流行的前端框架进行开发,它是建立在 Nuxt之上的,而我对 Vue 还算比较熟悉,也就选择了 Vue 这条技术路线一路到底。

即使现在看来,整个开发过程的体验还是很轻松加愉快的。不足之处是,说明文档不是很全,有很多待补充内容,如果找不到想要的内容,只能到 issues 中寻找。

打开 WXT 的官方网站,找到 Get Stared,初始化项目,

1
pnpm dlx wxt@latest init <project-name>

命令执行完成之后,会生成一些模板代码,具体有什么用可以到官网查看 Link 。

WXT 遵循的是 约定优于配置,每个目录内的文件在最终编译时会转换为对应的文件。

例如:components 内的文件会转换为组件,background 内的文件会转为 ServiceWorker,*.content.ts 会转为 content scripts。

如果熟悉 Nuxt 的话,composables, hooks, utils 内的文件可以自动导入。像是在使用全局的函数,用起来很方便。

插件内的概念

在开始动手之前,需要先了解一下浏览器插件的概念,有助于开发。

popup 是用户点击插件图标时,弹出的窗口。这个窗口有大小限制,为 800x600。

这个窗口内的内容,可以理解为就是html。框架内编写的vue组件是在这里渲染的。对应 WXT 的 entrypoints 目录中的 vue 文件。

每次打开 popup 时,vue代码都会重新加载。如果想实现一些状态共享的功能,在 popup 关闭时,状态都会清空,所以需要存到 storage (后面讲)中在打开时再次读取。

background scripts

background scripts 是在浏览器后台运行的脚本。在 manifest v3 中,backgound scripts 就是 service worker.

对应 WXT 的 entrypoints/background 目录中的文件。

background scripts 不受跨域影响,可以监听来自 popup 和 content scripts 的消息。

content scripts

content scripts 是在浏览器页面中运行的脚本。在页面加载后会注入到页面中。可以操作 DOM 元素。

storage

storage 用来存储数据。类似 localStorage,使用时必须加上前缀,local:、session:、sync: 和 managed:。

与 localStorage 不同,storage 可以监听值的变化,也可以存储对象值。

storage 的数据可以在 popup,background scripts 和 content scripts 之间共享。

我把它当作 Redis 来用,暂时没发现姿势有什么不对劲。

message

message 用来在 popup,background scripts 和 content scripts 之间传递消息。

主要用法为

1
2
3
4
5
6
7
8
9
// 接受消息
browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
// do something
sendResponse();
// 如果发送端需要回复 要返回return true 表明是一个异步消息
return true;
});
// 发送消息
browser.runtime.sendMessage({message: 'hello'});

插件功能介绍

插件通话功能是由 jssip 实现的。页面上实现了登录注册,指定好后端地址,就可以使用通话功能了。

插件的页面组件使用了 vant.

拨号盘和话务功能

这部分我使用 vue 画了一个拨号盘的界面用于用户交互。话务功能 jssip 中有现成的函数,可以拨号,挂断,静音等,直接调用相关函数就可以了。

拨号盘界面需要注意的是,要根据不同的话机状态,显示不同的样式。

因为 jssip 要获取用户媒体和创建 Audio 用于播放铃声,这部分代码只能放到 content scripts 脚本中。

个人暂时觉得还是不太完善,在网上查资料可以有 offscreen.html 和 background.html 的其他方法,暂未成功。

popup 中的状态,在用户关闭后就会丢失,而且这个窗口很容易被关闭。每次打开时都需要从 content scripts 中同步状态,这也就用到了 storage 和 message。

来电通知使用了 notifications,如下代码。这一部分倒是没遇到什么问题,jssip 收到来电后,使用 message 发到 background scripts 就可以了。

1
2
3
4
5
6
browser.notifications.create({
type: '',
title: '',
iconUrl: '',
message: '',
});

为了加强插件状态的直观性,在通话和来电时改变图标,可以使用

1
browser.action.setIcon({});

除了 jssip 的 websocket 外,插件还需要一个新的 websocket 获取后台系统的消息,以实现弹屏功能。这一部分也放到了 content scripts 脚本中,暂时没遇到什么坑,轻松加愉快。

通讯录

通讯录可以直接使用 vant 内的组件和 storage 来实现。

在使用 storage 存储的时候需要注意,数据是插件内全部可见的。也就是即使我这个插件需要不同的用户先登录后再使用,如果用户A保存了通讯录,用户B登录后也能访问到。

所以在保存这些数据时,需要给数据打上标记,让用户只能访问自己建立的数据。

弹屏功能

弹屏功能是收到来电后,可以打开指定的网页内容。网页可以由用户自行填写,支持内容替换。这样在和其他CRM系统集成的时候,可以方便的调用第三方系统内的功能。

这一部分也比较简单,使用 message 然后在 background scripts 脚本中处理。

1
2
3
chrome.tabs.create({ url });
// or
chrome.tabs.update({ url });

国际化

这一部分使用了 vueI18n

可以参照 WXT 的示例

没有遇到什么坑点,唯一要注意的是,在 background scripts 中,要这么用

1
i18n.global.t('', {}, { locale: 'en' });

界面截图

screenshot-1 screenshot-2 screenshot-3 screenshot-4

总结

经过这个项目,我熟悉了浏览器插件的开发流程和概念。

幸好用了框架,没有让代码乱成一锅粥。

vue 生态中的资源也能很好的利用上。我想如果后续迭代的话,也很方便快捷。

——————
本文为个人原创,转载请署名且注明出处。