第一次 Chrome 插件开发经历,有点心痛

背景

不知道各位在看Axure产品原型的时候有没有遇到过这样的问题,在打开产品经理发给我们的原型地址后,找不到当前页面对应的导航菜单,特别是当原型页面比较多,左侧的菜单目录也特别多的时候。因为Axure在初次加载时不会定位到对应的菜单,也不会折叠其他目录,这就导致每次切换页面时都要在左侧菜单栏滚动很久才能找到菜单,属实恶心。

像这种页面体验上的问题,对于一个强迫症前端来说,自然是不能忍的。

思考

后面分析了一下左侧菜单栏,其实每次打开页面后菜单已经是高亮选中的状态,问题只是没有自动将当前选中的菜单滚动到可视区域而已。现在需要做的就是在初次加载完成后,找到选中的菜单节点,通过滚动API使其滚动到可视区域就OK了。

So Easy!

先F12一下,找到高亮的菜单节点元素,然后控制台输入$0.scrollIntoView({ behavior: 'smooth', block: 'center' })回车。“咻”地一下,那个高亮的菜单便以极其优雅的身姿滚动到菜单栏的中央,太帅了,一行代码完美解决!

好了,本文到此结束!

开玩笑,都到这一步了,自然是要将这些步骤写成一个脚本,来自动执行这些动作。至于这个脚本丢在哪里?可以是F12下的控制台,也可以放在类似油猴的插件里。

但是,我更想做成一个Chrome插件,为什么?因为没做过。

实现

核心代码其实已经实现了,不过要作为一个完整的插件发布到谷歌商店,还是需要完善和准备一下的。

插件首先需要提供一个manifest.json清单文件。在文件中,我们可以对插件标题、版本、图标等信息进行描述,其次也需要指定脚本的入口content_scripts字段,要发布到商店还需求指定相关的权限,以及匹配的URL。

关于更多详细的说明肯定是要去谷歌官方文档的啦!这里简单看一下content_scripts字段配置:

1
2
3
4
5
6
7
8
9
10
11
12
{
"content_scripts": [
{
"matches": [
"<all_urls>"
],
"css": ["location.css"],
"js": ["location.js"],
"run_at": "document_idle"
}
],
}

这里使用<all_urls>主要是无法确定使用者的原型文档的域名,如果仅仅是自己使用,直接指定域名即可。location.css主要是对样式进行了一些优化,执行的动作代码全部都在location.js文件中。

先简单的看一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// location.js
async function run() {
// 1. 等待页面DOM加载完成
const result = await axureReady();
if (!result) return;

// 2. 找到当前高亮菜单节点
const { selectedItem, toolBar } = result;

// 滚动到可视区域
selectedItem.scrollIntoView({ behavior: 'smooth', block: 'center' });
}

try {
await run();
} catch (err) {
//
}

这里需要注意一下,我们上面已经指定了document_idle,也就是在DOM加载完成后才执行脚本,为什么我们还需要在代码中等待DOM加载完成?

这是因为Axure的左侧菜单是异步渲染的!!!在脚本文件中直接用document.querySelector是拿不到的。

问题好像又变复杂了,我们怎么知道何时渲染完成呢?应该等多久?

打开控制台,无意中发现了Axure的内部渲染框架:

window.$axure

心中一喜,由于没有使用文档,只能逐个去找了,一圈下来,好像并没有发现有提供一个类似的钩子函数。

思前想后,还是用最简单最暴力的方法吧,循环去读取菜单的DOM

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function axureReady() {
let MAX_TRY_COUNT = 100;

return new Promise<{ selectedItem: Element } | null>(async (resolve) => {
let timer = setInterval(() => {
// 避免不可预知情况导致无限循环
if (MAX_TRY_COUNT <= 0) {
clearInterval(timer);

resolve(null);
}
MAX_TRY_COUNT--;

const selectedItem = document.querySelector('#sitemapTreeContainer .sitemapHighlight');

// 保证左侧导航数据已加载完成
if (selectedItem) {
clearInterval(timer);

resolve({ selectedItem });
}
}, 500);
});
}

虽然是暴力解决,但也没有太极端,这里限制了500ms读取一次,最多循环100次。也就是给了50秒的加载时间,我想再慢再卡的网速应该也差不多了吧。

完善

核心功能已经完成,为了不让功能太过单一,后面又扩展了几个比较实用的小功能。

  • 默认折叠其他菜单项,整个菜单再多看起来也十分清爽;
  • 在页面中嵌入一键定位与折叠的手动操作按钮,这样便于菜单凌乱后能够一键定位和折叠;
  • 支持开启与关闭此插件,因为插件的匹配机制是全匹配,为了不必要的性能消耗,插件默认在任何网站中禁用,需要的时候手动开启。

完整的代码看这里:https://github.com/moohng/axure-location-extension

上架

后来上谷歌插件商店的时候才发现,注册开发者居然的收费的,也能理解,为了过滤一些低质量的插件(比如这个)。既然都到这一步了,此时放弃太浪费感情了。好在收费不算多,只好安慰自己等后面搞一个收费的插件把这笔钱再挣回来吧,虽然不太可能。

然后就是对插件的描述,配图等,作为一个写代码的,准备这些东西花费的时间都够写几个这样的插件了。考虑到是上架的东西,为了避免可能的侵权,最后还是决定自己用UI工具画了一个蹩脚的logo图标。主图就自己截了一个图片,马马虎虎就这样吧。

不出所料,第一次审核肯定是通不过的,只能每次打回来再根据要求重新填写。差不多两三次吧,最后还是上架成功了,毕竟的交了钱的。

总结

目前为止,插件已经上架好几个月了,没去推广,也没几个人使用。这里写出来一是记录一下自己做插件的一些经历,也顺便让有需要的朋友体验一下

不过还是想在谷歌商店把那几十块钱挣回来,心痛!

最后附上插件地址:https://chromewebstore.google.com/detail/axure-location/cdlifpialfjnhfofnamojepppmlnpbkj


第一次 Chrome 插件开发经历,有点心痛
https://codingmo.com/article/20241111/5919209bc501/
作者
颜漠笑年
发布于
2024年11月11日
许可协议