Гүйцэтгэлийн оновчлол
Програм зөв ажиллахаас гадна хурдан ажиллах нь чухал. Судалгаанаас үзэхэд хуудас 3 секундын дотор ачааллахгүй бол хэрэглэгчдийн тал хувь нь орхиж явдаг. TypeScript болон Next.js-д гүйцэтгэлийг сайжруулах олон арга байдаг.
Энэ хичээлд хамгийн их нөлөөтэй аргуудыг практик жишээтэй сурна.
React.memo болон useMemo
React component дахин render хийх нь гүйцэтгэлийн гол асуудал юм. React.memo болон useMemo нь шаардлагагүй дахин тооцоолохоос сэргийлдэг:
"use client";
import { memo, useMemo, useState } from "react";
// React.memo — props өөрчлөгдөөгүй бол дахин render хийхгүй
const LessonCard = memo(function LessonCard({
title,
order,
дууссан,
}: {
title: string;
order: number;
дууссан: boolean;
}) {
console.log(`LessonCard render: ${title}`); // хэр олон render хийхийг харна
return (
<div className="lesson-card">
<span>{order}.</span>
<span>{title}</span>
{дууссан && <span>✓</span>}
</div>
);
});
// useMemo — үнэтэй тооцоолол давтахгүй
function CourseProgress({ хичээлүүд }: { хичээлүүд: { дууссан: boolean }[] }) {
const [шүүлтүүр, setШүүлтүүр] = useState<"бүгд" | "дууссан">("бүгд");
// хичээлүүд өөрчлөгдөхөд л дахин тооцоолно — render бүрт биш
const статистик = useMemo(() => {
const нийт = хичээлүүд.length;
const дууссан = хичээлүүд.filter((х) => х.дууссан).length;
const хувь = нийт > 0 ? Math.round((дууссан / нийт) * 100) : 0;
return { нийт, дууссан, хувь };
}, [хичээлүүд]); // зөвхөн хичээлүүд өөрчлөгдөхөд
return (
<div>
<p>
{статистик.дууссан}/{статистик.нийт} хичээл ({статистик.хувь}%)
</p>
</div>
);
}
useCallback — функц дахин үүсгэхгүй байх
"use client";
import { useCallback, useState } from "react";
function ДэвшилтийнТовч({
хичээлийнСлуг,
дааллагаДуусгах,
}: {
хичээлийнСлуг: string;
дааллагаДуусгах: (slug: string) => void;
}) {
return (
<button onClick={() => дааллагаДуусгах(хичээлийнСлуг)}>
Дуусгах
</button>
);
}
function LessonPage({ courseSlug }: { courseSlug: string }) {
const [дууссанХичээлүүд, setДууссанХичээлүүд] = useState<string[]>([]);
// useCallback — courseSlug өөрчлөгдөхөд л шинэ функц үүсгэнэ
// Үгүй бол render бүрт шинэ функц үүсэж, ДэвшилтийнТовч дахин render хийнэ
const хичээлДуусгах = useCallback(
(slug: string) => {
setДууссанХичээлүүд((өмнөх) => [...өмнөх, slug]);
fetch("/api/progress", {
method: "POST",
body: JSON.stringify({ lessonSlug: slug, courseSlug, passed: true }),
headers: { "Content-Type": "application/json" },
});
},
[courseSlug] // courseSlug өөрчлөгдөхөд л шинэчлэгдэнэ
);
return <ДэвшилтийнТовч хичээлийнСлуг="01-intro" дааллагаДуусгах={хичээлДуусгах} />;
}
Next.js dynamic import — lazy loading
Том component-уудыг шаардлагатай үед л ачаалж, эхний bundle хэмжээг багасгана:
// app/courses/[courseSlug]/[lessonSlug]/page.tsx
import dynamic from "next/dynamic";
// Хуудас нээгдэх үед шууд ачааллахгүй — хэрэглэгч харах үед л ачаална
const LessonSidebar = dynamic(
() => import("@/components/lesson/LessonSidebar"),
{
loading: () => <div>Ачааллаж байна...</div>,
ssr: false, // client-side only
}
);
const TableOfContents = dynamic(
() => import("@/components/lesson/TableOfContents"),
{ ssr: false }
);
export default function LessonPage() {
return (
<div className="lesson-layout">
<LessonSidebar /> {/* хэрэглэгч харах үед л ачаална */}
<main>{/* хичээлийн агуулга */}</main>
<TableOfContents />
</div>
);
}
TypeScript-тэй холбоотой гүйцэтгэлийн зөвлөмжүүд
TypeScript хөрвүүлэлтийн үеийн (compile-time) хэрэгсэл тул ажиллах үеийн (runtime) гүйцэтгэлд шууд нөлөөлдөггүй. Гэхдээ буруу хэрэглэлт код чанарт нөлөөлнэ:
// ❌ Муу — any ашиглах нь type checking-ийг унтраадаг
function өгөгдөлБоловсруулах(өгөгдөл: any[]): any[] {
return өгөгдөл.map((з) => з.утга); // алдаа байж болох ч мэдэгдэхгүй
}
// ✅ Сайн — тодорхой төрөл нь IDE дэмжлэг болон оновчлолд тусалдаг
interface ДэвшилтийнОруулга {
хичээлийнСлуг: string;
дууссан: boolean;
огноо: Date;
}
function дэвшилБоловсруулах(өгөгдөл: ДэвшилтийнОруулга[]): string[] {
return өгөгдөл
.filter((з) => з.дууссан)
.map((з) => з.хичээлийнСлуг);
}
// ❌ Муу — асар том объект бүхэлдээ дамжуулах
function гарчигГаргах(хэрэглэгч: { нэр: string; и_мэйл: string; xp: number; /* ... */ }) {
return хэрэглэгч.нэр;
}
// ✅ Сайн — зөвхөн хэрэгтэй талбарыг Pick-ээр авна
function гарчигГаргах(хэрэглэгч: Pick<Хэрэглэгч, "нэр">) {
return хэрэглэгч.нэр;
}
Bundle хэмжээ шалгах
# Next.js-ийн дотоод bundle analyzer
ANALYZE=true npm run build
# Эсвэл @next/bundle-analyzer суулгах (үнэгүй)
npm install --save-dev @next/bundle-analyzer
next.config.ts дотор:
import type { NextConfig } from "next";
const тохиргоо: NextConfig = {
// Зөвхөн хөгжүүлэлтийн үед тооцоолол нуух
reactStrictMode: true,
// Зураг оновчлол — next/image автоматаар хийдэг
images: {
formats: ["image/avif", "image/webp"],
},
};
export default тохиргоо;
CLAUDE.md-д заасанчлан эхний JS bundle 200KB gzip-ээс бага байхыг зорино. Том library суулгахаас өмнө bundlephobia.com-д хэмжээг шалгах нь зуршил болгох хэрэгтэй.
Дараагийн хичээлд:
Курсын эцсийн төсөл — TypeScript-ийн сурсан бүх зүйлсийг нэгтгэн бодит програм бүтээнэ.