BiliBili-UWP第三方客户端架构深度解析:MVVM模式下的高性能UWP实现
BiliBili-UWP第三方客户端架构深度解析MVVM模式下的高性能UWP实现【免费下载链接】BiliBili-UWPBiliBili的UWP客户端当然是第三方的了项目地址: https://gitcode.com/gh_mirrors/bi/BiliBili-UWPBiliBili-UWP是一款基于Universal Windows PlatformUWP架构的第三方B站客户端为Windows用户提供了原生化、高性能的视频观看体验。该项目采用MVVM设计模式通过模块化架构实现了B站核心功能的重构与优化展现了在桌面环境下移动应用界面适配的创新解决方案。作为技术开发者了解其架构设计和实现细节对于构建高质量UWP应用具有重要参考价值。️ 架构设计与技术选型分层架构实现BiliBili-UWP采用了清晰的三层架构设计确保代码的可维护性和扩展性核心服务层BiliBili-Lib位于BiliBili-Lib/Service/目录包含BiliBiliClient、AccountService、VideoService等核心业务逻辑类。这一层负责与B站API通信、数据处理和业务逻辑封装完全独立于UI层。// BiliBiliClient作为统一入口点 public class BiliBiliClient { public AccountService Account; public ChannelService Channel; public TopicService Topic; public VideoService Video; public AnimeService Anime; public BiliBiliClient(string accessToken , string refreshToken , int expiry 0) { // 初始化各服务实例 Account new AccountService(new TokenPackage(accessToken, refreshToken, expiry)); Video new VideoService(); Anime new AnimeService(); } }UI组件层BiliBili-Controls提供自定义控件库包括自适应网格视图、气泡控件、虚拟化面板等增强了应用的视觉表现力和交互体验。控件库位于BiliBili-Controls/目录包含AdaptiveGridView、Bubble、CustomPanel等模块。应用呈现层BiliBili-UWP主应用程序层包含页面布局、视图模型和用户界面逻辑实现了桌面模式与平板模式的双重适配。项目支持响应式布局根据设备类型自动切换界面风格。MVVM模式实践项目严格遵循MVVM设计模式通过AppViewModel和BiliViewModel两大核心ViewModel管理应用状态// AppViewModel管理应用级状态 public class AppViewModel { public double StateChangeWidth 1000; // 响应式布局阈值 public AppMenuItem SelectedSideMenuItem { get; set; } public Type CurrentPageType { get; set; } public SidePanel CurrentSidePanel; public PagePanel CurrentPagePanel; public VideoPlayer CurrentVideoPlayer; public PlayerType CurrentPlayerType; // 窗口尺寸变化处理 public void WindowSizeChangedHandle(object sender, WindowSizeChangedEventArgs e) { if (WindowsSizeChangedNotify.Count 0) { WindowsSizeChangedNotify.ForEach(p p.Item2?.Invoke(e.Size)); } } }BiliBili-UWP启动界面采用响应式设计适配不同分辨率设备 API通信与数据处理机制统一API端点管理项目在BiliBili-Lib/Models/Others/Api.cs中集中管理所有B站API端点采用常量定义方式确保类型安全public class Api { public const string _apiBase https://api.bilibili.com; public const string _appBase https://app.bilibili.com; public const string _vcBase https://api.vc.bilibili.com; public const string _liveBase https://api.live.bilibili.com; public const string _passBase https://passport.bilibili.com; // 验证相关API public const string PASSPORT_KEY_ENCRYPT _passBase /api/oauth2/getKey; public const string PASSPORT_LOGIN _passBase /api/v3/oauth2/login; public const string PASSPORT_REFRESH_TOKEN _passBase /api/oauth2/refreshToken; // 视频相关API public const string VIDEO_DETAIL _apiBase /x/web-interface/view; public const string VIDEO_PLAY_URL _apiBase /x/player/playurl; public const string VIDEO_RECOMMEND _appBase /x/v2/feed/index; // 分区相关API public const string REGION_INDEX _appBase /x/v2/region/index; public const string REGION_DYNAMIC_INIT _appBase /x/v2/region/dynamic; }智能数据缓存策略应用实现了智能的数据缓存策略在网络请求失败时自动使用本地缓存数据提升用户体验public async Task GetRegionsAsync() { RegionCollection.Clear(); var regions await _client.GetRegionIndexAsync(); if (regions ! null) { // 网络成功更新本地缓存 await IOTool.SetLocalDataAsync(region.json, JsonConvert.SerializeObject(regions.Where(p p.children ! null).ToList())); } else { // 网络失败使用本地缓存 regions await IOTool.GetLocalDataAsyncListRegionContainer(region.json); } regions.Where(p p.children ! null).ToList().ForEach(p RegionCollection.Add(p)); }异步数据加载模式所有网络请求都采用异步模式避免UI线程阻塞确保应用响应性public async TaskListVideoRecommend GetRecommendVideoAsync(int idx 0) { var param new Dictionarystring, string(); param.Add(idx, idx.ToString()); param.Add(flush, 0); param.Add(column, 4); param.Add(device, pad); // 模拟平板设备获取更适合桌面的数据格式 string url BiliTool.UrlContact(Api.VIDEO_RECOMMEND, param, true, useiPhone:true); var data await BiliTool.ConvertEntityFromWebAsyncListVideoRecommend(url, data.items); return data ?? new ListVideoRecommend(); }应用采用B站标志性的粉色电视图标保持品牌一致性 UI适配与多设备支持桌面与平板双模式架构项目最大的技术特色之一是实现了桌面模式与平板模式的无缝切换通过不同的页面布局策略适应不同设备桌面模式采用经典的Master-Detail设计左侧导航栏右侧内容区域适合鼠标键盘操作平板模式借鉴XBOX界面设计横向滚动为主大留白设计优化触摸操作体验!-- 桌面模式主页面布局 -- Grid Grid.ColumnDefinitions ColumnDefinition WidthAuto/ ColumnDefinition Width*/ /Grid.ColumnDefinitions !-- 左侧导航区域 -- local:SidePanel x:NameSidePanel Grid.Column0/ !-- 右侧内容区域 -- local:PagePanel x:NamePagePanel Grid.Column1/ /Grid响应式布局系统应用内置了完善的响应式布局系统通过AppViewModel.StateChangeWidth属性动态调整界面布局// 响应式布局实现 private void OnWindowSizeChanged(object sender, WindowSizeChangedEventArgs e) { var width e.Size.Width; if (width App.AppViewModel.StateChangeWidth) { // 切换到紧凑布局 VisualStateManager.GoToState(this, CompactState, true); } else { // 切换到扩展布局 VisualStateManager.GoToState(this, ExpandedState, true); } }自定义控件库项目在BiliBili-Controls/目录下提供了丰富的自定义控件AdaptiveGridView自适应网格视图自动调整项目大小VirtualizingPanel虚拟化面板优化大量数据项的渲染性能Bubble气泡控件用于消息提示和交互反馈StaggeredPanel瀑布流布局面板适用于图片和视频展示// 虚拟化面板实现 public class VirtualizingPanel : Panel { // 仅渲染可视区域内的项目 protected override Size MeasureOverride(Size availableSize) { // 虚拟化测量逻辑 foreach (var child in Children) { if (IsInViewport(child)) { child.Measure(availableSize); } } return availableSize; } } 视频播放器技术实现多格式视频支持VideoPlayer控件是项目的核心技术组件支持多种视频播放模式和画质切换public sealed partial class VideoPlayer : UserControl { private ObservableCollectionTupleint, string QualityCollection new ObservableCollectionTupleint, string(); private ObservableCollectionTupledouble, string PlayRateCollection; // 支持多种播放模式 public enum PlayerType { Normal, // 普通模式 FullWindow, // 全窗口模式 Cinema, // 影院模式 CompactOverlay // 画中画模式 } // 初始化播放器 private void InitPlayer() { _mediaPlayer new MediaPlayer(); _mediaPlayerElement.SetMediaPlayer(_mediaPlayer); _mediaPlayer.PlaybackSession.PlaybackStateChanged PlaybackSession_PlaybackStateChanged; } }弹幕系统集成项目集成了NSDanmaku弹幕引擎提供了完整的弹幕显示和交互功能using NSDanmaku.Helper; using NSDanmaku.Model; // 弹幕初始化与配置 private void InitDanmaku() { _danmakuView new DanmakuView(); _danmakuView.DanmakuArea DanmakuArea; _danmakuView.DanmakuSize DanmakuSize; _danmakuView.DanmakuOpacity SettingsHelper.GetValuedouble(Settings.DanmakuOpacity, 1.0); _danmakuView.DanmakuFontSize SettingsHelper.GetValuedouble(Settings.DanmakuFontSize, 25); // 弹幕设置 var settings new DanmakuSettings { Speed SettingsHelper.GetValueint(Settings.DanmakuSpeed, 10), FontFamily SettingsHelper.GetValuestring(Settings.DanmakuFontFamily, Microsoft YaHei), Merge SettingsHelper.GetValuebool(Settings.DanmakuMerge, true) }; }宽屏启动界面适配不同设备比例提供一致的用户体验⚡ 性能优化策略虚拟化列表渲染项目在BiliBili-Controls/CustomPanel/目录中实现了自定义虚拟化面板优化大量数据项的渲染性能public class VirtualizingPanel : Panel { private ListUIElement _visibleChildren new ListUIElement(); protected override Size MeasureOverride(Size availableSize) { // 计算可视区域 var viewport GetViewport(); // 仅测量可视区域内的子元素 foreach (var child in Children) { var childRect GetChildRect(child); if (viewport.IntersectsWith(childRect)) { child.Measure(availableSize); if (!_visibleChildren.Contains(child)) _visibleChildren.Add(child); } else if (_visibleChildren.Contains(child)) { _visibleChildren.Remove(child); } } return availableSize; } }图片懒加载机制应用实现了图片懒加载机制仅当图片进入可视区域时才加载显示// BiliImage控件实现懒加载 public sealed partial class BiliImage : UserControl { private bool _isInViewport false; private void OnViewportChanged(object sender, bool isInViewport) { if (isInViewport !_isInViewport) { _isInViewport true; LoadImage(); // 延迟加载图片 } } private async void LoadImage() { if (string.IsNullOrEmpty(Source)) return; var bitmap new BitmapImage(); bitmap.UriSource new Uri(Source); await bitmap.InitAsync(); // 异步加载图片 ImageControl.Source bitmap; } }数据预加载策略应用通过预测用户行为提前加载可能需要的下一页数据减少等待时间public class DataPreloader { private Dictionarystring, object _preloadedData new Dictionarystring, object(); public async TaskT PreloadDataAsyncT(string key, FuncTaskT dataLoader) { if (_preloadedData.ContainsKey(key)) { return (T)_preloadedData[key]; } var data await dataLoader(); _preloadedData[key] data; return data; } // 预加载下一页数据 public async Task PreloadNextPageT(int currentPage, Funcint, TaskListT pageLoader) { var nextPageData await pageLoader(currentPage 1); // 缓存预加载的数据 } } 安全与认证机制OAuth2令牌管理应用实现了完整的OAuth2令牌管理机制支持访问令牌的自动刷新public class TokenPackage { public string AccessToken { get; set; } public string RefreshToken { get; set; } public int Expiry { get; set; } public TokenPackage(string accessToken, string refreshToken, int expiry) { AccessToken accessToken; RefreshToken refreshToken; Expiry expiry; } public bool IsExpired() { return DateTimeOffset.Now.ToUnixTimeSeconds() Expiry; } } // 令牌刷新逻辑 public async Taskbool RefreshTokenAsync() { try { var package await _client.Account.RefreshTokenAsync(); if (package ! null) { // 更新本地存储的令牌 await IOTool.SetLocalDataAsync(token.json, JsonConvert.SerializeObject(package)); return true; } } catch (Exception) { // 刷新失败处理 } return false; }本地数据加密存储敏感用户数据采用本地加密存储保护用户隐私public class BiliSecure { private static readonly byte[] Key Encoding.UTF8.GetBytes(YourSecretKey); private static readonly byte[] IV Encoding.UTF8.GetBytes(YourIV1234567890); // 用户数据加密存储实现 public static string EncryptData(string data) { using (var aes Aes.Create()) { aes.Key Key; aes.IV IV; var encryptor aes.CreateEncryptor(aes.Key, aes.IV); using (var ms new MemoryStream()) using (var cs new CryptoStream(ms, encryptor, CryptoStreamMode.Write)) { using (var sw new StreamWriter(cs)) { sw.Write(data); } return Convert.ToBase64String(ms.ToArray()); } } } public static string DecryptData(string encryptedData) { var buffer Convert.FromBase64String(encryptedData); using (var aes Aes.Create()) { aes.Key Key; aes.IV IV; var decryptor aes.CreateDecryptor(aes.Key, aes.IV); using (var ms new MemoryStream(buffer)) using (var cs new CryptoStream(ms, decryptor, CryptoStreamMode.Read)) using (var sr new StreamReader(cs)) { return sr.ReadToEnd(); } } } } 部署与扩展方案项目编译与打包项目采用标准的UWP项目结构支持Visual Studio 2019及以上版本编译!-- Package.appxmanifest 应用清单配置 -- Package xmlnshttp://schemas.microsoft.com/appx/manifest/foundation/windows10 xmlns:mphttp://schemas.microsoft.com/appx/2014/phone/manifest xmlns:uaphttp://schemas.microsoft.com/appx/manifest/uap/windows10 Identity NameBiliBili-UWP PublisherCNRichasy Version1.0.0.0 / Dependencies TargetDeviceFamily NameWindows.Universal MinVersion10.0.17763.0 MaxVersionTested10.0.19041.0 / /Dependencies Capabilities Capability NameinternetClient / Capability NameprivateNetworkClientServer / uap:Capability NamevideosLibrary / /Capabilities /Package模块化扩展架构虽然项目目前未实现完整的插件系统但架构设计为未来扩展留下了清晰的接口// 扩展接口定义示例 public interface IExtensionModule { string ModuleName { get; } string ModuleVersion { get; } Task InitializeAsync(); Taskobject ProcessRequestAsync(string command, object parameters); void Cleanup(); } // 服务注册机制 public class ServiceRegistry { private DictionaryType, object _services new DictionaryType, object(); public void RegisterServiceT(T service) where T : class { _services[typeof(T)] service; } public T GetServiceT() where T : class { if (_services.TryGetValue(typeof(T), out var service)) return service as T; return null; } } 技术挑战与解决方案移动端API适配桌面环境项目面临的最大挑战是将为移动端设计的B站API适配到桌面环境参数适配技术通过devicepad等参数模拟平板设备获取更适合桌面的数据格式UI重组策略将移动端卡片式布局转换为桌面端的多列网格布局交互优化方案将触摸手势转换为鼠标键盘操作同时保留触摸支持// API参数适配示例 public static string AdaptApiForDesktop(string originalUrl) { var uri new Uri(originalUrl); var query HttpUtility.ParseQueryString(uri.Query); // 添加桌面适配参数 query[device] pad; query[platform] windows; query[build] 10000; var builder new UriBuilder(uri) { Query query.ToString() }; return builder.ToString(); }性能优化策略针对UWP平台特性项目实施了多项性能优化虚拟化列表渲染对视频列表、评论列表等大量数据项进行虚拟化渲染图片懒加载机制仅当图片进入可视区域时才加载显示数据预加载策略预测用户行为提前加载可能需要的下一页数据内存管理优化及时释放不再使用的资源防止内存泄漏️ 技术生态与开发指南依赖的技术栈核心框架UWP (Universal Windows Platform) 10.0.17763.0UI框架XAML WinUI网络库System.Net.Http 自定义封装JSON处理Newtonsoft.Json弹幕引擎NSDanmaku视频播放SYEngine Windows.Media.Playback数据绑定INotifyPropertyChanged ObservableCollection开源贡献指南项目采用模块化设计便于开发者参与贡献API模块扩展在BiliBili-Lib/Service/中添加新的服务类UI组件开发在BiliBili-Controls/中开发自定义控件页面扩展在BiliBili-UWP/Pages-*目录中创建新页面ViewModel扩展在BiliBili-UWP/Models/Core/中添加新的ViewModel// 添加新服务的示例 public class NewFeatureService { private readonly BiliBiliClient _client; public NewFeatureService(BiliBiliClient client) { _client client; } public async TaskNewFeatureData GetDataAsync() { // 实现新的API调用逻辑 var url Api.NEW_FEATURE_API; var data await BiliTool.ConvertEntityFromWebAsyncNewFeatureData(url); return data; } } 总结与技术价值BiliBili-UWP项目展示了如何将移动优先的Web API成功适配到桌面环境的技术实践。通过MVVM架构、响应式设计、虚拟化渲染等现代前端技术项目在保持B站原生体验的同时提供了更适合桌面操作的交互方式。技术亮点总结架构设计优秀清晰的三层架构和MVVM模式确保了代码的可维护性和可扩展性性能优化到位虚拟化渲染、懒加载、数据预加载等多重优化策略用户体验出色桌面与平板双模式支持响应式布局系统安全性考虑周全完整的OAuth2令牌管理和本地数据加密代码质量高模块化设计清晰的命名规范完善的错误处理适用场景与学习价值UWP开发者学习现代UWP应用架构设计和性能优化技巧跨平台开发者了解如何将移动端API适配到桌面环境前端工程师研究响应式设计和虚拟化渲染的最佳实践开源贡献者参与大型客户端项目的开发和维护虽然项目已停止官方维护但其技术实现仍为UWP开发者提供了宝贵的参考价值特别是在API适配、性能优化和跨设备界面设计方面。对于希望深入学习UWP开发、理解大型客户端应用架构的开发者来说BiliBili-UWP的源代码是一个极佳的学习资源。要开始探索这个项目可以通过以下命令克隆仓库git clone https://gitcode.com/gh_mirrors/bi/BiliBili-UWP项目的模块化设计、清晰的代码结构和完整的功能实现都体现了高质量软件工程的最佳实践值得每一位客户端开发者深入研究和学习。【免费下载链接】BiliBili-UWPBiliBili的UWP客户端当然是第三方的了项目地址: https://gitcode.com/gh_mirrors/bi/BiliBili-UWP创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

