perf(web): optimize bundle size — dynamic import Mapbox GL and code split Recharts

- Dynamic import ListingMap with next/dynamic (ssr: false) in /listings/[id] and /search pages
- Extract Recharts into lazy-loaded DistrictBarChart and PriceTrendChart components
- /listings/[id] first-load JS: 618KB → 149KB (-76%)
- /search first-load JS: 619KB → 150KB (-76%)
- Both pages now well under 300KB target

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Ho Ngoc Hai
2026-04-08 13:10:24 +07:00
parent 585fdc6ab6
commit 5848c2b386
6 changed files with 177 additions and 129 deletions

View File

@@ -1,17 +1,9 @@
'use client';
import dynamic from 'next/dynamic';
import Image from 'next/image';
import Link from 'next/link';
import { useEffect, useState } from 'react';
import {
BarChart,
Bar,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
ResponsiveContainer,
} from 'recharts';
import { ListingStatusBadge } from '@/components/listings/listing-status-badge';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
@@ -22,6 +14,11 @@ import {
} from '@/lib/analytics-api';
import { listingsApi, type ListingDetail, type PaginatedResult } from '@/lib/listings-api';
const DistrictBarChart = dynamic(
() => import('@/components/charts/district-bar-chart').then((mod) => mod.DistrictBarChart),
{ ssr: false, loading: () => <div className="flex h-64 items-center justify-center text-muted-foreground">Dang tai bieu do...</div> },
);
const CITY = 'Ho Chi Minh';
const PERIOD = '2026-Q1';
@@ -180,27 +177,12 @@ export default function DashboardPage() {
Chua co du lieu
</div>
) : (
<ResponsiveContainer width="100%" height={280}>
<BarChart data={chartData} margin={{ top: 5, right: 20, left: 0, bottom: 5 }}>
<CartesianGrid strokeDasharray="3 3" className="stroke-muted" />
<XAxis
dataKey="district"
tick={{ fontSize: 12 }}
className="fill-muted-foreground"
/>
<YAxis tick={{ fontSize: 12 }} className="fill-muted-foreground" />
<Tooltip
contentStyle={{
backgroundColor: 'hsl(var(--card))',
border: '1px solid hsl(var(--border))',
borderRadius: '0.5rem',
fontSize: '0.875rem',
}}
formatter={(value) => [`${value} tr/m2`, 'Gia']}
/>
<Bar dataKey="Gia/m2" fill="hsl(var(--primary))" radius={[4, 4, 0, 0]} />
</BarChart>
</ResponsiveContainer>
<DistrictBarChart
data={chartData}
height={280}
dataKey="Gia/m2"
tooltipFormatter={(value) => [`${value} tr/m2`, 'Gia']}
/>
)}
</CardContent>
</Card>