Browse Source

chore: reduce bundle size by replacing heavy dependencies (#11698)

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: afc163 <afc163@gmail.com>
pull/11704/head
afc163 5 months ago
committed by GitHub
parent
commit
ed3e8ace89
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 1
      config/config.ts
  2. 1
      jest.config.ts
  3. 3
      package.json
  4. 70
      pnpm-lock.yaml
  5. 21
      src/components/Footer/index.tsx
  6. 4
      src/pages/account/center/components/Applications/index.tsx
  7. 8
      src/pages/dashboard/analysis/components/Charts/index.tsx
  8. 17
      src/pages/dashboard/analysis/components/IntroduceRow.tsx
  9. 4
      src/pages/dashboard/analysis/components/ProportionSales.tsx
  10. 6
      src/pages/dashboard/analysis/components/SalesCard.tsx
  11. 4
      src/pages/dashboard/analysis/components/TopSearch.tsx
  12. 17
      src/pages/dashboard/monitor/index.tsx
  13. 4
      src/pages/list/search/applications/index.tsx
  14. 4
      src/pages/user/login/__snapshots__/login.test.tsx.snap
  15. 3
      src/typings.d.ts
  16. 16
      src/utils/format.ts

1
config/config.ts

@ -200,5 +200,6 @@ export default defineConfig({
'process.env.CI': process.env.CI,
'process.env.COMMIT_HASH': process.env.COMMIT_HASH || '',
'process.env.CF_PAGES_COMMIT_SHA': process.env.CF_PAGES_COMMIT_SHA || '',
__APP_VERSION__: JSON.stringify(require('./../package.json').version),
},
});

1
jest.config.ts

@ -21,6 +21,7 @@ export default async (): Promise<any> => {
globals: {
...config.globals,
localStorage: null,
__APP_VERSION__: 'test',
},
};
};

3
package.json

@ -51,8 +51,6 @@
"antd-style": "^4.1.0",
"clsx": "^2.1.1",
"dayjs": "^1.11.19",
"git-url-parse": "^16.1.0",
"numeral": "^2.0.6",
"react": "^19.2.4",
"react-dom": "^19.2.4"
},
@ -66,7 +64,6 @@
"@types/express": "^5.0.6",
"@types/jest": "^30.0.0",
"@types/node": "^25.0.3",
"@types/numeral": "^2.0.5",
"@types/react": "^19.2.10",
"@types/react-dom": "^19.2.3",
"@types/react-helmet": "^6.1.11",

70
pnpm-lock.yaml

