
Bilibili-Evolved 复制评论链接功能解析从评论区菜单到锚点定位的完整实现【免费下载链接】Bilibili-Evolved强大的哔哩哔哩增强脚本项目地址: https://gitcode.com/gh_mirrors/bi/Bilibili-Evolved导读复制评论链接是 Bilibili-Evolved哔哩哔哩增强脚本中一个非常实用的工具型组件开启后每条评论的菜单中都会新增复制链接选项一键即可把当前评论的精确锚点链接写入剪贴板方便分享或回帖引用。本文以该组件的功能文档与源码为主线剖析其工作流程——如何把复制链接按钮注入评论菜单、如何隐藏 B 站原生的同名菜单项、如何在动态页与普通评论区生成不同的链接格式并顺带讲解其背后依赖的评论区统一抽象层CommentArea/CommentReplyItem等基础设施。读完本文你既能掌握该功能的实际用法也能理解增强脚本扩展 B 站评论区菜单的标准做法。功能一览组件注册信息位于 registry/lib/components/utils/comments/copy-link/index.tsexport const component defineComponentMetadata({ name: copyCommentsLink, displayName: 复制评论链接, entry, tags: [componentsTags.utils], })namecopyCommentsLink组件内部唯一标识displayName复制评论链接即设置面板中展示的名称entry组件入口函数页面加载后由脚本框架调用tags归类为componentsTags.utils工具类与评论区其他工具组件如content-replace、disable-search-link保持一致的分组。功能文档 registry/lib/components/utils/comments/copy-link/index.md 的描述只有一句话开启后可在每条评论的菜单中选择复制链接。——这正是该组件的全部交互入口而背后的实现细节则全部沉淀在entry函数中。入口流程如何把复制链接装进每条评论entry是异步函数组件被加载时执行const entry async () { const { forEachCommentItem, addMenuItem } await import(/components/utils/comment-apis) // ... forEachCommentItem({ added: addCopyLinkButton, }) }入口首先动态导入import()评论相关的 API随后调用forEachCommentItem注册一个added回调。这意味着每当页面上新增一条评论时addCopyLinkButton都会被调用为该评论及其回复注入菜单项。forEachCommentItem评论遍历的统一入口forEachCommentItem定义在 src/components/utils/comment-apis.ts它本身只是对评论管理器的转发export const forEachCommentItem (callbacks: { added?: CommentItemCallback removed?: CommentItemCallback }) { commentAreaManager.forEachCommentItem(callbacks) }而commentAreaManager是CommentAreaManager类的单例实例src/components/utils/comment-apis.ts在页面内容加载完成后初始化。其核心机制位于 src/components/utils/comment/comment-area-manager.ts通过allMutations监听整页 DOM 变化并扫描.bili-comment, .bb-comment, bili-comments等选择器识别页面中出现的所有评论区observeAreas每个评论区被包装为CommentArea实例并调用其observe()forEachCommentItem会把回调转发给每个评论区的forEachCommentItem最终逐条触发added/removed回调。因此即使评论区是异步加载的例如翻页、展开更多回复新出现的评论也会被自动捕获并注入按钮。addCopyLinkButton一条评论 它的所有回复const addCopyLinkButton (comment: CommentItem) { const processItems (items: CommentReplyItem[]) { items.forEach(item { // 1. 隐藏 B 站原生复制评论链接 if (item.parent instanceof CommentAreaV3) { item.parent.getMenuElement(item).then(menu { const originalItem menu ?.querySelectorAll(li) .find(li li.textContent 复制评论链接) as HTMLElement | undefined if (originalItem) originalItem.style.display none }) } // 2. 追加自定义复制链接菜单项 addMenuItem(item, { className: copy-link, text: 复制链接, action: async () { /* 见下文 */ }, }) }) } processItems([comment, ...comment.replies]) comment.addEventListener(repliesUpdate, e processItems(e.detail)) }这段代码透露了两个关键细节只对CommentAreaV3隐藏原生菜单项B 站新版评论区V3自带复制评论链接菜单项增强脚本选择隐藏它避免与自己的复制链接重复而对旧版评论区V1/V2则直接追加不去动原有菜单。回复也会被处理processItems([comment, ...comment.replies])把主评论与其全部回复一次性处理同时监听repliesUpdate事件——当评论区异步加载出更多回复时事件定义见 src/components/utils/comment/types.ts由CommentItem.dispatchRepliesUpdate派发见 src/components/utils/comment/comment-item.ts对新回复同样注入按钮。addMenuItem跨版本评论区的菜单注入抽象addMenuItem是评论区抽象层提供的标准接口src/components/utils/comment-apis.tsexport const addMenuItem ( item: CommentReplyItem, config: { className: string; text: string; action: (e: MouseEvent) void }, ) { commentAreaManager.forEachCommentArea(area area.addMenuItem(item, config)) }它会把菜单项配置广播给页面上所有已注册的评论区。而不同版本评论区的实现各有差异从源码结构看框架刻意用抽象类统一了接口抽象基类 src/components/utils/comment/areas/base.ts 声明了getMenuElement与addMenuItem两个抽象方法并规定菜单项配置为{ className, text, action }三元组V1 评论区areas/v1.ts在.opera-list ul中插入菜单项V2 评论区areas/v2.ts在.operation-list中插入菜单项且className已存在时跳过避免重复注入V3 评论区areas/v3.ts通过 Shadow DOM 入口查询菜单中的#options列表同样以li.${className}去重后追加。正是这套接口统一、实现分版的设计让copy-link组件可以用同一份代码兼容 B 站历代的评论区结构。链接生成动态页优先普通页兜底复制链接按钮的action是整篇实现的核心action: async () { const url findParentFeedsUrl(item.element) || document.URL.replace(location.hash, ) await navigator.clipboard.writeText(${url}#reply${item.id}) }生成的链接格式统一为页面地址#reply评论ID其中页面地址由findParentFeedsUrl决定若当前评论位于动态页向上遍历 DOM 找到带data-did属性的祖先节点取https://t.bilibili.com/did作为动态页地址——这样复制出的链接能直达该动态并定位到其中的评论其余情况普通视频、专栏、番剧评论区等使用当前页面 URL 并去掉location.hash避免残留旧的锚点再拼接#reply评论ID。findParentFeedsUrl的完整判定逻辑如下const findParentFeedsUrl (commentElement: HTMLElement) { if (document.URL.match(/\/\/t\.bilibili\.com\/(\d)/)) return if (feedsUrls.every(url !matchUrlPattern(url))) return let element: HTMLElement | null commentElement while (element ! null element ! document.body) { if (element.hasAttribute(data-did)) { return https://t.bilibili.com/${element.getAttribute(data-did)} } element element.parentElement } return }三个判定条件依次是已在动态详情页URL 匹配//t.bilibili.com/数字直接返回空串走兜底逻辑当前页 URL不在任何动态列表页通过feedsUrls与matchUrlPattern判断feedsUrls定义于 src/core/utils/urls.ts涵盖动态相关的 URL 模式不匹配则返回空串遍历祖先节点找data-did动态列表页中每条动态的容器元素都带有data-did属性其值即动态 ID据此拼接出精确的动态页链接。最后链接通过navigator.clipboard.writeText异步写入剪贴板。item.id是评论 IDCommentReplyItem的标准字段见 src/components/utils/comment/reply-item.ts#reply评论ID锚点正是 B 站评论区的标准定位方式收到链接的人打开后即可自动滚动到对应评论。使用与配置开启方式在脚本的设置面板中找到复制评论链接组件属于工具分类并开启即可无需任何额外配置参数。生效范围支持新版V3与旧版V1/V2评论区覆盖普通视频、专栏、番剧以及动态页等场景。使用姿势鼠标悬停任意评论点击菜单中的复制链接然后在任意地方粘贴即可分享该评论的精确锚点链接。实现要点小结要点说明依据组件注册defineComponentMetadata声明copyCommentsLink归类 utilsindex.ts评论遍历forEachCommentItem由CommentAreaManager在内容加载后统一驱动支持动态加载comment-apis.ts菜单注入抽象CommentArea.addMenuItemV1/V2/V3 各有实现className去重areas/base.ts、areas/v3.ts链接格式动态页用data-did生成t.bilibili.com链接其余用当前 URL统一拼#reply评论IDindex.ts剪贴板写入navigator.clipboard.writeText异步写入index.ts从整体设计看复制评论链接是 Bilibili-Evolved 评论区基础设施CommentArea抽象 CommentItem/CommentReplyItem数据模型 菜单注入 API的一个典型消费方组件本身只负责链接怎么生成而按钮装到哪里、何时装载完全交给评论区框架处理。这种分层让评论区相关的各工具组件如content-replace、disable-search-link得以共享同一套健壮的评论区识别与注入能力也是增强脚本能长期适配 B 站评论区改版的关键所在。【免费下载链接】Bilibili-Evolved强大的哔哩哔哩增强脚本项目地址: https://gitcode.com/gh_mirrors/bi/Bilibili-Evolved创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考