From 2ad9ebea808807783f09372b36e37d16ec396697 Mon Sep 17 00:00:00 2001 From: NowScott Date: Tue, 17 Mar 2026 17:11:03 +0800 Subject: [PATCH 1/2] =?UTF-8?q?perf(MainPage):=20=E4=BD=BF=E7=94=A8=20useM?= =?UTF-8?q?emo=20=E4=BC=98=E5=8C=96=E8=BF=87=E6=BB=A4=E6=80=A7=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 移除 useEffect 中的状态更新循环,改用 useMemo 直接计算 filteredPosts 和 visibleTags。这避免了不必要的渲染,并确保标签过滤与搜索查询的同步更新。 --- components/MainPage.js | 35 +++++++++++++++-------------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/components/MainPage.js b/components/MainPage.js index 98c89c2..929c9ce 100644 --- a/components/MainPage.js +++ b/components/MainPage.js @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import HeaderBar from './HeaderBar'; import Footer from './Footer'; import FontMenu from './FontMenu'; @@ -18,8 +18,6 @@ const MainPage = ({ initialPosts, initialTags, lastFetched: initialLastFetched } const [searchQuery, setSearchQuery] = useState(''); const [onList, setOnList] = useState([]); - const [filteredPosts, setFilteredPosts] = useState([]); - const [visibleTags, setVisibleTags] = useState(tags); // 初始化全局缓存 useEffect(() => { @@ -28,24 +26,21 @@ const MainPage = ({ initialPosts, initialTags, lastFetched: initialLastFetched } } }, [initialPosts]); - // 当源数据 posts, 标签过滤 onList 或 搜索内容 searchQuery 变化时更新结果 - useEffect(() => { - // 确保有数据时才进行过滤 - if (posts && posts.length > 0) { - const results = updateResults(posts, onList); - const finalResults = filterPostsBySearch(results, searchQuery); - const availableTags = extractTags(finalResults); - setFilteredPosts(finalResults); - setVisibleTags( - searchQuery.trim() - ? tags.filter(tag => onList.includes(tag) || availableTags.includes(tag)) - : tags - ); - } else { - setFilteredPosts([]); - setVisibleTags(searchQuery.trim() ? onList : tags); + const filteredPosts = useMemo(() => { + if (!posts || posts.length === 0) { + return []; + } + const results = updateResults(posts, onList); + return filterPostsBySearch(results, searchQuery); + }, [posts, onList, searchQuery]); + + const visibleTags = useMemo(() => { + if (!searchQuery.trim()) { + return tags; } - }, [posts, onList, searchQuery, tags]); + const availableTags = extractTags(filteredPosts); + return tags.filter(tag => onList.includes(tag) || availableTags.includes(tag)); + }, [tags, onList, searchQuery, filteredPosts]); const handleToggleTagButton = tag => { const newOnList = _.xor(onList, [tag]); From 27d6a68452e0f4d50899181531dd5993fde99180 Mon Sep 17 00:00:00 2001 From: NowScott Date: Tue, 17 Mar 2026 17:18:33 +0800 Subject: [PATCH 2/2] =?UTF-8?q?refactor:=20=E7=A7=BB=E9=99=A4=E6=9C=AA?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E7=9A=84=E5=AF=BC=E5=85=A5=E5=92=8C=E5=8F=98?= =?UTF-8?q?=E9=87=8F=E4=BB=A5=E7=AE=80=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 删除 MainPage 和 about 页面中未使用的导入 - 简化 ThemeContext 中 toggleTheme 函数的参数 - 移除 Footer 组件中未使用的 loading 状态和相关逻辑 - 清理 HeaderBar 组件中未使用的变量和导入 --- components/Footer.js | 6 +----- components/HeaderBar.js | 10 ++-------- components/MainPage.js | 1 - contexts/ThemeContext.js | 2 +- pages/about.js | 1 - 5 files changed, 4 insertions(+), 16 deletions(-) diff --git a/components/Footer.js b/components/Footer.js index 6b77779..1f3b56a 100644 --- a/components/Footer.js +++ b/components/Footer.js @@ -1,18 +1,16 @@ -import React, { useState, useEffect } from 'react'; +import React, { useEffect } from 'react'; import packageInfo from '../package.json'; import { useStats } from '../contexts/StatsContext'; import { HiOutlineCursorClick, HiOutlineTag, HiOutlineCode, HiOutlineGlobeAlt, HiOutlineClock } from 'react-icons/hi'; const Footer = () => { const { stats, updateStats } = useStats(); - const [loading, setLoading] = useState(false); useEffect(() => { // 如果已经有缓存的访问量,不再重复请求 if (stats.visitCount !== null) return; const fetchVisitCount = async () => { - setLoading(true); try { const response = await fetch('/api/visit-count'); if (!response.ok) { @@ -23,8 +21,6 @@ const Footer = () => { } catch (error) { console.error('获取访问量失败:', error); updateStats({ visitCount: 0 }); - } finally { - setLoading(false); } }; diff --git a/components/HeaderBar.js b/components/HeaderBar.js index d691426..74a7081 100644 --- a/components/HeaderBar.js +++ b/components/HeaderBar.js @@ -1,14 +1,12 @@ import React, { useEffect } from 'react'; import ThemeToggleButton from './ThemeToggleButton'; -import { useTheme } from '../contexts/ThemeContext'; import { useStats } from '../contexts/StatsContext'; -import { HiOutlineClock, HiOutlineGlobeAlt, HiOutlineInformationCircle, HiOutlineHome, HiOutlineSearch } from 'react-icons/hi'; +import { HiOutlineInformationCircle, HiOutlineHome, HiOutlineSearch } from 'react-icons/hi'; import Link from 'next/link'; import { useRouter } from 'next/router'; const HeaderBar = ({ lastFetched, count, searchQuery, setSearchQuery }) => { - const { mounted } = useTheme(); - const { stats, updateStats } = useStats(); + const { updateStats } = useStats(); const router = useRouter(); const isAboutPage = router.pathname === '/about'; @@ -19,10 +17,6 @@ const HeaderBar = ({ lastFetched, count, searchQuery, setSearchQuery }) => { } }, [count, lastFetched]); - // 优先使用全局 stats,如果没有则回退到 props - const displayCount = stats.count ?? count; - const displayTime = stats.lastFetched ?? lastFetched; - return (
diff --git a/components/MainPage.js b/components/MainPage.js index 929c9ce..77f9113 100644 --- a/components/MainPage.js +++ b/components/MainPage.js @@ -1,6 +1,5 @@ import React, { useEffect, useMemo, useState } from 'react'; import HeaderBar from './HeaderBar'; -import Footer from './Footer'; import FontMenu from './FontMenu'; import Tags from './Tags'; import WebList from './WebList'; diff --git a/contexts/ThemeContext.js b/contexts/ThemeContext.js index cfd9224..494b0c3 100644 --- a/contexts/ThemeContext.js +++ b/contexts/ThemeContext.js @@ -40,7 +40,7 @@ export const ThemeProvider = ({ children }) => { return () => mediaQuery.removeEventListener('change', handleChange); }, []); - const toggleTheme = (event) => { + const toggleTheme = () => { const toggle = (newTheme) => { document.documentElement.classList.toggle('dark', newTheme); diff --git a/pages/about.js b/pages/about.js index cb14dd6..0dd65ea 100644 --- a/pages/about.js +++ b/pages/about.js @@ -5,7 +5,6 @@ import FontMenu from '../components/FontMenu'; import { HiOutlineSparkles, HiOutlineCollection, HiOutlineHeart } from 'react-icons/hi'; import Head from 'next/head'; import { getDatabase } from '../lib/notion'; -import { unique } from '../lib/dataLoader'; import Link from 'next/link'; import { useStats } from '../contexts/StatsContext';