深入解析.NET MAUI:跨平台开发的架构设计与性能优化
深入解析.NET MAUI跨平台开发的架构设计与性能优化【免费下载链接】maui.NET MAUI is the .NET Multi-platform App UI, a framework for building native device applications spanning mobile, tablet, and desktop.项目地址: https://gitcode.com/GitHub_Trending/ma/maui.NET MAUI作为微软新一代跨平台应用开发框架代表了现代移动和桌面应用开发的技术演进方向。本文将从技术架构、设计哲学、性能优化和企业级实践等多个维度深度解析.NET MAUI如何通过统一的代码库实现在Android、iOS、macOS和Windows平台上的原生体验。核心理念设计哲学与架构演进.NET MAUI的设计哲学核心在于一次编写处处运行的理念但与传统跨平台方案不同它采用了更加智能的平台适配策略。框架通过抽象层将共享业务逻辑与平台特定实现分离同时保持对原生性能的极致追求。Handler解析机制跨平台渲染的核心在.NET MAUI中Handler机制是实现跨平台UI渲染的技术核心。每个跨平台View都对应一个平台特定的Handler这种设计模式既保证了代码共享又确保了原生性能。Handler的注册和解析过程如下// Handler注册示例 builder.ConfigureMauiHandlers(handlers { // 注册Button的跨平台处理程序 handlers.AddHandler(typeof(Button), typeof(ButtonHandler)); // 注册自定义控件的处理程序 handlers.AddHandler(typeof(CustomControl), typeof(CustomControlHandler)); });Handler解析过程涉及MauiFactory和IMauiHandlersFactory等关键组件。MauiFactory最初作为高性能依赖解析器设计虽然现在与IServiceProvider共存但在某些场景下仍提供性能优势。这种双解析器架构体现了.NET MAUI在性能与标准化之间的平衡考量。平台特定代码的优雅处理.NET MAUI通过条件编译和依赖注入机制优雅处理平台差异public interface IPlatformService { Taskstring GetDeviceInfoAsync(); } // Android实现 #if ANDROID public class AndroidPlatformService : IPlatformService { public async Taskstring GetDeviceInfoAsync() { // Android特定实现 return $Android {Build.VERSION.Release}; } } #endif // iOS实现 #if IOS public class IosPlatformService : IPlatformService { public async Taskstring GetDeviceInfoAsync() { // iOS特定实现 return $iOS {UIDevice.CurrentDevice.SystemVersion}; } } #endif架构设计模块化与可扩展性.NET MAUI的架构采用分层设计从核心抽象到平台实现每一层都有明确的职责边界。这种设计不仅保证了代码的可维护性也为第三方扩展提供了标准接口。核心抽象层设计在src/Core/src/Core/目录中我们可以看到.NET MAUI的核心抽象接口设计。这些接口定义了跨平台UI组件的基本契约// 核心接口示例 public interface IView : IElement { // 布局相关属性 double Width { get; } double Height { get; } // 渲染方法 void Measure(double widthConstraint, double heightConstraint); void Arrange(Rect bounds); // 事件处理 event EventHandlerFocusEventArgs Focused; event EventHandlerFocusEventArgs Unfocused; }这种接口优先的设计模式确保了平台实现的灵活性同时为开发者提供了统一的编程模型。平台适配层架构每个平台都有对应的适配层实现位于src/Core/src/Platforms/目录。这些实现负责将抽象接口映射到具体的平台API// Android平台Button实现示例 public partial class ButtonHandler : ViewHandlerIButton, AndroidX.AppCompat.Widget.AppCompatButton { protected override AndroidX.AppCompat.Widget.AppCompatButton CreatePlatformView() { return new AndroidX.AppCompat.Widget.AppCompatButton(Context); } protected override void ConnectHandler(AndroidX.AppCompat.Widget.AppCompatButton platformView) { base.ConnectHandler(platformView); // 绑定原生事件 platformView.Click OnButtonClick; } private void OnButtonClick(object sender, EventArgs e) { VirtualView?.Clicked(); } }实战演练构建企业级应用的最佳实践MVVM模式的最佳实践在企业级应用中MVVM模式是.NET MAUI推荐的数据绑定架构。以下是一个完整的ViewModel实现示例public class ProductViewModel : BaseViewModel { private readonly IProductService _productService; private ObservableCollectionProduct _products; private Product _selectedProduct; public ProductViewModel(IProductService productService) { _productService productService; LoadProductsCommand new AsyncRelayCommand(LoadProductsAsync); AddProductCommand new RelayCommand(AddProduct); } public ObservableCollectionProduct Products { get _products; set SetProperty(ref _products, value); } public Product SelectedProduct { get _selectedProduct; set SetProperty(ref _selectedProduct, value); } public ICommand LoadProductsCommand { get; } public ICommand AddProductCommand { get; } private async Task LoadProductsAsync() { try { IsBusy true; var products await _productService.GetProductsAsync(); Products new ObservableCollectionProduct(products); } catch (Exception ex) { // 错误处理 await Shell.Current.DisplayAlert(Error, ex.Message, OK); } finally { IsBusy false; } } private void AddProduct() { // 导航到添加产品页面 Shell.Current.GoToAsync(addproduct); } }依赖注入与服务配置.NET MAUI内置了强大的依赖注入容器支持复杂的服务注册和生命周期管理public static class MauiProgram { public static MauiApp CreateMauiApp() { var builder MauiApp.CreateBuilder(); builder.UseMauiAppApp(); // 注册服务 builder.Services.AddSingletonIProductService, ProductService(); builder.Services.AddTransientProductDetailViewModel(); builder.Services.AddScopedICartService, CartService(); // 配置日志 builder.Logging.AddDebug(); // 注册自定义Handler builder.ConfigureMauiHandlers(handlers { handlers.AddHandlerCustomEntry, CustomEntryHandler(); }); return builder.Build(); } }.NET MAUI跨平台天气应用展示同一代码库在Windows、Android、iOS和macOS上的原生渲染效果测试驱动开发实践.NET MAUI提供了完整的测试基础设施支持单元测试、集成测试和UI自动化测试[TestFixture] public class ProductViewModelTests { private MockIProductService _mockProductService; private ProductViewModel _viewModel; [SetUp] public void Setup() { _mockProductService new MockIProductService(); _viewModel new ProductViewModel(_mockProductService.Object); } [Test] public async Task LoadProductsCommand_ShouldPopulateProducts() { // 准备 var expectedProducts new ListProduct { new Product { Id 1, Name Product 1, Price 99.99m }, new Product { Id 2, Name Product 2, Price 149.99m } }; _mockProductService .Setup(s s.GetProductsAsync()) .ReturnsAsync(expectedProducts); // 执行 await _viewModel.LoadProductsCommand.ExecuteAsync(null); // 验证 Assert.That(_viewModel.Products, Has.Count.EqualTo(2)); Assert.That(_viewModel.Products[0].Name, Is.EqualTo(Product 1)); } [Test] public void SelectedProduct_WhenSet_ShouldRaisePropertyChanged() { // 准备 var product new Product { Id 1, Name Test Product }; var propertyChangedRaised false; _viewModel.PropertyChanged (s, e) { if (e.PropertyName nameof(ProductViewModel.SelectedProduct)) propertyChangedRaised true; }; // 执行 _viewModel.SelectedProduct product; // 验证 Assert.IsTrue(propertyChangedRaised); } }.NET MAUI UI自动化测试失败分析界面展示测试覆盖率、失败用例详细信息和截图对比高级特性性能优化与架构扩展内存管理与性能优化.NET MAUI提供了多种内存管理和性能优化策略public class OptimizedListView : ListView { // 使用虚拟化提高列表性能 public OptimizedListView() { CachingStrategy ListViewCachingStrategy.RecycleElement; // 优化项模板 ItemTemplate new DataTemplate(() { var viewCell new ViewCell(); // 轻量级布局 var stackLayout new StackLayout { Orientation StackOrientation.Horizontal, Spacing 10, Padding new Thickness(10) }; // 重用视图实例 var image new Image { HeightRequest 50, WidthRequest 50 }; image.SetBinding(Image.SourceProperty, ImageUrl); var label new Label { VerticalOptions LayoutOptions.Center }; label.SetBinding(Label.TextProperty, Name); stackLayout.Children.Add(image); stackLayout.Children.Add(label); viewCell.View stackLayout; return viewCell; }); } // 实现IDisposable释放资源 protected override void Dispose(bool disposing) { if (disposing) { // 清理资源 ItemsSource null; ItemTemplate null; } base.Dispose(disposing); } }自定义渲染器与平台特定优化虽然Handler是推荐的方式但.NET MAUI仍然支持自定义渲染器用于复杂场景// 自定义iOS渲染器示例 #if IOS [assembly: ExportRenderer(typeof(CustomMap), typeof(CustomMapRenderer))] namespace MyApp.Platforms.iOS { public class CustomMapRenderer : MapRenderer { private MKMapView _nativeMap; protected override void OnElementChanged(ElementChangedEventArgsView e) { base.OnElementChanged(e); if (e.OldElement ! null) { // 清理旧元素 _nativeMap.RemoveOverlays(_nativeMap.Overlays); _nativeMap null; } if (e.NewElement ! null) { _nativeMap Control as MKMapView; if (_nativeMap ! null) { // iOS特定优化 _nativeMap.ShowsUserLocation true; _nativeMap.UserTrackingMode MKUserTrackingMode.Follow; // 添加自定义覆盖层 AddCustomOverlays(); } } } private void AddCustomOverlays() { // iOS特定的地图功能实现 var circle MKCircle.Circle( new CLLocationCoordinate2D(37.7749, -122.4194), 1000); _nativeMap.AddOverlay(circle); } } } #endif.NET MAUI测试项目结构展示包含核心单元测试、设备测试和源码生成器测试的模块化组织生态集成CI/CD与质量保证持续集成与自动化测试.NET MAUI项目提供了完整的CI/CD流水线配置位于eng/pipelines/目录。这些配置支持多平台构建、测试和部署# 示例CI配置 stages: - stage: Build displayName: Build and Test jobs: - job: BuildWindows displayName: Build for Windows pool: vmImage: windows-latest steps: - task: UseDotNet2 inputs: version: 8.0.x - script: | dotnet restore dotnet build --configuration Release displayName: Build .NET MAUI Windows - job: BuildAndroid displayName: Build for Android pool: vmImage: macos-latest steps: - script: | dotnet build src/Controls/Controls.csproj \ -f net8.0-android \ -c Release displayName: Build Android Application - job: UITests displayName: Run UI Tests dependsOn: [BuildWindows, BuildAndroid] steps: - script: | dotnet test src/Controls/tests/TestCases.Shared.Tests \ --logger trx;LogFileNametest-results.trx displayName: Execute UI Tests截图测试与视觉回归.NET MAUI的UI测试框架支持截图对比确保视觉一致性[Test] [Category(UI)] public async Task LoginPage_ShouldRenderCorrectly() { // 启动应用 var app AppiumDriverHelper.StartApp(); try { // 导航到登录页面 await app.GoToAsync(login); // 等待页面加载 await Task.Delay(1000); // 截取屏幕 var screenshot app.Screenshot(); // 与基线截图比较 var diff await ImageComparer.CompareWithBaselineAsync( screenshot, LoginPage_Baseline.png); // 验证视觉一致性 Assert.That(diff.DifferencePercentage, Is.LessThan(0.01)); // 验证UI元素存在 var usernameEntry app.FindElement(UsernameEntry); var passwordEntry app.FindElement(PasswordEntry); var loginButton app.FindElement(LoginButton); Assert.That(usernameEntry, Is.Not.Null); Assert.That(passwordEntry, Is.Not.Null); Assert.That(loginButton, Is.Not.Null); } finally { app.Quit(); } }.NET MAUI CI/CD流水线构建产物管理展示多平台构建输出和UI测试结果企业级应用架构扩展插件化架构设计.NET MAUI支持插件化架构便于功能模块的动态加载public interface IModule { string Name { get; } Version Version { get; } Task InitializeAsync(IServiceCollection services); Task RegisterRoutesAsync(IRouteBuilder routeBuilder); } public class PaymentModule : IModule { public string Name Payment; public Version Version new Version(1, 0, 0); public async Task InitializeAsync(IServiceCollection services) { services.AddSingletonIPaymentService, PaymentService(); services.AddTransientPaymentViewModel(); services.AddTransientPaymentPage(); } public async Task RegisterRoutesAsync(IRouteBuilder routeBuilder) { routeBuilder.RegisterRoute(payment, typeof(PaymentPage)); routeBuilder.RegisterRoute(payment/success, typeof(PaymentSuccessPage)); } } // 模块加载器 public class ModuleLoader { private readonly IServiceCollection _services; private readonly ListIModule _loadedModules new(); public ModuleLoader(IServiceCollection services) { _services services; } public async Task LoadModulesAsync() { // 从配置或发现机制加载模块 var modules new ListIModule { new PaymentModule(), new AnalyticsModule(), new NotificationModule() }; foreach (var module in modules) { await module.InitializeAsync(_services); _loadedModules.Add(module); } } public async Task RegisterRoutesAsync(IRouteBuilder routeBuilder) { foreach (var module in _loadedModules) { await module.RegisterRoutesAsync(routeBuilder); } } }性能监控与诊断企业级应用需要完善的性能监控体系public class PerformanceMonitor : IPerformanceMonitor { private readonly ConcurrentDictionarystring, PerformanceMetric _metrics; private readonly ILoggerPerformanceMonitor _logger; public PerformanceMonitor(ILoggerPerformanceMonitor logger) { _metrics new ConcurrentDictionarystring, PerformanceMetric(); _logger logger; } public IDisposable TrackOperation(string operationName) { return new PerformanceTracker(operationName, this); } public void RecordMetric(string name, TimeSpan duration, bool success true) { var metric _metrics.GetOrAdd(name, _ new PerformanceMetric(name)); metric.Record(duration, success); // 日志性能数据 _logger.LogInformation( Performance metric recorded: {Name} - {Duration}ms - {Success}, name, duration.TotalMilliseconds, success); // 触发性能警告 if (duration.TotalMilliseconds 1000) { _logger.LogWarning( Performance alert: {Name} took {Duration}ms, name, duration.TotalMilliseconds); } } public PerformanceReport GenerateReport() { var report new PerformanceReport { GeneratedAt DateTime.UtcNow, Metrics _metrics.Values.ToList() }; return report; } } // 使用示例 public class ProductService : IProductService { private readonly IPerformanceMonitor _performanceMonitor; public async TaskListProduct GetProductsAsync() { using (_performanceMonitor.TrackOperation(GetProducts)) { try { // 业务逻辑 await Task.Delay(100); // 模拟网络请求 return new ListProduct(); } catch (Exception ex) { _performanceMonitor.RecordMetric(GetProducts, TimeSpan.Zero, false); throw; } } } }进阶学习路径与技术扩展源码深度研究建议核心架构研究研究src/Core/src/Core/中的接口设计模式分析src/Core/src/Handlers/中的Handler实现机制探索src/Controls/src/Core/中的控件架构平台适配层分析研究各平台特定实现Android/、iOS/、Windows/、MacCatalyst/分析平台间差异处理策略学习平台特定优化的最佳实践构建与测试体系研究eng/目录下的构建脚本和CI/CD配置分析测试框架设计src/TestUtils/学习UI自动化测试实现docs/UITesting-Guide.md技术扩展方向自定义控件开发基于现有控件扩展新功能实现平台特定的渲染优化创建可重用的控件库性能优化研究内存泄漏检测与预防渲染性能优化启动时间优化策略架构模式扩展微前端架构在MAUI中的应用领域驱动设计(DDD)实践事件溯源与CQRS模式集成社区贡献指南代码贡献流程阅读项目贡献指南遵循代码风格规范编写完整的单元测试文档改进补充API文档编写使用示例翻译技术文档问题跟踪与修复重现和报告问题提供修复方案编写回归测试.NET MAUI作为现代跨平台开发的重要框架其技术深度和架构设计为开发者提供了强大的工具集。通过深入理解其内部机制、掌握最佳实践并参与社区贡献开发者不仅能够构建高质量的应用还能推动整个生态的发展。无论是初创项目还是企业级应用.NET MAUI都提供了可靠的技术基础和灵活的扩展能力。【免费下载链接】maui.NET MAUI is the .NET Multi-platform App UI, a framework for building native device applications spanning mobile, tablet, and desktop.项目地址: https://gitcode.com/GitHub_Trending/ma/maui创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