@ -50,12 +50,6 @@ importers:
dayjs:
specifier: ^1.11.19
version: 1.11.20
git-url-parse:
specifier: ^16.1.0
version: 16.1.0
numeral:
specifier: ^2.0.6
version: 2.0.6
react:
specifier: ^19.2.4
version: 19.2.5
@ -90,9 +84,6 @@ importers:
'@types/node':
specifier: ^25.0.3
version: 25.6.0
'@types/numeral':
specifier: ^2.0.5
version: 2.0.5
'@types/react':
specifier: ^19.2.10
version: 19.2.14
@ -2988,16 +2979,9 @@ packages:
'@types/normalize-package-data@2.4.4':
resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==}
'@types/numeral@2.0.5':
resolution: {integrity: sha512-kH8I7OSSwQu9DS9JYdFWbuvhVzvFRoCPCkGxNwoGgaPeDfEPJlcxNvEOypZhQ3XXHsGbfIuYcxcJxKUfJHnRfw==}
'@types/parse-json@4.0.2':
resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==}
'@types/parse-path@7.1.0':
resolution: {integrity: sha512-EULJ8LApcVEPbrfND0cRQqutIOdiIgJ1Mgrhpy755r14xMohPTEpkV/k28SJvuOs9bHRFW8x+KeDAEPiGQPB9Q==}
deprecated: This is a stub types definition. parse-path provides its own type definitions, so you do not need this installed.
'@types/pbf@3.0.5':
resolution: {integrity: sha512-j3pOPiEcWZ34R6a6mN07mUkM4o4Lwf6hPNt8eilOeZhTFbxFXmKhvXl9Y28jotFPaI1bpPDJsbCprUoNke6OrA==}
@ -5773,12 +5757,6 @@ packages:
engines: {node: '>=18'}
hasBin: true
git-up@8.1.1:
resolution: {integrity: sha512-FDenSF3fVqBYSaJoYy1KSc2wosx0gCvKP+c+PRBht7cAaiCeQlBtfBDX9vgnNOHmdePlSFITVcn4pFfcgNvx3g==}
git-url-parse@16.1.0:
resolution: {integrity: sha512-cPLz4HuK86wClEW7iDdeAKcCVlWXmrLpb2L+G9goW0Z1dtpNS6BXXSOckUTlJT/LDQViE1QZKstNORzHsLnobw==}
gl-matrix@3.4.4:
resolution: {integrity: sha512-latSnyDNt/8zYUB6VIJ6PCh2jBjJX6gnDsoCZ7LyW7GkqrD51EWwa9qCoGixj8YqBtETQK/xY7OmpTF8xz1DdQ==}
@ -6426,9 +6404,6 @@ packages:
resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==}
engines: {node: '>= 0.4'}
is-ssh@1.4.1:
resolution: {integrity: sha512-JNeu1wQsHjyHgn9NcWTaXq6zWSR6hqE0++zhfZlkFBbScNkyvxCdeV8sRkSBaeLKxmbpR21brail63ACNxJ0Tg==}
is-stream@1.1.0:
resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==}
engines: {node: '>=0.10.0'}
@ -7526,9 +7501,6 @@ packages:
number-to-words@1.2.4:
resolution: {integrity: sha512-/fYevVkXRcyBiZDg6yzZbm0RuaD6i0qRfn8yr+6D0KgBMOndFPxuW10qCHpzs50nN8qKuv78k8MuotZhcVX6Pw==}
numeral@2.0.6:
resolution: {integrity: sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA==}
nunjucks@3.2.4:
resolution: {integrity: sha512-26XRV6BhkgK0VOxfbU5cQI+ICFUtMLixv1noZn1tGU38kQH5A5nmmbk/O45xdyBhD1esk47nKrY0mvQpZIhRjQ==}
engines: {node: '>= 6.9.0'}
@ -7744,13 +7716,6 @@ packages:
resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==}
engines: {node: '>= 0.10'}
parse-path@7.1.0:
resolution: {integrity: sha512-EuCycjZtfPcjWk7KTksnJ5xPMvWGA/6i4zrLYhRG0hGvC3GPU/jGUj3Cy+ZR0v30duV3e23R95T1lE2+lsndSw==}
parse-url@9.2.0:
resolution: {integrity: sha512-bCgsFI+GeGWPAvAiUv63ZorMeif3/U0zaXABGJbOWt5OH2KCaPHF6S+0ok4aqM9RuIPGyZdx9tR9l13PsW4AYQ==}
engines: {node: '>=14.13.0'}
parse5@7.3.0:
resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==}
@ -8263,9 +8228,6 @@ packages:
protocol-buffers-schema@3.6.1:
resolution: {integrity: sha512-VG2K63Igkiv9p76tk1lilczEK1cT+kCjKtkdhw1dQZV3k3IXJbd3o6Ho8b9zJZaHSnT2hKe4I+ObmX9w6m5SmQ==}
protocols@2.0.2:
resolution: {integrity: sha512-hHVTzba3wboROl0/aWRRG9dMytgH6ow//STBZh43l/wQgmMhYhOFi0EHWAPtoCz9IAUymsyP0TSBHkhgMEGNnQ==}
proxy-addr@2.0.7:
resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
engines: {node: '>= 0.10'}
@ -13969,14 +13931,8 @@ snapshots:
'@types/normalize-package-data@2.4.4': {}
'@types/numeral@2.0.5': {}
'@types/parse-json@4.0.2': {}
'@types/parse-path@7.1.0':
dependencies:
parse-path: 7.1.0
'@types/pbf@3.0.5': {}
'@types/prismjs@1.26.6': {}
@ -17755,15 +17711,6 @@ snapshots:
- conventional-commits-filter
- conventional-commits-parser
git-up@8.1.1:
dependencies:
is-ssh: 1.4.1
parse-url: 9.2.0
git-url-parse@16.1.0:
dependencies:
git-up: 8.1.1
gl-matrix@3.4.4: {}
glob-parent@5.1.2:
@ -18429,10 +18376,6 @@ snapshots:
dependencies:
call-bound: 1.0.4
is-ssh@1.4.1:
dependencies:
protocols: 2.0.2
is-stream@1.1.0: {}
is-stream@2.0.1: {}
@ -19797,8 +19740,6 @@ snapshots:
number-to-words@1.2.4: {}
numeral@2.0.6: {}
nunjucks@3.2.4(chokidar@3.6.0):
dependencies:
a-sync-waterfall: 1.0.1
@ -20054,15 +19995,6 @@ snapshots:
parse-node-version@1.0.1: {}
parse-path@7.1.0:
dependencies:
protocols: 2.0.2
parse-url@9.2.0:
dependencies:
'@types/parse-path': 7.1.0
parse-path: 7.1.0
parse5@7.3.0:
dependencies:
entities: 6.0.1
@ -20537,8 +20469,6 @@ snapshots:
protocol-buffers-schema@3.6.1: {}
protocols@2.0.2: {}
proxy-addr@2.0.7:
dependencies:
forwarded: 0.2.0

21
src/components/Footer/index.tsx

@ -1,17 +1,22 @@
import { GithubOutlined } from '@ant-design/icons';
import { DefaultFooter } from '@ant-design/pro-components';
import packageJson from '@root/package.json';
import GitUrlParse from 'git-url-parse';
import React from 'react';
const getRepoUrl = () => {
if (!packageJson.repository)
return 'https://github.com/ant-design/ant-design-pro';
const parsed = GitUrlParse(packageJson.repository);
return `https://${parsed.source}/${parsed.owner}/${parsed.name}`;
const repo =
typeof packageJson.repository === 'string'
? packageJson.repository
: (packageJson.repository as { url: string }).url;
// Parse git URLs like git@github.com:owner/name.git or https://github.com/owner/name.git
const match = repo.match(/github\.com[:/]([^/]+)\/([^/.]+)/);
if (!match) return 'https://github.com/ant-design/ant-design-pro';
return `https://github.com/${match[1]}/${match[2]}`;
};
const REPO = getRepoUrl();
const REPO_URL = getRepoUrl();
// Git commit hash, can be updated via CI/CD (GitHub Actions or Cloudflare Pages)
const COMMIT_HASH =
@ -27,8 +32,8 @@ const Footer: React.FC = () => {
links={[
{
key: 'version',
title: `v${packageJson.version}`,
href: REPO,
title: `v${__APP_VERSION__}`,
href: REPO_URL,
blankTarget: true,
},
...(COMMIT_HASH
@ -36,7 +41,7 @@ const Footer: React.FC = () => {
{
key: 'commit',
title: COMMIT_HASH.slice(0, 7),
href: `${REPO}/commit/${COMMIT_HASH}`,
href: `${REPO_URL}/commit/${COMMIT_HASH}`,
blankTarget: true,
},
]
@ -49,7 +54,7 @@ const Footer: React.FC = () => {
Ant Design Pro
</>
),
href: REPO,
href: REPO_URL,
blankTarget: true,
},
]}

4
src/pages/account/center/components/Applications/index.tsx

@ -6,8 +6,8 @@ import {
} from '@ant-design/icons';
import { useQuery } from '@tanstack/react-query';
import { Avatar, Card, Dropdown, List, Tooltip } from 'antd';
import numeral from 'numeral';
import React from 'react';
import { formatNumber } from '@/utils/format';
import type { ListItemDataType } from '../../data.d';
import { queryFakeList } from '../../service';
import useStyles from './index.style';
@ -123,7 +123,7 @@ const Applications: React.FC = () => {
<div>
<CardInfo
activeUser={formatWan(item.activeUser)}
newUser={numeral(item.newUser).format('0,0')}
newUser={formatNumber(item.newUser)}
/>
</div>
</Card>

8
src/pages/dashboard/analysis/components/Charts/index.tsx

@ -1,13 +1,11 @@
import numeral from 'numeral';
import { formatYuan } from '@/utils/format';
import ChartCard from './ChartCard';
import Field from './Field';
const yuan = (val: number | string) => `¥ ${numeral(val).format('0,0')}`;
const Charts = {
yuan,
yuan: formatYuan,
ChartCard,
Field,
};
export { ChartCard, Charts as default, Field, yuan };
export { ChartCard, Charts as default, Field, formatYuan as yuan };

17
src/pages/dashboard/analysis/components/IntroduceRow.tsx

@ -1,7 +1,7 @@
import { InfoCircleOutlined } from '@ant-design/icons';
import { Area, Column } from '@ant-design/plots';
import { Col, Progress, Row, Tooltip } from 'antd';
import numeral from 'numeral';
import { formatNumber } from '@/utils/format';
import type { DataItem } from '../data.d';
import useStyles from '../style.style';
import Yuan from '../utils/Yuan';
@ -39,12 +39,7 @@ const IntroduceRow = ({
}
loading={loading}
total={() => <Yuan>126560</Yuan>}
footer={
<Field
label="日销售额"
value={`¥${numeral(12423).format('0,0')}`}
/>
}
footer={<Field label="日销售额" value={`¥${formatNumber(12423)}`} />}
contentHeight={46}
>
<Trend
@ -73,10 +68,8 @@ const IntroduceRow = ({
<InfoCircleOutlined />
</Tooltip>
}
total={numeral(8846).format('0,0')}
footer={
<Field label="日访问量" value={numeral(1234).format('0,0')} />
}
total={formatNumber(8846)}
footer={<Field label="日访问量" value={formatNumber(1234)} />}
contentHeight={46}
>
<Area
@ -105,7 +98,7 @@ const IntroduceRow = ({
<InfoCircleOutlined />
</Tooltip>
}
total={numeral(6560).format('0,0')}
total={formatNumber(6560)}
footer={<Field label="转化率" value="60%" />}
contentHeight={46}
>

4
src/pages/dashboard/analysis/components/ProportionSales.tsx

@ -1,7 +1,7 @@
import { Pie } from '@ant-design/plots';
import { Card, Segmented, Typography } from 'antd';
import numeral from 'numeral';
import React from 'react';
import { formatNumber } from '@/utils/format';
import type { DataItem } from '../data.d';
import useStyles from '../style.style';
@ -58,7 +58,7 @@ const ProportionSales = ({
label={{
position: 'spider',
text: (item: { x: number; y: number }) =>
`${item.x}: ${numeral(item.y).format('0,0')}`,
`${item.x}: ${formatNumber(item.y)}`,
}}
/>
</Card>

6
src/pages/dashboard/analysis/components/SalesCard.tsx

@ -1,7 +1,7 @@
import { Column } from '@ant-design/plots';
import { Button, Card, Col, DatePicker, Row, Tabs } from 'antd';
import type { RangePickerProps } from 'antd/es/date-picker';
import numeral from 'numeral';
import { formatNumber } from '@/utils/format';
import type { DataItem } from '../data.d';
import useStyles from '../style.style';
@ -147,7 +147,7 @@ const SalesCard = ({
>
{item.title}
</span>
<span>{numeral(item.total).format('0,0')}</span>
<span>{formatNumber(item.total)}</span>
</li>
))}
</ul>
@ -208,7 +208,7 @@ const SalesCard = ({
>
{item.title}
</span>
<span>{numeral(item.total).format('0,0')}</span>
<span>{formatNumber(item.total)}</span>
</li>
))}
</ul>

4
src/pages/dashboard/analysis/components/TopSearch.tsx

@ -1,8 +1,8 @@
import { InfoCircleOutlined } from '@ant-design/icons';
import { Area } from '@ant-design/plots';
import { Card, Col, Row, Table, Tooltip } from 'antd';
import numeral from 'numeral';
import React from 'react';
import { formatNumber } from '@/utils/format';
import type { DataItem } from '../data.d';
import NumberInfo from './NumberInfo';
import Trend from './Trend';
@ -105,7 +105,7 @@ const TopSearch = ({
</span>
}
gap={8}
total={numeral(12321).format('0,0')}
total={formatNumber(12321)}
status="up"
subTotal={17.1}
/>

17
src/pages/dashboard/monitor/index.tsx

@ -1,14 +1,15 @@
import { Gauge, Liquid, WordCloud } from '@ant-design/plots';
import { GridContent } from '@ant-design/pro-components';
import { useQuery } from '@tanstack/react-query';
import { Card, Col, Progress, Row, Statistic } from 'antd';
import numeral from 'numeral';
import type { FC } from 'react';
import { Card, Col, Progress, Row, Spin, Statistic } from 'antd';
import { type FC, lazy, Suspense } from 'react';
import { formatNumber } from '@/utils/format';
import ActiveChart from './components/ActiveChart';
import MonitorMap from './components/Map';
import { queryTags } from './service';
import useStyles from './style.style';
const MonitorMap = lazy(() => import('./components/Map'));
const deadline = Date.now() + 1000 * 60 * 60 * 24 * 2 + 1000 * 30; // Moment is also OK
const Monitor: FC = () => {
@ -43,7 +44,7 @@ const Monitor: FC = () => {
<Statistic
title="今日交易总额"
suffix="元"
value={numeral(124543233).format('0,0')}
value={formatNumber(124543233)}
/>
</Col>
<Col md={6} sm={12} xs={24}>
@ -61,12 +62,14 @@ const Monitor: FC = () => {
<Statistic
title="每秒交易总额"
suffix="元"
value={numeral(234).format('0,0')}
value={formatNumber(234)}
/>
</Col>
</Row>
<div className={styles.mapChart}>
<MonitorMap />
<Suspense fallback={<Spin />}>
<MonitorMap />
</Suspense>
</div>
</Card>
</Col>

4
src/pages/list/search/applications/index.tsx

@ -16,10 +16,10 @@ import {
Select,
Tooltip,
} from 'antd';
import numeral from 'numeral';
import type { FC } from 'react';
import React from 'react';
import { StandardFormRow, TagSelect } from '@/components';
import { formatNumber } from '@/utils/format';
import { categoryOptions } from '../../mock';
import type { ListItemDataType } from './data.d';
import { queryFakeList } from './service';
@ -232,7 +232,7 @@ export const Applications: FC<Record<string, any>> = () => {
<div>
<CardInfo
activeUser={formatWan(item.activeUser)}
newUser={numeral(item.newUser).format('0,0')}
newUser={formatNumber(item.newUser)}
/>
</div>
</Card>

4
src/pages/user/login/__snapshots__/login.test.tsx.snap

@ -473,7 +473,7 @@ exports[`Login Page should login success 1`] = `
target="_blank"
title="version"
>
v6.0.0-beta.4
vtest
</a>
<a
class="ant-pro-global-footer-list-link"
@ -984,7 +984,7 @@ exports[`Login Page should show login form 1`] = `
target="_blank"
title="version"
>
v6.0.0-beta.4
vtest
</a>
<a
class="ant-pro-global-footer-list-link"

3
src/typings.d.ts

@ -11,5 +11,6 @@ declare module '*.gif';
declare module '*.bmp';
declare module '*.tiff';
declare module 'omit.js';
declare module 'numeral';
declare module 'mockjs';
declare const __APP_VERSION__: string;

16
src/utils/format.ts

@ -0,0 +1,16 @@
const numberFormatter = new Intl.NumberFormat('en-US');
/**
* Format a number with thousand separators.
* Replaces numeral(val).format('0,0')
*/
export const formatNumber = (val: number | string): string => {
const parsed = Number(val);
return Number.isFinite(parsed) ? numberFormatter.format(parsed) : '';
};
/**
* Format a number as yuan currency string.
* Replaces `¥ ${numeral(val).format('0,0')}`
*/
export const formatYuan = (val: number | string) => `¥ ${formatNumber(val)}`;
Loading…
Cancel
Save