From 61c64841f930b6e7a2f51006d635ca1923aee1f3 Mon Sep 17 00:00:00 2001 From: xfy Date: Wed, 8 Jul 2026 14:18:08 +0800 Subject: [PATCH] =?UTF-8?q?refactor(skeletons):=20=E7=BB=9F=E4=B8=80?= =?UTF-8?q?=E9=AA=A8=E6=9E=B6=E5=B1=8F=E5=BB=B6=E8=BF=9F=E6=9C=BA=E5=88=B6?= =?UTF-8?q?=EF=BC=8C200ms=20=E5=86=85=E5=8A=A0=E8=BD=BD=E4=B8=8D=E6=98=BE?= =?UTF-8?q?=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - DelayedSkeleton 从「延迟 pulse 动画」改为「延迟渲染」: 前 200ms 完全不渲染,超时后才显示并带 pulse 动画 - admin_layout 从 use_delayed_loading hook 改为 DelayedSkeleton 包裹 - CommentListSkeleton 的两处裸用(section.rs / post_detail.rs) 补上 DelayedSkeleton 包裹 - 移除不再使用的 use_delayed_loading hook 模块 --- src/components/admin_layout.rs | 16 +++++---- src/components/comments/section.rs | 3 +- src/components/skeletons/delayed_skeleton.rs | 35 +++++++++++--------- src/hooks/mod.rs | 6 ++-- src/pages/post_detail.rs | 4 ++- 5 files changed, 35 insertions(+), 29 deletions(-) diff --git a/src/components/admin_layout.rs b/src/components/admin_layout.rs index 02e9d34..75d05bd 100644 --- a/src/components/admin_layout.rs +++ b/src/components/admin_layout.rs @@ -8,9 +8,9 @@ use dioxus::router::components::Link; use crate::api::auth::{get_current_user, logout}; use crate::components::admin_skeleton::AdminDashboardSkeleton; +use crate::components::skeletons::delayed_skeleton::DelayedSkeleton; use crate::components::write_skeleton::WriteSkeleton; use crate::context::UserContext; -use crate::hooks::delayed_loading::use_delayed_loading; use crate::router::Route; use crate::theme::ThemeToggle; @@ -19,7 +19,7 @@ pub fn AdminLayout() -> Element { let mut ctx: UserContext = use_context(); let navigator = dioxus::router::navigator(); let route = use_route::(); - let show_skeleton = use_delayed_loading(move || !(ctx.checked)()); + use_effect(move || { if !(ctx.checked)() { @@ -145,11 +145,13 @@ pub fn AdminLayout() -> Element { div { class: "flex-1 flex flex-col min-w-0 h-screen p-2 md:p-4", div { class: "flex-1 bg-[var(--color-paper-theme)] rounded-[2rem] shadow-sm border border-[var(--color-paper-border)] overflow-hidden relative flex flex-col", main { class: "{main_class}", - div { class: if show_skeleton() { "p-10" } else { "opacity-0" }, - { - match route { - Route::Write {} => rsx! { WriteSkeleton {} }, - _ => rsx! { AdminDashboardSkeleton {} }, + DelayedSkeleton { + div { class: "p-10", + { + match route { + Route::Write {} => rsx! { WriteSkeleton {} }, + _ => rsx! { AdminDashboardSkeleton {} }, + } } } } diff --git a/src/components/comments/section.rs b/src/components/comments/section.rs index 67cd609..c3f543d 100644 --- a/src/components/comments/section.rs +++ b/src/components/comments/section.rs @@ -9,6 +9,7 @@ use crate::api::comments::{check_pending_status, get_comments, CommentTreeRespon use crate::components::comments::form::CommentForm; use crate::components::comments::list::CommentList; use crate::components::skeletons::comment_skeleton::CommentListSkeleton; +use crate::components::skeletons::delayed_skeleton::DelayedSkeleton; use crate::utils::comment_storage::{self, PendingComment}; /// 评论上下文,供评论相关组件共享状态。 @@ -118,7 +119,7 @@ pub fn CommentSection(post_id: i32) -> Element { } } None => rsx! { - CommentListSkeleton {} + DelayedSkeleton { CommentListSkeleton {} } }, } } diff --git a/src/components/skeletons/delayed_skeleton.rs b/src/components/skeletons/delayed_skeleton.rs index 775d45c..74e5de0 100644 --- a/src/components/skeletons/delayed_skeleton.rs +++ b/src/components/skeletons/delayed_skeleton.rs @@ -1,36 +1,39 @@ //! 延迟骨架屏包装组件 //! -//! 让骨架屏先以静态灰色块立即显示,延迟一段时间后再启动 pulse 动画, -//! 避免快速加载时动画一闪而过带来的视觉抖动。 +//! 200ms 内完全不渲染骨架屏,避免快速加载时骨架屏一闪而过; +//! 超过 200ms 后渲染骨架屏并带有 pulse 动画,提示正在加载。 use crate::utils::time::sleep_ms; use dioxus::prelude::*; -/// 骨架屏 pulse 动画延迟(毫秒)。 +/// 骨架屏显示延迟(毫秒)。 /// -/// 加载时间低于此值时骨架屏只显示静态灰色块,避免 pulse 动画一闪而过。 -const SKELETON_PULSE_DELAY_MS: u32 = 200; +/// 加载时间低于此值时完全不显示骨架屏,避免闪烁。 +const SKELETON_DELAY_MS: u32 = 200; -/// 延迟 pulse 动画的骨架屏包装组件。 +/// 延迟渲染的骨架屏包装组件。 /// -/// 骨架屏区域**立即显示**(灰色静态占位块),避免空白闪烁。 -/// 延迟 `SKELETON_PULSE_DELAY_MS` 毫秒后,如果仍在加载,则启动 pulse 动画。 +/// 前 `SKELETON_DELAY_MS` 毫秒内不渲染任何内容; +/// 超过延迟后渲染子元素并启动 pulse 动画。 /// -/// 快加载(< 200ms):用户只看到静态灰色块一闪而过,无动画感知。 -/// 慢加载:灰色块正常 pulse,提示正在加载。 +/// 快加载(< 200ms):组件在渲染前就被卸载,用户完全看不到骨架屏。 +/// 慢加载:骨架屏正常显示并 pulse,提示正在加载。 #[component] pub fn DelayedSkeleton(children: Element) -> Element { - let mut pulsing = use_signal(|| false); + let mut visible = use_signal(|| false); - // 延迟启动 pulse 动画 use_effect(move || { spawn(async move { - sleep_ms(SKELETON_PULSE_DELAY_MS).await; - pulsing.set(true); + sleep_ms(SKELETON_DELAY_MS).await; + visible.set(true); }); }); - rsx! { - div { class: if pulsing() { "animate-pulse" } else { "" }, {children} } + if visible() { + rsx! { + div { class: "animate-pulse", {children} } + } + } else { + rsx! {} } } diff --git a/src/hooks/mod.rs b/src/hooks/mod.rs index b8c6d9a..b5ae98a 100644 --- a/src/hooks/mod.rs +++ b/src/hooks/mod.rs @@ -1,15 +1,13 @@ //! 共享的 Dioxus Hooks 模块。 //! //! 该模块集中管理可在组件树中复用的自定义 Hook,包括: -//! - 骨架屏延迟加载状态 //! - 通用 DOM 事件监听(注册 + 自动卸载清理) //! - 客户端分页数据加载 //! //! 评论草稿持久化(localStorage 读写)已迁至 `crate::utils::comment_storage`, //! 因为它是纯工具函数而非渲染期 Hook。 - -/// 延迟加载状态 Hook。 -pub mod delayed_loading; +//! +//! 骨架屏延迟渲染已统一至 `crate::components::skeletons::delayed_skeleton::DelayedSkeleton` 组件。 /// 通用 DOM 事件监听 Hook(add/remove_event_listener 生命周期封装)。 pub mod event_listener; diff --git a/src/pages/post_detail.rs b/src/pages/post_detail.rs index 29c1678..9a5052f 100644 --- a/src/pages/post_detail.rs +++ b/src/pages/post_detail.rs @@ -63,7 +63,9 @@ pub fn PostDetail(slug: String) -> Element { div { class: "mt-12 border-t border-gray-200 dark:border-gray-700 pt-8", SuspenseBoundary { fallback: move |_| rsx! { - crate::components::skeletons::comment_skeleton::CommentListSkeleton {} + DelayedSkeleton { + crate::components::skeletons::comment_skeleton::CommentListSkeleton {} + } }, crate::components::comments::section::CommentSection { post_id: post.id } }