I/O技术中,程序查询(Polling)、中断(Interrupt)和DMA(Direct Memory Access)是三种主要的数据传输控制方式

I/O技术中,程序查询(Polling)、中断(Interrupt)和DMA(Direct Memory Access)是三种主要的数据传输控制方式

I/O技术中,程序查询(Polling)、中断(Interrupt)和DMA(Direct Memory Access)是三种主要的数据传输控制方式,它们在CPU参与程度、效率、实时性和硬件复杂度等方面有显著区别&#xff…

2026/10/13 8:33:06 阅读更多 →
**就绪(Ready)**:进程已获得除CPU外的所有必要资源,等待调度程序分配处理器时间

**就绪(Ready)**:进程已获得除CPU外的所有必要资源,等待调度程序分配处理器时间

操作系统中进程的三大基础核心状态确实是: 就绪(Ready):进程已获得除CPU外的所有必要资源,等待调度程序分配处理器时间;运行(Running):进程正在CPU上执行指令&#xff1b…

2026/10/13 2:16:53 阅读更多 →
手把手教你配置Visual Studio与QT集成开发环境

手把手教你配置Visual Studio与QT集成开发环境

1. 项目概述:从零搭建C与QT的集成开发环境如果你是一名C开发者,或者正打算从其他语言转向C,想要涉足图形界面(GUI)或跨平台应用开发,那么QT绝对是一个绕不开的利器。但很多朋友,尤其是初学者&am…

