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:
@@ -1,16 +1,28 @@
|
||||
'use client';
|
||||
|
||||
import dynamic from 'next/dynamic';
|
||||
import Link from 'next/link';
|
||||
import { useParams } from 'next/navigation';
|
||||
import * as React from 'react';
|
||||
import { ImageGallery } from '@/components/listings/image-gallery';
|
||||
import { ListingMap } from '@/components/map/listing-map';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { listingsApi, type ListingDetail } from '@/lib/listings-api';
|
||||
import { PROPERTY_TYPES, DIRECTIONS, TRANSACTION_TYPES } from '@/lib/validations/listings';
|
||||
|
||||
const ListingMap = dynamic(
|
||||
() => import('@/components/map/listing-map').then((mod) => mod.ListingMap),
|
||||
{
|
||||
ssr: false,
|
||||
loading: () => (
|
||||
<div className="flex h-[300px] items-center justify-center rounded-lg bg-muted">
|
||||
<p className="text-sm text-muted-foreground">Dang tai ban do...</p>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
);
|
||||
|
||||
function formatPrice(priceVND: string): string {
|
||||
const num = Number(priceVND);
|
||||
if (num >= 1_000_000_000) return `${(num / 1_000_000_000).toFixed(1)} tỷ`;
|
||||
|
||||
@@ -1,13 +1,25 @@
|
||||
'use client';
|
||||
|
||||
import dynamic from 'next/dynamic';
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import * as React from 'react';
|
||||
import { ListingMap } from '@/components/map/listing-map';
|
||||
import { FilterBar, type SearchFilters } from '@/components/search/filter-bar';
|
||||
import { SearchResults } from '@/components/search/search-results';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { listingsApi, type ListingDetail, type PaginatedResult } from '@/lib/listings-api';
|
||||
|
||||
const ListingMap = dynamic(
|
||||
() => import('@/components/map/listing-map').then((mod) => mod.ListingMap),
|
||||
{
|
||||
ssr: false,
|
||||
loading: () => (
|
||||
<div className="flex h-[calc(100vh-220px)] items-center justify-center rounded-lg bg-muted">
|
||||
<p className="text-sm text-muted-foreground">Dang tai ban do...</p>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
);
|
||||
|
||||
type ViewMode = 'list' | 'map' | 'split';
|
||||
|
||||
const defaultFilters: SearchFilters = {
|
||||
|
||||
Reference in New Issue
Block a user