Bilibili-Evolved 组件源码解析:禁用评论区搜索词链接(disable-search-link)
【免费下载链接】Bilibili-Evolved强大的哔哩哔哩增强脚本项目地址: https://gitcode.com/gh_mirrors/bi/Bilibili-Evolved
导读
本文以 Bilibili-Evolved 仓库中的「禁用评论区搜索词」组件(disable-search-link)为切入点,完整解析该功能从需求到实现的技术细节:它如何兼容新版(V3)与旧版两种评论区结构、如何借助 Shadow DOM 事件与事件拦截双通道分别处理搜索词链接、如何用!important样式兜底,以及组件的加载/卸载生命周期设计。读完本文,你既能直接上手启用该功能,也能理解 Bilibili-Evolved 评论区组件与 Shadow DOM 观察体系的核心工作原理,为编写同类评论区增强组件提供可复用的实现范式。
一、功能是什么
在哔哩哔哩的评论区中,部分评论内容里包含的搜索词会被渲染为可点击的跳转链接(旧版评论区表现为.jump-link.search-word与.icon.search-word元素,新版评论区表现为<a>const name = 'disableCommentsSearchLink' let prevent = false export const component = defineComponentMetadata({ name, displayName: '禁用评论区搜索词', instantStyles: [ { name, style: () => import('./disable-search-link.scss'), important: true, }, { name, style: () => import('./disable-search-link-shadow.scss'), shadowDom: true, }, ], tags: [componentsTags.utils, componentsTags.style], entry: async () => { /* ... */ }, reload: () => { prevent = true }, unload: () => { prevent = false }, })
几个值得注意的元数据细节:
instantStyles与两套样式:组件声明了两份即时加载的样式。第一份 disable-search-link.scss 作用于普通 DOM,并标记important: true(生成规则时自动追加!important);第二份 disable-search-link-shadow.scss 设置了shadowDom: true,用于注入到 Shadow DOM 内部——这是新版评论区(Web Component + Lit 渲染)所必需的。tags归属:组件被归类为utils(工具)与style(样式)两类标签,说明它本质上是"以样式为主、辅以事件拦截"的增强组件,在设置面板中按这两个标签均可检索到。- 模块级状态
prevent:组件使用模块级布尔变量prevent作为"是否启用拦截"的开关,由entry(置为true)、reload(重新置为true)、unload(置为false)三者协同维护,避免重复监听。
三、前置基础:Bilibili-Evolved 的评论区抽象
要理解该组件的实现,需要先了解仓库中评论区的抽象体系。src/components/utils/comment-apis.ts对外暴露了统一的遍历 API(comment-apis.ts):
/** 为每一个评论区执行操作 */ export const forEachCommentArea = (callback: CommentAreaCallback) => { commentAreaManager.forEachCommentArea(callback) }其底层CommentAreaManager通过allMutations(全局 MutationObserver)监听整个文档,识别选择器.bili-comment, .bb-comment, bili-comments匹配的节点作为评论区(comment-area-manager.ts),并交由工厂函数getCommentArea分派到具体实现(comment-area.ts):
export const getCommentArea = (element: HTMLElement): CommentArea => { if (CommentAreaV3.isV3Area(element)) { return new CommentAreaV3(element) } if (CommentAreaV2.isV2Area(element)) { return new CommentAreaV2(element) } return new CommentAreaV1(element) }由此可知仓库将评论区划分为三代:
| 版本 | 判定方式 | 结构特点 |
|---|---|---|
| V3 | element.tagName.toLowerCase() === 'bili-comments' | 新版 Web Component,内容渲染在 Shadow DOM 内,使用 Lit 框架(见 areas/v3.ts) |
| V2 | isV2Area(element) | 旧版结构(.bili-comment等),DOM 直接可见 |
| V1 | 兜底 | 更早期的评论区结构 |
「禁用评论区搜索词」组件的entry正是基于area instanceof CommentAreaV3的判断,将新版与旧版评论区拆成两条完全不同的处理路径。
四、核心实现:两条处理路径
4.1 新版评论区(V3):Shadow DOM 内的属性剥离
新版评论区使用 Web Component,其内部 DOM 位于 Shadow Root 中,普通 MutationObserver 与事件委托无法直接触达。组件针对 V3 的路径(index.ts):
const isV3Area = area instanceof CommentAreaV3 if (isV3Area) { area.commentAreaEntry.addEventListener( ShadowRootEvents.Updated, (e: CustomEvent<MutationRecord[]>) => { const records = e.detail records.forEach(record => { record.addedNodes.forEach(node => { const isCommentLink = node instanceof HTMLAnchorElement && node.getAttribute('data-type') === 'search' if (!isCommentLink) { return } node.removeAttribute('href') node.removeAttribute('target') }) }) }, ) }这里依赖两个关键设施:
ShadowRootEvents.Updated:这是仓库 shadow-root 模块自定义的 DOM 事件(见 v3.ts 中同类事件的使用方式),其detail携带MutationRecord[],由ShadowDomObserver在监听 Shadow DOM 变更时派发。组件监听该事件即可感知 V3 评论区内部的节点增删,无需自行观察 Shadow Root。data-type="search"特征:新版评论区渲染搜索词链接时,会在<a>元素上标记data-type="search"。组件以此作为唯一判定条件,命中后直接执行removeAttribute('href')与removeAttribute('target')——href被移除后链接不再可跳转,target被移除后即使残留样式也不会新开标签页。
这种"观察新增节点 + 特征属性判定 + 属性剥离"的方式是典型的新版评论区处理范式,同类组件(如 copy-link)也遵循forEachCommentArea+ Shadow DOM 事件的架构。
4.2 旧版评论区(V1/V2):事件拦截
旧版评论区没有 Shadow DOM 隔离,搜索词链接是普通可点击元素。组件使用preventEvent在捕获阶段拦截点击(index.ts):
preventEvent(area.element, 'click', e => { if (!(e.target instanceof HTMLElement) || !prevent) { return false } const element = e.target as HTMLElement if ( ['.jump-link.search-word', '.icon.search-word'].some(selector => element.matches(selector), ) ) { return true } return false })- 选择器集合:
'.jump-link.search-word'是旧版评论区搜索词跳转链接的 class 组合;'.icon.search-word'则对应其附带的小图标(放大镜图标),两者都会被拦截。 - 回调语义:
preventEvent的回调返回true表示阻止该事件(阻止默认行为与冒泡),返回false表示放行。这里通过prevent开关兜底——组件卸载后即便回调残留,也会因prevent === false而放行事件,保证卸载干净。
4.3 两条路径的对比
| 维度 | V3(新版) | V1/V2(旧版) |
|---|---|---|
| 技术手段 | 监听ShadowRootEvents.Updated,操作MutationRecord | preventEvent捕获阶段拦截click |
| 判定依据 | node instanceof HTMLAnchorElement &&>.search-word { &.icon { display: none !important; } // 需要比夜间模式的优先级高 .reply-warp .reply-item &.jump-link { color: inherit !important; cursor: text !important; } }
Shadow DOM 样式(disable-search-link-shadow.scss):
可见该组件在"功能上剥离跳转"与"视觉上还原纯文本"两个层面做了双保险,即使某条路径(如新版评论区的事件未及时触发)暂时失效,样式兜底也能保证视觉上不再呈现为可点击链接。 六、生命周期与卸载安全性组件通过三个钩子维护状态:
七、如何启用与验证该组件随 Bilibili-Evolved 主脚本打包发布,用户无需单独安装:
验证要点(供开发与回归测试参考):
八、总结与可复用经验「禁用评论区搜索词」是一个小而完整的评论区增强组件范本,其可复用的工程经验包括:
参考实现文件:组件入口 index.ts、普通样式 disable-search-link.scss、Shadow 样式 disable-search-link-shadow.scss、评论区抽象 comment-apis.ts 与 comment-area-manager.ts。 【免费下载链接】Bilibili-Evolved强大的哔哩哔哩增强脚本 创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考 |