2026/10/14 16:15:27 阅读更多 →

最新新闻

粒子群算法解柔性作业车间调度:双链编码实战与Kacem基准验证

粒子群算法解柔性作业车间调度:双链编码实战与Kacem基准验证

简介:一套基于Java的粒子群算法(PSO)实现,用于求解柔性作业车间调度(FJSP)问题,适合正在学习智能优化算法、开展生产调度相关课题或课程设计的学生与科研人员。压缩包共26个文件,主要…

2026/10/15 13:52:34 阅读更多 →
工业锅炉人孔设计:密封技术与安全运维详解

工业锅炉人孔设计:密封技术与安全运维详解

1. 锅炉人孔的核心价值解析在工业锅炉这个封闭的钢铁巨兽内部,常年维持着数百摄氏度的高温和数十个大气压的压力环境。人孔作为直径通常只有400-600mm的圆形开口,却是维系整个系统安全运行的"生命通道"。这个看似简单的结构件,实际…

2026/10/15 13:52:34 阅读更多 →
3-1 独立按键控制LED状态(SDCC + Notepad++ + STC-ISP/普中ISP)

3-1 独立按键控制LED状态(SDCC + Notepad++ + STC-ISP/普中ISP)

问题分析上节实现了按下按键LED亮,松开按键LED 灭,这节需要实现的是第一次按下按键LED会亮,第二次按下按键LED会灭,如此循环。 像要实现这个功能,可以先读取灯的值,当按键按下后,如果是高电平就…

