尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

Bilibili-Evolved 动态图片平铺展示(legacyFeedsImageViewer)功能源码解析

发布时间:2026/9/19 11:45:06

资讯中心
01
ARTICLE

Bilibili-Evolved 动态图片平铺展示(legacyFeedsImageViewer)功能源码解析

Bilibili-Evolved 动态图片平铺展示(legacyFeedsImageViewer)功能源码解析
Bilibili-Evolved 动态图片平铺展示legacyFeedsImageViewer功能源码解析【免费下载链接】Bilibili-Evolved强大的哔哩哔哩增强脚本项目地址: https://gitcode.com/gh_mirrors/bi/Bilibili-EvolvedBilibili-Evolved哔哩哔哩增强脚本中的动态图片平铺展示组件用于将 B 站动态中左右切换式的图片查看器图片轮播/滑动切换样式改回传统的平铺展示方式让多条图片在卡片中直接纵向排列、一次可见避免在查看图片时被迫逐张切换。本文以该组件在仓库中的功能文档与 TypeScript 实现为核心结合动态卡片管理、Vue 数据读取等底层源码讲解其工作原理、生效范围、两种动态数据结构的处理逻辑以及它在动态详情页可能稍有延迟的原因帮助读者理解并复现这一典型的数据层样式修复型增强脚本组件。功能文档与组件定位功能文档位于 registry/lib/components/feeds/legacy-image-viewer/index.md全文对功能的描述非常精炼将动态中左右切换式的图片改回传统的平铺展示. (在动态详情中可能稍有延迟)这句话概括了组件的三个关键信息改造对象动态feeds卡片中的图片区域即 B 站新样式下采用左右切换式图片查看器 / 轮播渲染的多图动态改造方式改回传统的平铺展示即所有图片按顺序纵向平铺在卡片正文中已知限制在动态详情页/opus/{id}等页面中由于数据结构和渲染时机不同改造可能存在稍有延迟。组件注册代码位于 registry/lib/components/feeds/legacy-image-viewer/index.ts通过defineComponentMetadata定义见 src/components/define.ts声明元数据export const component defineComponentMetadata({ name: legacyFeedsImageViewer, displayName: 动态图片平铺展示, tags: [componentsTags.feeds], urlInclude: feedsUrls, entry: () { /* ... */ }, })name内部标识legacyFeedsImageViewerlegacy前缀表明其意图是恢复旧版legacy的平铺图片展示形态displayName用户在设置面板中看到的中文名称动态图片平铺展示tags归属动态分类componentsTags.feeds便于在设置面板按标签筛选urlInclude限定组件运行的页面范围值为feedsUrlsentry组件入口页面匹配通过后执行内部完成对每张动态卡片的图片样式改写。ComponentMetadata的urlInclude字段语义见 src/components/types.ts是设置匹配的 URL匹配才运行此组件因此该组件不会在其他无关页面加载执行。生效页面范围feedsUrlsurlInclude: feedsUrls引用的 URL 匹配表定义在 src/core/utils/urls.ts/** 含有动态的页面 */ export const feedsUrls [ ...feedsUrlsWithoutDetail, /^https:\/\/t\.bilibili\.com\//, /^https:\/\/www\.bilibili\.com\/opus\/[\d]$/, ]其中feedsUrlsWithoutDetailsrc/core/utils/urls.ts为/** 除动态详情页以外的含有动态的页面 */ export const feedsUrlsWithoutDetail [ /^https:\/\/t\.bilibili\.com\/$/, /^https:\/\/space\.bilibili\.com\//, /^https:\/\/live\.bilibili\.com\/(blanc\/)?[\d]/, ]合并后组件生效的页面包括页面URL 匹配规则说明动态广场/首页https://t.bilibili.com/动态时间线主页面个人空间https://space.bilibili.com/*用户主页中的动态 Tab直播间https://live.bilibili.com/{roomId}含blanc/前缀直播间页内嵌的动态卡片动态详情页https://www.bilibili.com/opus/{id}单条动态的详情页对应文档中稍有延迟的场景也就是说左右切换式图片改回平铺这一改造会作用于上述所有包含动态卡片的页面而不仅是动态主页。底层机制forEachFeedsCard 与动态卡片管理器组件入口的第一步是调用forEachFeedsCard该函数由动态卡片 API 提供定义在 src/components/feeds/api/manager/index.tsexport const forEachFeedsCard async (callback: FeedsCardCallback) { if (feedsUrls.every(url !matchUrlPattern(url))) { return null } const success await feedsCardsManager.startWatching() if (!success) { console.error(feedsCardsManager.startWatching() failed) return null } const { added } callback if (added) { feedsCardsManager.cards.forEach(c added(c)) } feedsCardCallbacks.push({ added: none, removed: none, ...callback }) return feedsCardsManager }其工作流程可以拆解为三步URL 校验再次用feedsUrls匹配当前页面不匹配则直接返回null实现双保险启动观察调用feedsCardsManager.startWatching()开始监听动态卡片的新增与移除。动态卡片是异步加载的只有通过 MutationObserver 持续观察 DOM才能在卡片出现的第一时间拿到它注册回调先对已存在的卡片立即执行added回调再把回调挂入feedsCardCallbacks后续新出现的卡片也会自动执行。feedsCardsManager的具体实现分为两代见 src/components/feeds/api/manager/index.tsexport const feedsCardsManager (() { const isV2 isV2Feeds() if (isV2) { return new FeedsCardsManagerV2() } return new FeedsCardsManagerV1() })()通过isV2Feeds()判断 B 站是否启用了新版动态v2它读取 cookiehit-dyn-v2当其值大于 0 且页面命中动态相关 URL 时使用 v2 管理器否则回退到 v1 管理器。两代管理器都提供统一的cards列表与startWatching()接口因此在组件层无需关心当前是 v1 还是 v2 数据forEachFeedsCard会透明地完成适配。拿到卡片对象card后组件通过card.element动态卡片的 DOM 根元素获取其 Vue 实例数据。读取 Vue 数据getVue2DataB 站动态页面由 Vue 2 渲染卡片 DOM 元素上挂载了组件实例。组件使用getVue2Data(card.element)获取对应的数据对象其实现位于 src/core/utils/index.ts/** 尝试获取元素对应的 Vue Data (仅适用于 Vue 2 组件) */ export const getVue2Data (el: any) // eslint-disable-next-line no-underscore-dangle el.__vue__ ?? el.parentElement.__vue__ ?? el.children[0].__vue__ ?? el.__vueParentComponent该函数按优先级依次尝试四种途径取到元素关联的 Vue 组件实例el.__vue__元素自身绑定的 Vue 实例el.parentElement.__vue__父元素的 Vue 实例el.children[0].__vue__第一个子元素的 Vue 实例el.__vueParentComponent元素最近的父组件实例。也就是说只要卡片 DOM 结构中的任一环节挂有 Vue 实例就能取到数据源。这也是数据层改造型组件的通用取数方式v1/v2 卡片管理器内部解析卡片类型、作者、点赞转发数据时同样复用了它见 v1.ts 与 v2.ts。普通动态改写 opus.style拿到vueData后组件先处理普通动态时间线卡片核心逻辑如下index.tsconst cardType vueData?.data?.type const path cardType DYNAMIC_TYPE_FORWARD ? data.orig.modules.module_dynamic.major.opus.style : data.modules.module_dynamic.major.opus.style const imageViewerStyle: number | null lodash.get(vueData, path, null) if (imageViewerStyle 1) { lodash.set(vueData, path, undefined) return }要点如下区分转发动态根据动态类型data.type判断。转发动态DYNAMIC_TYPE_FORWARD的原始内容位于data.orig下普通动态则直接位于data下定位图片样式字段两者最终都指向modules.module_dynamic.major.opus.style。opus是动态内容载体图文/视频等多媒体内容的通用结构其中的style字段标记了图片区域的展示形态判定左右切换式当style 1时即表示当前图片采用的是左右切换式图片查看器/轮播渲染执行改写用lodash.set(vueData, path, undefined)将style置为undefined从而触发 Vue 响应式更新让动态卡片按默认的平铺方式重新渲染图片。置为undefined而非0是因为 B 站渲染层对未指定与明确为平铺值的处理不同undefined会让图片区域回落到传统平铺分支。这一读style、改style的思路与 B 站数据结构解耦即便字段路径在后续版本中调整也只需修改path常量即可适配。动态详情页重组 paragraphs普通动态卡片修改的是opus.style但动态详情页的数据结构不同它把图片信息放在顶部分区MODULE_TYPE_TOP的相册album中需要把相册图片重新塞回正文段落paragraphs里。对应代码如下index.ts// 动态详情 const modules vueData?.data?.modules if (Array.isArray(modules)) { const moduleTop modules.find(m m.module_type MODULE_TYPE_TOP) const moduleContent modules.find(m m.module_type MODULE_TYPE_CONTENT) const album moduleTop?.module_top?.display?.album const paragraphs: any[] moduleContent?.module_content?.paragraphs if (album paragraphs) { modules.splice(modules.indexOf(moduleTop), 1) paragraphs.push({ align: 0, para_type: 2, pic: { pics: album.pics, style: 1, }, }) } }拆解其流程查找模块在data.modules数组中定位MODULE_TYPE_TOP顶部分区和MODULE_TYPE_CONTENT内容区两个模块取出相册数据module_top.display.album中保存了该动态的相册album.pics即图片列表取出正文段落module_content.paragraphs是动态正文的富文本段落数组移除顶部相册模块modules.splice(modules.indexOf(moduleTop), 1)把顶部的相册展示模块从模块列表中删除相当于移除左右切换式图片查看器的数据源追加平铺段落向paragraphs尾部 push 一个图片段落对象para_type: 2标记为图片段落pic.pics: album.pics复用原相册的图片列表style: 1图片段落内的平铺样式align: 0默认对齐方式。通过删掉顶部查看器模块 把图片以段落形式写回正文详情页的图片被渲染为跟随正文的平铺图片列表。由于这里需要先等待详情页的数据模块MODULE_TYPE_TOP/MODULE_TYPE_CONTENT渲染完成、modules数组就绪后才能重组而卡片管理器的added回调可能在数据尚未完整挂载时即被触发因此会出现文档中提到的动态详情中可能稍有延迟现象——组件入口会继续等待后续回调机会完成改造。数据流总结与可验证性整个组件的运行链路可以归纳为页面 URL 匹配 feedsUrls ↓ forEachFeedsCard 注册 added 回调src/components/feeds/api/manager/index.ts ↓ feedsCardsManager.startWatching() 观察动态卡片出现v1 / v2 管理器 ↓ card.element → getVue2Data() 获取 Vue 数据src/core/utils/index.ts ↓ 普通动态置 opus.style undefined恢复平铺 动态详情删除 MODULE_TYPE_TOP 相册模块将 album.pics 追加为 para_type2 的图片段落验证要点均可在当前仓库中直接确认组件元数据、两种改写路径的完整实现见 registry/lib/components/feeds/legacy-image-viewer/index.tsfeedsUrls的完整匹配规则见 src/core/utils/urls.tsforEachFeedsCard的回调注册与观察器启动逻辑见 src/components/feeds/api/manager/index.tsv1/v2 动态卡片管理器的分流逻辑见 src/components/feeds/api/manager/index.ts两代实现分别在 v1.ts 与 v2.tsgetVue2Data的取数优先级见 src/core/utils/index.ts。使用方式与注意事项该组件是 Bilibili-Evolved 的内置功能组件无需额外配置选项。在安装了增强脚本后打开脚本的设置面板在动态componentsTags.feeds分类下即可找到动态图片平铺展示并开关启用。启用后访问动态首页t.bilibili.com、个人空间动态、直播间动态或动态详情页opus/{id}时会自动生效。实际使用中可关注两点兼容前提组件依赖 B 站页面使用 Vue 2 渲染动态卡片且数据字段结构为module_dynamic.major.opus/modules[].module_*。若 B 站未来调整动态数据结构例如opus.style的取值语义变化组件需要同步更新字段路径详情页延迟在动态详情页中改造依赖modules数组与相册/段落数据就绪初次进入时可能观察到图片短暂保持左右切换式、随后才切换为平铺这是数据结构重组所需等待的正常表现与文档说明一致。【免费下载链接】Bilibili-Evolved强大的哔哩哔哩增强脚本项目地址: https://gitcode.com/gh_mirrors/bi/Bilibili-Evolved创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。