yggdrasil/src/components/frontend_layout.rs
xfy 0398cc6c66 perf(render): 列表加显式 key + release 启用 panic=abort
依据 Dioxus 0.7 optimizing 指南做两项改进:

- 为数据驱动列表渲染补全显式 key(13 处,9 个文件):
  home/archives/tags/search 文章与标签列表、post_card/post_footer
  标签、header 桌面/移动导航、admin dashboard/posts/write 列表。
  使用各类型稳定唯一字段(post.id / tag / tag.name / item.label /
  err.id / year_group.year / month_group.month_en)作为 key,
  让 Dioxus diff 正确识别元素身份。骨架屏 for _ in 0..N 静态占位
  循环与已带 key 的评论树/admin 行不动。

- [profile.release] 加 panic = "abort":WASM 去掉 unwind 元数据减小
  体积;server 端错误处理走 Result+?(见 error.rs),不依赖 panic
  unwind,无副作用。

其余文件为本机格式化(单行折叠/多行展开),一并提交。

验证:dx check 通过、cargo test 405 passed。
2026-06-25 18:07:22 +08:00

68 lines
2.3 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! 前台布局组件
//!
//! 包裹所有前台路由,提供统一的 Header、Footer 与主内容区容器,
//! 并为不同路由在 SuspenseBoundary 中展示对应的骨架屏。
use dioxus::prelude::*;
use crate::components::footer::Footer;
use crate::components::header::Header;
use crate::components::nav::use_nav_items;
use crate::components::skeletons::archive_skeleton::ArchiveSkeleton;
use crate::components::skeletons::delayed_skeleton::DelayedSkeleton;
use crate::components::skeletons::home_skeleton::HomeSkeleton;
use crate::components::skeletons::post_detail_skeleton::PostDetailSkeleton;
use crate::components::skeletons::search_skeleton::SearchSkeleton;
use crate::components::skeletons::tags_skeleton::TagsSkeleton;
use crate::router::Route;
use crate::theme::ThemeToggle;
/// 根据当前前台路由选择对应的骨架屏组件。
fn route_skeleton(route: &Route) -> Element {
match route {
Route::Archives {} => rsx! {
DelayedSkeleton { ArchiveSkeleton {} }
},
Route::Tags {} | Route::TagDetail { .. } => rsx! {
DelayedSkeleton { TagsSkeleton {} }
},
Route::Search {} => rsx! {
DelayedSkeleton { SearchSkeleton {} }
},
Route::PostDetail { .. } => rsx! {
DelayedSkeleton { PostDetailSkeleton {} }
},
Route::NotFound { .. } => rsx! {
div { class: "py-20 md:py-28" }
},
_ => rsx! {
DelayedSkeleton { HomeSkeleton {} }
},
}
}
/// 前台整体布局组件。
///
/// 负责渲染 Header含前台导航与主题切换、主内容区与 Footer
/// 并在路由内容加载过程中显示与路由匹配的骨架屏。
#[component]
pub fn FrontendLayout() -> Element {
let route = use_route::<Route>();
let nav_items = use_nav_items(route.clone());
rsx! {
div { class: "min-h-screen flex flex-col bg-paper-theme",
Header {
nav_items,
right_content: rsx! {
ThemeToggle {}
},
}
main { class: "flex-1 w-full max-w-3xl mx-auto px-6 py-6",
SuspenseBoundary { fallback: move |_| route_skeleton(&route), Outlet::<Route> {} }
}
Footer {}
}
}
}