From 588ae49f5f4f3a47fe831cf9dd62d11ffe8f33e2 Mon Sep 17 00:00:00 2001 From: hypercross Date: Thu, 26 Feb 2026 14:51:26 +0800 Subject: [PATCH] fix: caching and reactivity --- src/App.tsx | 7 +++--- src/components/Article.tsx | 44 +++++++++++++++------------------ src/components/table.tsx | 50 ++++++++++++++++++++------------------ 3 files changed, 49 insertions(+), 52 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 08e1553..5bb4193 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,4 +1,4 @@ -import { Component, createSignal, onMount } from "solid-js"; +import { Component, createMemo, createSignal } from "solid-js"; import { useLocation } from "@solidjs/router"; // 导入组件以注册自定义元素 @@ -7,10 +7,9 @@ import { Article, Sidebar } from "./components"; const App: Component = () => { const location = useLocation(); - const [currentPath, setCurrentPath] = createSignal(""); const [isSidebarOpen, setIsSidebarOpen] = createSignal(false); - onMount(() => { + const currentPath = createMemo(() => { // 根据路由加载对应的 markdown 文件 let path = decodeURIComponent(location.pathname); @@ -18,7 +17,7 @@ const App: Component = () => { if (path.endsWith("/")) path += "index"; if (!path.endsWith(".md")) path += ".md"; - setCurrentPath(path); + return path; }); return ( diff --git a/src/components/Article.tsx b/src/components/Article.tsx index a45aea9..08deeb2 100644 --- a/src/components/Article.tsx +++ b/src/components/Article.tsx @@ -1,4 +1,4 @@ -import { Component, createSignal, onMount, onCleanup, Show } from 'solid-js'; +import { Component, createSignal, createEffect, onCleanup, Show, createResource } from 'solid-js'; import { parseMarkdown } from '../markdown'; import { fetchData, extractSection } from '../data-loader'; @@ -9,51 +9,45 @@ export interface ArticleProps { onError?: (error: Error) => void; } +async function fetchArticleContent(params: { src: string; section?: string }): Promise { + const text = await fetchData(params.src); + // 如果指定了 section,提取对应内容 + return params.section ? extractSection(text, params.section) : text; +} + /** * Article 组件 * 用于将特定 src 位置的 md 文件显示为 markdown 文章 */ export const Article: Component = (props) => { - const [content, setContent] = createSignal(''); - const [loading, setLoading] = createSignal(true); - const [error, setError] = createSignal(null); + const [content, { refetch }] = createResource( + () => ({ src: props.src, section: props.section }), + fetchArticleContent + ); let articleRef: HTMLArticleElement | undefined; - onMount(async () => { - setLoading(true); - try { - const text = await fetchData(props.src); - // 如果指定了 section,提取对应内容 - const finalContent = props.section - ? extractSection(text, props.section) - : text; - setContent(finalContent); - setLoading(false); + createEffect(() => { + const data = content(); + if (data) { props.onLoaded?.(); - } catch (err) { - const errorObj = err instanceof Error ? err : new Error(String(err)); - setError(errorObj); - setLoading(false); - props.onError?.(errorObj); } }); onCleanup(() => { // 清理时清空内容,触发内部组件的销毁 - setContent(''); }); return (
- +
加载中...
- -
加载失败:{error()?.message}
+ +
加载失败:{content.error?.message}
- -
+ +
); diff --git a/src/components/table.tsx b/src/components/table.tsx index ca86a6f..4fceb5b 100644 --- a/src/components/table.tsx +++ b/src/components/table.tsx @@ -1,5 +1,5 @@ import { customElement, noShadowDOM } from 'solid-element'; -import { createSignal, For, Show, createEffect, createMemo } from 'solid-js'; +import { createSignal, For, Show, createEffect, createMemo, createResource } from 'solid-js'; import { parse } from 'csv-parse/browser/esm/sync'; import { marked } from '../markdown'; @@ -10,12 +10,14 @@ interface TableRow { [key: string]: string; } +// 全局缓存已加载的 CSV 内容 +const csvCache = new Map(); + customElement('md-table', { roll: false, remix: false }, (props, { element }) => { noShadowDOM(); const [rows, setRows] = createSignal([]); const [activeTab, setActiveTab] = createSignal(0); const [activeGroup, setActiveGroup] = createSignal(null); - const [loaded, setLoaded] = createSignal(false); const [bodyHtml, setBodyHtml] = createSignal(''); // 从 element 的 textContent 获取 CSV 路径 @@ -41,34 +43,36 @@ customElement('md-table', { roll: false, remix: false }, (props, { element }) => const resolvedSrc = resolvePath(articlePath, src); - // 解析 CSV 内容 - const parseCSV = (content: string) => { + // 加载 CSV 文件的函数 + const loadCSV = async (path: string): Promise => { + // 先从缓存获取 + if (csvCache.has(path)) { + return csvCache.get(path)!; + } + const response = await fetch(path); + const content = await response.text(); const records = parse(content, { columns: true, comment: '#', trim: true, skipEmptyLines: true }); - setRows(records as TableRow[]); - setLoaded(true); + const result = records as TableRow[]; + // 缓存结果 + csvCache.set(path, result); + return result; }; - // 加载 CSV 文件 - const loadCSV = async () => { - try { - const response = await fetch(resolvedSrc); - const content = await response.text(); - parseCSV(content); - } catch (error) { - console.error('Failed to load CSV:', error); - setLoaded(true); + // 使用 createResource 加载 CSV,自动响应路径变化并避免重复加载 + const [csvData] = createResource(() => resolvedSrc, loadCSV); + + // 当数据加载完成后更新 rows + createEffect(() => { + const data = csvData(); + if (data) { + setRows(data); } - }; - - // 初始化加载 - if (!loaded()) { - loadCSV(); - } + }); // 检测是否有 group 列 const hasGroup = createMemo(() => { @@ -119,7 +123,7 @@ customElement('md-table', { roll: false, remix: false }, (props, { element }) => // 更新 body 内容 const updateBodyContent = () => { const filtered = filteredRows(); - if (loaded() && filtered.length > 0) { + if (!csvData.loading && filtered.length > 0) { const index = Math.min(activeTab(), filtered.length - 1); const currentRow = filtered[index]; setBodyHtml(processBody(currentRow.body, currentRow)); @@ -207,7 +211,7 @@ customElement('md-table', { roll: false, remix: false }, (props, { element }) =>
- 0}> + 0}>