SwitchyAgain 开发杂谈(五)能力

SwitchyAgain 作为一个扩展程序,其能力的上限主要会被两方面所限制住。第一就是扩展规范的版本,即 MV2 和 MV3。第二就是浏览器内核,以 Firefox 和 Chrome 为主要的平台来考虑。有再多的开发需求,都无法越过这两方面的限制。

扩展规范规定了如何去调用浏览器所提供的 API 接口,而不同的浏览器内核则是提供了不同类型的 API。明确这一点是非常重要的,直接能够确定,什么功能是可以去做的,什么功能无法做到。

SwitchyAgain 本质上,就只是在调用 API 提供一个便携的操作界面而已。扩展程序自身的功能,都只是浏览器自身就有的,只是浏览器自身没有去做出这些面向用户的更适合的操作逻辑和界面,但只要有提供 API 接口,扩展程序就可以调用 API 来实现更简便、更丰富的操作。

先聊聊 MV2 和 MV3。MV2 存在了很多年,这个版本的扩展规范给扩展程序赋予了非常丰富的调用资源。最厉害的一个例子就是 Ublock Origin,把 MV2 针对网页资源的加载操作的能力发挥到了极致。由此引申出来,MV3 之于 MV2,到底是否有存在的意义?

MV3 各方面来说,就我的认知中,更多的还是在限制 MV2 的能力。因为 MV2 能力太强,授予的操作几乎无所不能。这也符合当时的那个环境的一个需求,尽可能把浏览器的能力给开放出来,这样才能形成一种生态。到了 MV3,生态建立好了,扩展程序也多了,就以安全为由给限制能力了。

还有一点,面对过多的扩展程序,MV3 设计成了会去释放扩展程序所分配到的资源,从而减少资源消耗。即不运行的空闲的扩展程序会被回收资源,再次被调用时才重新被激活和分配资源。这一点我很难去评价是否更合理。对于 UbO 这种大受欢迎的扩展程序来说,因为这种限制就无法完全迁移到 MV3,只能停留在 MV2。各个方面来看,MV3 面向的,就是能力弱化、无法实现更多功能的扩展程序。

在了解了两个不同版本的扩展规范的区别后,也就不难理解为什么 SwitchyOmega 在 MV3 下会出现很多难以理解的问题。首先就是点击在工具栏的扩展图标,无法弹出菜单,需要再点击一次才出现菜单。这就是扩展程序被释放了资源后,无法立刻被重新分配资源,从而必须点两次,才能开始工作。

从 SwitchyOmega 迁移过来时,针对这个问题我在 AI 的辅助下有思考过很多的解决方案。但其实对于这种一直都要运行的扩展程序,我是很难理解要设计成这种闲时就必须要释放资源的设计的。那它就是要一直运行的,反反复复去释放又分配,显得很没必要。

但这就是扩展程序的能力限制。扩展程序只能去根据扩展规范来调用 API,有再多的情绪也改变不了这样的一个事实。所以就只能这样去接受了。好在 SwitchyAgain 仅仅只是一个针对代理的设置调整而已,涉及到的相关的 API 并没有很多。这里还涉及到了 Service Worker,是和 MV3 一起的配套方案。

最后还是老老实实按照 MV3 的设计,调整成了适合闲时被释放、再次被使用马上就分配的方案。总体来说,这个时间差不会太明显,但在性能弱的机器上,就和一直在运行做对比,这种反复调用拉起,应该是会有影响的。就这么一个看起来好像很简单的流程,我还是专门做了多轮优化,以达到一种并没有察觉到的无感设计。

那么在浏览器内核方面,这个比扩展规范更有意思。很多事情真的只有在开始做了才会有非常清晰的认识,而不是停留在文字上的那种冰冷的感受。Firefox 和 Chrome 虽然都支持 MV3,但是它们本身的功能就有很大的差别,因此即使使用 MV3,也还是要做针对性的开发设计。

对于 Chrome 来说,它的内核当然是优异的,尤其是 MV2 的时代,涌现了一大批优秀的扩展程序,这和它的内核是分不开的。但是到了 MV3 的时代,Chrome 似乎变得保守了。它的代码当然仍然有开放,但是它所开放出来的 API 接口却并不丰富。也就是代码的确写好了,但只打算给它自己使用。看起来好像是为了安全考虑,但有好多的功能,和安全根本不沾边,我觉得要么是就不想开放出来,要么就是没有这个能力去写 API 接口。

举几个例子,比如说获取到当前标签页的 ID 或者说一个身份定位的标记,以及标签组的身份定位标记。还有原本有增加的但后来被删除掉的,可以针对一个 URL 的 PATH 做规则匹配,这个是考虑到 HTTPS 如果也能这样做就失去了 HTTPS 的所谓安全的意义,于是自动忽略 PATH 部分,只能识别到开头的主机部分。主动去弱化了这种功能。然而这几点,Firefox 都是支持的,甚至还扩大了,这么多年并没有出现所谓的安全方面的重大新闻。

因为 Chrome 这种对 API 接口的限制,导致 SwitchyAgain 在开发的过程中,就会很被动。因为还需要支持 Firefox 的正常运行以及最大限度去发挥其应有的能力,那么 Firefox 有这非常丰富的 API 接口设计,除了前述的当前标签页的识别、标签组的识别、URL 的正则匹配,也能针对其特有的容器标签页做识别,特定的某个网站或网址。于是,就可以在一个浏览器里,不同的标签页打开同一个网站,却可以通过不同的代理来访问。

这对于 Chrome 来说根本做不到,因为 Chrome 没有提供这些 API 接口。最多的只能是,针对普通窗口和隐私窗口,分配不同的代理方案。不然就只能新开一个 Profile 用户资料,但这个就完全是新的不同的一个浏览器用户了,无法直接继承原有的默认用户的所有设置以及安装的扩展程序。这种残次品使用起来真的太难受了。

但因为 Chrome 占据了绝对的市场占有率,又因为原项目本身就是针对 Chrome 做开发,甚至没打算适配 Firefox,于是还是做了针对性的适配设计。所以实际上从 SwitchyOmega 到 SwitchyAgain,开发的适配重心是从 Chrome 变成了 Firefox。Firefox 对开发者的各种友好的设计,跟 Chrome 进行对比,的确很容易就博得超高好感。

最后,由于还是必须要同时支持 Firefox 和 Chrome 的,所以虽然它们有使用不同的 API 接口,但都写到了代码里,于是把它们的入口从原本的两个合并到了一个。也就是不再预设成两个入口,都统一先走同一个入口,进去后才再去区分。我很难说这个是一种优化,但对于一个打算还能继续再用十年甚至更久的扩展程序来说,这样的设计感觉会更合理和更清晰。

很多设计都必须要考虑到未来的走向,这也是 SwitchyAgain 完全移除对 MV2 的支持的原因,没有看到很明显的劣势,那就是跟上时代的发展,与时俱进保持其该有的活力。另一方面,也是选择 TypeScript + React 的原因,可以看到这必然是未来的主流,不会那么轻易就过时。但是同时,仅就对于 SwitchyAgain 的核心功能来说,代理切换本身就没有更多的需要去完善的设计了,在很长的一段时间里,应该都不会有如此频繁的重构迁移和增加新功能了。

(完)