3D人体动作生成新手指南:如何用HumanML3D构建你的第一个AI动作模型

3D人体动作生成新手指南:如何用HumanML3D构建你的第一个AI动作模型

3D人体动作生成新手指南:如何用HumanML3D构建你的第一个AI动作模型 【免费下载链接】HumanML3D HumanML3D: A large and diverse 3d human motion-language dataset. 项目地址: https://gitcode.com/gh_mirrors/hu/HumanML3D 你是否想过让AI理解人类动作&…

2026/10/21 22:31:57 阅读更多 →
X-AnyLabeling:免费开源的AI图像标注工具,让数据标注效率提升10倍

X-AnyLabeling:免费开源的AI图像标注工具,让数据标注效率提升10倍

X-AnyLabeling:免费开源的AI图像标注工具,让数据标注效率提升10倍 【免费下载链接】X-AnyLabeling Open-source AI-assisted annotation platform for images, videos, text, and multimodal data. 项目地址: https://gitcode.com/gh_mirrors/xa/X-Any…

2026/10/19 16:09:07 阅读更多 →
高级玩家指南:Soar的声明式配置如何实现软件环境一键复刻?

高级玩家指南:Soar的声明式配置如何实现软件环境一键复刻?

高级玩家指南:Soar的声明式配置如何实现软件环境一键复刻? 【免费下载链接】soar A fast, modern package manager for Static Binaries, Portable Formats (AppImage|AppBundle|FlatImage|Runimage) & More [maintainerQaidVoid] 项目地址: https…

2026/10/20 4:58:01 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/10/22 10:18:38 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/10/22 11:01:53 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/10/22 9:44:47 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/10/22 10:42:00 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/10/22 10:40:08 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/10/22 10:56:37 阅读更多 →

日新闻

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/22 1:47:45 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/22 21:33:42 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/22 5:12:39 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/21 21:50:56 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/21 21:51:55 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/22 19:06:46 阅读更多 →