Skip to content

Commit cad2e74

Browse files
CodFrmclaude
andcommitted
⚡ 前端性能优化:动态加载、减少bundle体积、降低轮询频率
- ThemeClientContext 用 useMemo 缓存 antdTheme 对象,避免每次渲染重建 - next.config.ts 添加 optimizePackageImports 优化 antd/icons 等大库 tree-shaking - prebuild 脚本只复制必要的 Monaco Editor 文件(14MB → ~5MB) - MonacoEditor/MarkdownEditor/Charts 改为 next/dynamic 按需加载,减少首屏 JS ~1MB - 通知未读数轮询间隔 60s → 120s,关闭 revalidateOnFocus 减少不必要请求 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 06730ff commit cad2e74

10 files changed

Lines changed: 97 additions & 20 deletions

File tree

next.config.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,16 @@ const withNextIntl = createNextIntlPlugin('./src/i18n/request.ts');
66
const nextConfig: NextConfig = {
77
env: {},
88
output: 'standalone',
9+
experimental: {
10+
optimizePackageImports: [
11+
'antd',
12+
'@ant-design/icons',
13+
'@ant-design/charts',
14+
'@iconify/react',
15+
'dayjs',
16+
'prismjs',
17+
],
18+
},
919
async rewrites() {
1020
return [
1121
{

scripts/prebuild.tsx

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React from 'react';
22
import fs from 'fs';
3+
import path from 'path';
34
import { ConfigProvider, theme } from 'antd';
45
import { extractStyle } from '@ant-design/static-style-extract';
56

@@ -29,9 +30,38 @@ fs.mkdirSync('./public/styles', { recursive: true });
2930
// 生成css
3031
fs.writeFileSync(outputPath, css);
3132

32-
// 拷贝monaco
33-
fs.cpSync(
34-
'./node_modules/monaco-editor/min/vs',
35-
'./public/assets/monaco-editor/min/vs',
36-
{ recursive: true },
37-
);
33+
// 拷贝monaco - 只复制必要的文件,减少体积(14MB -> ~5MB)
34+
const monacoSrc = './node_modules/monaco-editor/min/vs';
35+
const monacoDst = './public/assets/monaco-editor/min/vs';
36+
37+
// 核心文件(必须)
38+
fs.mkdirSync(monacoDst, { recursive: true });
39+
fs.copyFileSync(path.join(monacoSrc, 'loader.js'), path.join(monacoDst, 'loader.js'));
40+
41+
// 中文语言包
42+
const nlsFile = 'nls.messages.zh-cn.js';
43+
if (fs.existsSync(path.join(monacoSrc, nlsFile))) {
44+
fs.copyFileSync(path.join(monacoSrc, nlsFile), path.join(monacoDst, nlsFile));
45+
}
46+
47+
// base(核心运行时)和 editor(编辑器主体)
48+
for (const dir of ['base', 'editor']) {
49+
fs.cpSync(path.join(monacoSrc, dir), path.join(monacoDst, dir), { recursive: true });
50+
}
51+
52+
// language - 只复制 typescript 和 json(JS 由 typescript worker 处理)
53+
for (const lang of ['typescript', 'json']) {
54+
fs.cpSync(
55+
path.join(monacoSrc, 'language', lang),
56+
path.join(monacoDst, 'language', lang),
57+
{ recursive: true },
58+
);
59+
}
60+
61+
// basic-languages - 只复制 javascript、typescript、css
62+
for (const lang of ['javascript', 'typescript', 'css']) {
63+
const langSrc = path.join(monacoSrc, 'basic-languages', lang);
64+
if (fs.existsSync(langSrc)) {
65+
fs.cpSync(langSrc, path.join(monacoDst, 'basic-languages', lang), { recursive: true });
66+
}
67+
}

src/app/[locale]/script-show-page/[id]/code/components/ScriptCodeClient.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@
22

33
import { Card } from 'antd';
44
import React from 'react';
5-
import MonacoEditor from '@/components/MonacoEditor';
5+
import dynamic from 'next/dynamic';
6+
7+
const MonacoEditor = dynamic(() => import('@/components/MonacoEditor'), {
8+
ssr: false,
9+
loading: () => <div style={{ height: '600px' }} />,
10+
});
611
import type { ScriptInfo } from '../../types';
712

813
type ScriptCodeClientProps = {

src/app/[locale]/script-show-page/[id]/diff/components/ScriptDiffClient.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,12 @@
33
import { Card, Tag, Divider, Alert } from 'antd';
44
import React from 'react';
55
import { useTranslations } from 'next-intl';
6-
import MonacoDiffEditor from '@/components/MonacoEditor/DiffEditor';
6+
import dynamic from 'next/dynamic';
7+
8+
const MonacoDiffEditor = dynamic(
9+
() => import('@/components/MonacoEditor/DiffEditor'),
10+
{ ssr: false, loading: () => <div style={{ height: '600px' }} /> },
11+
);
712
import type { ScriptInfo } from '../../types';
813

914
type ScriptDiffClientProps = {

src/app/[locale]/script-show-page/[id]/issue/[issueId]/components/IssueCommentClient.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,12 @@ import {
2929
import { CopyToClipboard } from 'react-copy-to-clipboard';
3030
import { useSemDateTime } from '@/lib/utils/semdate';
3131
import type { MarkdownEditorRef } from '@/components/MarkdownEditor';
32-
import MarkdownEditor from '@/components/MarkdownEditor';
32+
import dynamic from 'next/dynamic';
33+
34+
const MarkdownEditor = dynamic(() => import('@/components/MarkdownEditor'), {
35+
ssr: false,
36+
loading: () => <div style={{ height: '200px' }} />,
37+
});
3338
import MarkdownView from '@/components/MarkdownView';
3439
import ActionMenu from '@/components/ActionMenu';
3540
import type { IssueComment } from '@/lib/api/services/scripts/issue';

src/app/[locale]/script-show-page/[id]/issue/create/CreateIssueClient.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,12 @@ import { Button, Card, Input, Select, Space, message } from 'antd';
55
import { useTranslations } from 'next-intl';
66
import { useRouter } from '@/i18n/routing';
77
import type { MarkdownEditorRef } from '@/components/MarkdownEditor';
8-
import MarkdownEditor from '@/components/MarkdownEditor';
8+
import dynamic from 'next/dynamic';
9+
10+
const MarkdownEditor = dynamic(() => import('@/components/MarkdownEditor'), {
11+
ssr: false,
12+
loading: () => <div style={{ height: '300px' }} />,
13+
});
914
import { useScript } from '../../components/ScriptContext';
1015
import { scriptIssueService } from '@/lib/api/services/scripts/issue';
1116
import IssueLabel from '../components/IssueLabel';

src/app/[locale]/script-show-page/[id]/statistic/components/ScriptStatsClient.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,16 @@ import {
2929
} from '@ant-design/icons';
3030
import React, { useState } from 'react';
3131
import { useScript } from '../../components/ScriptContext';
32-
import { Line, Column } from '@ant-design/charts';
32+
import dynamic from 'next/dynamic';
33+
34+
const Line = dynamic(() => import('@ant-design/charts').then((mod) => mod.Line), {
35+
ssr: false,
36+
loading: () => <div style={{ height: 300 }} />,
37+
});
38+
const Column = dynamic(
39+
() => import('@ant-design/charts').then((mod) => mod.Column),
40+
{ ssr: false, loading: () => <div style={{ height: 300 }} /> },
41+
);
3342
import { useTheme } from '@/contexts/ThemeClientContext';
3443
import { useTranslations } from 'next-intl';
3544
import { useScriptStatistics, useScriptRealtime } from '@/lib/api/hooks/script';

src/components/ScriptEditor/index.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,17 @@ import {
2525
import React, { useRef, useState, useCallback, useEffect } from 'react';
2626
import { useTranslations } from 'next-intl';
2727
import type { MonacoEditorRef } from '@/components/MonacoEditor';
28-
import MonacoEditor from '@/components/MonacoEditor';
2928
import type { MarkdownEditorRef } from '@/components/MarkdownEditor';
30-
import MarkdownEditor from '@/components/MarkdownEditor';
29+
import dynamic from 'next/dynamic';
30+
31+
const MonacoEditor = dynamic(() => import('@/components/MonacoEditor'), {
32+
ssr: false,
33+
loading: () => <div style={{ height: '500px' }} />,
34+
});
35+
const MarkdownEditor = dynamic(() => import('@/components/MarkdownEditor'), {
36+
ssr: false,
37+
loading: () => <div style={{ height: '300px' }} />,
38+
});
3139
import {
3240
parseMetadata,
3341
parseTags,

src/contexts/ThemeClientContext.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client';
22

3-
import { useContext, createContext, useState, useEffect } from 'react';
3+
import { useContext, createContext, useState, useEffect, useMemo } from 'react';
44
import type { ThemeConfig } from 'antd';
55
import { ConfigProvider, theme } from 'antd';
66
import type { ThemeMode } from '@/lib/cookies';
@@ -34,7 +34,7 @@ export const ThemeClientProvider: React.FC<ThemeClientProviderProps> = ({
3434
initialMode || { mode: 'auto', theme: 'light' },
3535
);
3636

37-
const antdTheme: ThemeConfig = {
37+
const antdTheme: ThemeConfig = useMemo(() => ({
3838
algorithm:
3939
themeMode.theme === 'dark' ? theme.darkAlgorithm : theme.defaultAlgorithm,
4040
token: {
@@ -168,7 +168,7 @@ export const ThemeClientProvider: React.FC<ThemeClientProviderProps> = ({
168168
colorBgContainer: themeMode.theme === 'dark' ? '#161b22' : '#ffffff',
169169
},
170170
},
171-
};
171+
}), [themeMode.theme]);
172172

173173
useEffect(() => {
174174
// 记录到cookie

src/lib/api/hooks/notification.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,11 @@ export function useUnreadCount() {
4343
},
4444
{
4545
// 自动刷新
46-
refreshInterval: 60 * 1000, // 每分钟刷新一次
47-
revalidateOnFocus: true,
46+
refreshInterval: 120 * 1000, // 每2分钟刷新一次
47+
revalidateOnFocus: false,
4848
revalidateOnReconnect: true,
49-
// 缓存时间30秒
50-
dedupingInterval: 30 * 1000,
49+
// 缓存时间60秒
50+
dedupingInterval: 60 * 1000,
5151
},
5252
);
5353
}

0 commit comments

Comments
 (0)