2026/10/15 13:52:34 阅读更多 →
HyperFrames时间超分辨率全解析:从深度估计到慢动作补帧实战

HyperFrames时间超分辨率全解析:从深度估计到慢动作补帧实战

最近一段时间,“hyperframes”在视频处理圈子里出现的频率明显高了。这个词直译过来是“超帧”,但圈内讨论它的时候,基本都指向同一样东西:用深度学习做时间超分辨率(Temporal Super-Resolution)&#xff0…

2026/10/15 13:52:34 阅读更多 →
TPP技术解析:药物靶点发现的热力学蛋白质组学方法

TPP技术解析:药物靶点发现的热力学蛋白质组学方法

1. TPP技术概述:当热力学遇上蛋白质组学在药物研发领域,最令人头疼的问题莫过于"这个化合物到底作用于哪个靶点"。传统方法往往需要将蛋白质从细胞环境中分离出来进行研究,就像把鱼捞出水面观察其游动姿态——结果必然失真。TPP&am…

2026/10/15 13:52:34 阅读更多 →
R语言实战:基于KNN聚类的时间序列预测方法解析

R语言实战:基于KNN聚类的时间序列预测方法解析

大概半年前接了一个实际需求:预测某工厂循环水系统的运行负荷,为调度提供短期参考。数据拿回来倒是简单,一天24个小时一个点,连续一个月。可问题在于精度——ARIMA、指数平滑这类传统模型遇到周期性叠加随机波动、偶尔再来点异常数…

2026/10/15 13:51:29 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/15 13:48:15 阅读更多 →

月新闻

我发现了一个新思路:用 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/15 0:14:36 阅读更多 →
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/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练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/15 9:49:14 阅读更多 →