Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reduce svg size & lzay load github.svg and grid.svg #580

Merged
merged 5 commits into from
Dec 22, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ module.exports = {
extends: [
'eslint:recommended',
'plugin:react/recommended',
'plugin:react/jsx-runtime',
'plugin:prettier/recommended',
],
overrides: [],
Expand Down
1 change: 1 addition & 0 deletions assets/index.tsx
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export const yearStats = import.meta.glob('./year_*.svg', { import: 'ReactComponent' })
export const totalStat = import.meta.glob(['./github.svg', './grid.svg'], { import: 'ReactComponent' })
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"dependencies": {
"@mapbox/mapbox-gl-language": "^1.0.0",
"@mapbox/polyline": "^1.1.1",
"@svgr/plugin-svgo": "^8.1.0",
"@vercel/analytics": "^0.1.6",
"@vitejs/plugin-react": "^4.0.0",
"gcoord": "^0.3.2",
Expand Down
1 change: 0 additions & 1 deletion src/components/Header/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Link } from 'react-router-dom';
import React from 'react';
import useSiteMetadata from '@/hooks/useSiteMetadata';

const Header = () => {
Expand Down
1 change: 0 additions & 1 deletion src/components/LocationStat/CitiesStat.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react';
import Stat from '@/components/Stat';
import useActivities from '@/hooks/useActivities';

Expand Down
1 change: 0 additions & 1 deletion src/components/LocationStat/LocationSummary.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react';
import Stat from '@/components/Stat';
import useActivities from '@/hooks/useActivities';

Expand Down
1 change: 0 additions & 1 deletion src/components/LocationStat/PeriodStat.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react';
import Stat from '@/components/Stat';
import useActivities from '@/hooks/useActivities';

Expand Down
1 change: 0 additions & 1 deletion src/components/LocationStat/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react';
import YearStat from '@/components/YearStat';
import {
CHINESE_LOCATION_INFO_MESSAGE_FIRST,
Expand Down
1 change: 0 additions & 1 deletion src/components/RunMap/RunMapButtons.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react';
import useActivities from '@/hooks/useActivities';
import styles from './style.module.scss';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { ReactComponent as EndSvg } from '@assets/end.svg';
import { ReactComponent as StartSvg } from '@assets/start.svg';
import React from 'react';
import { Marker } from 'react-map-gl';
import styles from './style.module.scss';

Expand Down
6 changes: 3 additions & 3 deletions src/components/RunMap/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
MAP_HEIGHT,
} from '@/utils/const';
import { Coordinate, IViewState, geoJsonForMap } from '@/utils/utils';
import RunMarker from './RunMaker';
import RunMarker from './RunMarker';
import RunMapButtons from './RunMapButtons';
import styles from './style.module.scss';
import { FeatureCollection } from 'geojson';
Expand Down Expand Up @@ -82,7 +82,7 @@ const RunMap = ({
[endLon, endLat] = points[points.length - 1];
}
let dash = USE_DASH_LINE && !isSingleRun ? [2, 2] : [2, 0];
const onMove = React.useCallback(({ viewState }: {viewState: IViewState}) => {
const onMove = React.useCallback(({ viewState }: { viewState: IViewState }) => {
setViewState(viewState);
}, []);
const style: React.CSSProperties = {
Expand All @@ -98,7 +98,7 @@ const RunMap = ({

return (
<Map
{ ...viewState }
{...viewState}
onMove={onMove}
style={style}
mapStyle="mapbox://styles/mapbox/dark-v10"
Expand Down
1 change: 0 additions & 1 deletion src/components/RunTable/RunRow.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react';
import { formatPace, titleForRun, formatRunTime, Activity, RunIds } from '@/utils/utils';
import styles from './style.module.scss';

Expand Down
32 changes: 27 additions & 5 deletions src/components/SVGStat/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,34 @@
import React from 'react';
import { ReactComponent as GitHubSvg } from '@assets/github.svg';
import { ReactComponent as GridSvg } from '@assets/grid.svg';
import { ComponentType, lazy, Suspense } from 'react'
import styles from './style.module.scss';
import { totalStat } from '@assets/index'

// Lazy load both github.svg and grid.svg
const GithubSvg = lazy(() =>
totalStat['./github.svg']()
.then((res) => {
return { default: res as ComponentType<any> }
})
.catch(() => {
return { default: () => <div>Failed to load SVG</div> };
})
)

const GridSvg = lazy(() =>
totalStat['./grid.svg']()
.then((res) => {
return { default: res as ComponentType<any> }
})
.catch(() => {
return { default: () => <div>Failed to load SVG</div> };
})
)

const SVGStat = () => (
<div id="svgStat">
<GitHubSvg className={styles.runSVG} />
<GridSvg className={styles.runSVG} />
<Suspense fallback={<div className={styles.center}>Loading...</div>}>
<GithubSvg className={styles.runSVG} />
<GridSvg className={styles.runSVG} />
</Suspense>
</div>
);

Expand Down
4 changes: 4 additions & 0 deletions src/components/SVGStat/style.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,7 @@
width: 100%;
margin: 1rem 0 0;
}

.center {
text-align: center;
}
4 changes: 2 additions & 2 deletions src/components/Stat/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import { CSSProperties } from 'react';
import { intComma } from '@/utils/utils';

const divStyle: React.CSSProperties = {
const divStyle: CSSProperties = {
fontWeight: '700',
};

Expand Down
19 changes: 10 additions & 9 deletions src/components/YearStat/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import { ComponentType, lazy, Suspense } from 'react';
import Stat from '@/components/Stat';
import useActivities from '@/hooks/useActivities';
import { formatPace } from '@/utils/utils';
Expand All @@ -11,12 +11,13 @@ const YearStat = ({ year, onClick }: { year: string, onClick: (_year: string) =>
// for hover
const [hovered, eventHandlers] = useHover();
// lazy Component
const YearSVG = React.lazy(() => yearStats[`./year_${year}.svg`]()
.then((res) => ({ default: res }))
.catch((err) => {
console.error(err);
return { default: () => <div>Failed to load SVG</div> };
})
const YearSVG = lazy(() =>
yearStats[`./year_${year}.svg`]()
.then((res) => ({ default: res as ComponentType<any> }))
.catch((err) => {
console.error(err);
return { default: () => <div>Failed to load SVG</div> };
})
);

if (years.includes(year)) {
Expand Down Expand Up @@ -71,9 +72,9 @@ const YearStat = ({ year, onClick }: { year: string, onClick: (_year: string) =>
)}
</section>
{year !== "Total" && hovered && (
<React.Suspense fallback="loading...">
<Suspense fallback="loading...">
<YearSVG className={styles.yearSVG} />
</React.Suspense>
</Suspense>
)}
<hr color="red" />
</div>
Expand Down
11 changes: 5 additions & 6 deletions src/components/YearsStat/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react';
import YearStat from '@/components/YearStat';
import useActivities from '@/hooks/useActivities';
import { INFO_MESSAGE } from '@/utils/const';
Expand All @@ -25,11 +24,11 @@ const YearsStat = ({ year, onClick }: { year: string, onClick: (_year: string) =
<YearStat key={year} year={year} onClick={onClick} />
))}
{// eslint-disable-next-line no-prototype-builtins
yearsArrayUpdate.hasOwnProperty('Total') ? (
<YearStat key="Total" year="Total" onClick={onClick} />
) : (
<div />
)}
yearsArrayUpdate.hasOwnProperty('Total') ? (
<YearStat key="Total" year="Total" onClick={onClick} />
) : (
<div />
)}
</div>
);
};
Expand Down
1 change: 0 additions & 1 deletion src/pages/404.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react';
import Layout from '@/components/Layout';
import useSiteMetadata from '@/hooks/useSiteMetadata';

Expand Down
52 changes: 26 additions & 26 deletions src/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useEffect, useState } from 'react';
import { Analytics } from '@vercel/analytics/react';
import React, { useEffect, useState } from 'react';
import Layout from '@/components/Layout';
import LocationStat from '@/components/LocationStat';
import RunMap from '@/components/RunMap';
Expand Down Expand Up @@ -48,7 +48,7 @@ const Index = () => {
func: (_run: Activity, _value: string) => boolean
) => {
scrollToMap();
if(name != 'Year'){
if (name != 'Year') {
setYear(thisYear)
}
setActivity(filterAndSortRuns(activities, item, func, sortDateFunc));
Expand Down Expand Up @@ -132,19 +132,26 @@ const Index = () => {
if (!svgStat) {
return;
}
svgStat.addEventListener('click', (e) => {

const handleClick = (e: Event) => {
const target = e.target as HTMLElement;
if (target) {
const tagName = target.tagName.toLowerCase();

// click the github-stat style svg
if (
tagName === 'rect' &&
parseFloat(target.getAttribute('width') || '0.0') === 2.6 &&
parseFloat(target.getAttribute('height') || '0.0') === 2.6 &&
target.getAttribute('fill') !== '#444444'
) {
const [runDate] = target.innerHTML.match(/\d{4}-\d{1,2}-\d{1,2}/) || [
if (target.tagName.toLowerCase() === 'path') {
// Use querySelector to get the <desc> element and the <title> element.
const descEl = target.querySelector('desc');
if (descEl) {
// If the runId exists in the <desc> element, it means that a running route has been clicked.
const runId = Number(descEl.innerHTML);
if (!runId) {
return;
}
locateActivity([runId]);
return;
}

const titleEl = target.querySelector('title');
if (titleEl) {
// If the runDate exists in the <title> element, it means that a date square has been clicked.
const [runDate] = titleEl.innerHTML.match(/\d{4}-\d{1,2}-\d{1,2}/) || [
Comment on lines +136 to +154
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice refactor

`${+thisYear + 1}`,
];
const runIDsOnDate = runs
Expand All @@ -154,20 +161,13 @@ const Index = () => {
return;
}
locateActivity(runIDsOnDate);
} else if (tagName === 'polyline') {
// click the route grid svg
const desc = target.getElementsByTagName('desc')[0];
if (!desc) {
return;
}
const run_id = Number(desc.innerHTML);
if (!run_id) {
return;
}
locateActivity([run_id]);
}
}
});
}
svgStat.addEventListener('click', handleClick);
return () => {
svgStat && svgStat.removeEventListener('click', handleClick);
};
}, [year]);

return (
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"dom",
"esnext"
] /* Specify a set of bundled library declaration files that describe the target runtime environment. */,
"jsx": "react" /* Specify what JSX code is generated. */,
"jsx": "react-jsx" /* Specify what JSX code is generated. */,
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */
Expand Down
1 change: 1 addition & 0 deletions vite-env.d.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
/// <reference types="vite/client" />
/// <reference types="vite-plugin-svgr/client" />
48 changes: 43 additions & 5 deletions vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,61 @@ import process from 'node:process';
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import viteTsconfigPaths from 'vite-tsconfig-paths';
import svgrPlugin from 'vite-plugin-svgr';
import svgr from 'vite-plugin-svgr';

// The following are known larger packages or packages that can be loaded asynchronously.
const individuallyPackages = ['activities', 'github.svg', 'grid.svg'];

// https://vitejs.dev/config/
export default defineConfig({
plugins: [react(), viteTsconfigPaths(), svgrPlugin()],
plugins: [
react(),
viteTsconfigPaths(),
svgr({
include: ['**/*.svg'],
svgrOptions: {
exportType: 'named',
namedExport: 'ReactComponent',
plugins: ['@svgr/plugin-svgo', '@svgr/plugin-jsx'],
svgoConfig: {
floatPrecision: 2,
plugins: [
{
name: 'preset-default',
params: {
overrides: {
removeTitle: false,
removeViewBox: false,
},
},
},
],
},
},
}),
],
base: process.env.PATH_PREFIX || '/',
build: {
manifest: true,
outDir: './dist', // for user easy to use, vercel use default dir -> dist
rollupOptions: {
output: {
manualChunks: (id: string) => {
if (id.includes('activities')) {
return 'activities'
if (id.includes('node_modules')) {
return 'vendors';
// If there will be more and more external packages referenced in the future,
// the following approach can be considered.
// const name = id.split('node_modules/')[1].split('/');
// return name[0] == '.pnpm' ? name[1] : name[0];
} else {
for (const item of individuallyPackages) {
if (id.includes(item)) {
return item;
}
}
}
},
}
},
},
},
});