借 Redirect-Skipper 2.0 发布来「聊聊产品边界」

1,087 阅读6分钟

前言

上个月我因受不了掘金、知乎等网站的中转页面而潦草实现了一个跳过此类页面的浏览器插件:

并且将文章发布在掘金:《 脱裤子放屁 - 你们讨厌这样的页面吗?》,很高兴受到较多掘友的喜欢。

当时的实现思路比较粗糙,是通过修改页面上的 A 标签的 href 属性来达成的。


function findByTarget() {
  if (!hostnames.includes(location.hostname)) return;
  const linkKeyword = "?target=";
  const aLinks = document.querySelectorAll(
    `a[href*="${linkKeyword}"]:not([data-redirect-skipper])`
  );
  if (!aLinks) return;
  aLinks.forEach((a) => {
    const href = a.href;
    const targetIndex = href.indexOf(linkKeyword);
    if (targetIndex !== -1) {
      const newHref = href.substring(targetIndex + linkKeyword.length);
      a.href = decodeURIComponent(newHref);
      a.setAttribute("data-redirect-skipper", "true");
    }
  });
}

后来评论区有位掘友分享了他的同款插件,做的很完善,当下我就决定使用他的插件,弃用自己的。

image.png

他的插件使用了 onBeforeNavigate 来监听页面跳转,相对于通过修改 A 标签的方式它属于后发制人

高明之处在于它不需要修改 document 里的任何元素,只在浏览器即将打开新页面的时候进行判断,如果是中转页则将其更新成目标页面

而通过修改 A 标签的方式需要预先判断到 href 里面的地址是中转地址,然后进行替换。才能实现用户在点击 A 标签时直接进入目标页面。以至需要监听页面导航变化、DOM 更新来及时查找符合条件的 A 标签进行 href 属性的替换。

导致有如下的代码:

function observerDocument() {
  const mb = new MutationObserver((mutationsList) => {
    for (const mutation of mutationsList) {
      if (mutation.type === "childList") {
        if (mutation.addedNodes.length) {
          replaceALinks();
        }
      }
    }
  });
  mb.observe(document, { childList: true, subtree: true });
}

高下立判 onBeforeNavigate 实在是优雅!

而且通过修改 A 标签的方式还无法覆盖全部场景,因为有掘友反馈 CSDN 存在不带前缀地址的外链 A 标签。

截屏2025-05-28 15.13.01.png

(注意看 控制台-元素面板下的 a,它是只有外链地址的)

onBeforeNavigate因为是后发制人的思路,所以依然可以拦截到 csdn 的中转链接。

于是我当下就安装了掘友分享的插件,打算弃用自己潦草实现的。

产品边界

在体验了一番掘友的插件之后,我发现该插件做的太全了,而这个太全对我来说是个贬义词。

我喜欢小而美的产品,比如音乐软件我喜欢用 Apple Music,包括输入法,备忘录,日历,邮件等应用我都选择 Mac 内置的。理由无他,就是喜欢简洁且只专注核心功能的应用。

回到插件本身,它就是一个帮助用户自动跳过中转页,直达目标页面的浏览器插件,在它实现了该功能之余,不应该有其他任何分散注意力的东西。

最佳状态是用户安装了这个插件之后,甚至忘记它的存在,降低插件的存在感。

此处安利一个 iOS 的应用——熊猫吃短信,它就是只实现核心功能,安装之后就退居后台,几乎没有存在感的一款应用。我觉得所有应用都应该有这样的觉悟,做好本职工作,不要过多追求存在感。

而掘友的插件在实现了核心功能之余,还提供了主题切换数据统计等我觉得不必要的功能。(特别是当场景定位在 一个帮助用户自动跳过中转页,直达目标页面 的浏览器插件时)

虽然这些多余的功能不影响核心体验,但是总觉得膈应。因为本来少量代码量就可以完成需求时,它背后可能运行了更多我不需要的代码,对于有洁癖的人来说是很难接受的。

局部便利 vs 全局多余

有的功能是当你聚焦在功能本身的时候,会觉得它有需要。但是当你把焦点拉远,只看全局的时候,又会发现它是多余的。

比如 2.0 设计之初,我也打算在浏览器的右键菜单里注入一个选项(添加到跳过列表),点击之后可以快速的弹出操作面板,用来新增跳过名单。

截屏2025-05-28 15.52.49.png

如果只看这个功能,是觉得挺好的。当用户发现一个未被拦截的中转页面时,右键就可以立刻添加。

但是! 我们在平时浏览网页的大多数情况下,会有几次需要用到这个便利的菜单呢? 常见的中转站已经由插件收录到数据库了,只有少数情况下需要用户添加。而且在这样的少数场景下,用户点击插件图标就可以弹出操作面板。

也就是这个注入的右键菜单,只有极少情况是便利的,大多数情况是碍眼的。

于是我就将该功能删除了。

如果浏览器插件都这么随意注入右键菜单,那么很快我们的右键就要泛滥了。我不想成为它泛滥的原因之一。

保持简单

对于这样一个功能简单的小插件,我是不希望它的开发工程有很多复杂性。所以一切都是原生开发,没有引入任何框架和库,保持最简明的项目结构,没有任何 npm 包的依赖。

基本做到任何时候回头看,都可以快速理解业务代码(甚至不用 npm install)。

说实话掘友的项目工程略微复杂,我初次看代码的时候没看懂插件是如何构建出来的,后面才发现他用了 plasmo 这个框架。(当然对于掘友来说可能这个框架他轻车熟路了)

可能在开发复杂项目的时候有用吧。对于核心代码就百来行的小插件,我是不愿意接受引入框架的成本的。

拿来吧你!

所以,由于纯粹的个人主观偏好,我最终放弃使用掘友推荐的插件,而是将自己的插件升级了一番。

把掘友做得好的地方借(chao)鉴(xi)过来,甚至觉得掘友的 README.md 写得很漂亮也直接拿来吧你!

最终 Redirect-Skipper 2.0 实现了如下功能:

  • 也用了 onBeforeNavigate 来实现页面跳转
  • 也可以让用户添加自定义网址

没了! 是的,就这样。

下面是最终的效果:

demo.png

哦,不对,我增加了一个模糊匹配的功能。因为靠挨个收集中转页面的方式总是有疏漏,所以我开放了一个模糊匹配的选项。

如果开启该选项,当插件判断到 疑似中转页 则会尝试解析目标地址,如果顺利解析,就会进行直接跳转。

image.png

结尾

依然感谢 掘友的同款插件 开阔了我的知识面,我本来完全不知道 onBeforeNavigate 这样的 API。

也感谢他的漂亮的 README.md 让我了解到一些课外知识,比如:

Chrome Web Store Users Chrome Web Store Version Chrome Web Store Stars

这种酷炫的小图标(数据是实时的)。

如果你不介意大而全,那么你依然可以安装 掘友的同款插件

如果你和我一样喜欢小而美,那么推荐你安装我的 Chrome 安装地址

项目仓库:Redirect-Skipper

以上,感谢阅读!!