לעתים קרובות אנו נתקלים במצב: אתר מסחר אלקטרוני מבוסס React לוקח 8 שניות לטעינה במכשירים ניידים. הסיבה היא חבילת JS מונוליטית במשקל 2 MB. הדפדפן חייב להוריד, לנתח ולהריץ את כל הקוד לפני הצגת תוכן כלשהו. זה מחמיר את INP ו-FCP, ומפחית את ההמרה ב-30%. הצוות שלנו מציע אופטימיזציה שיטתית של החבילה: פיצול קוד לפי נתיבים, tree shaking, ייבוא עצלני, והחלפת ספריות כבדות. מפתח עד מפתח — ניתוח, שיפוץ, ניטור CI. אנו מבטיחים להפחית את ה-JS הראשוני ל-50 kB. אנו יכולים להעריך את הפרויקט שלך תוך יומיים.
ניתוח חבילה וגילוי בעיות
# Vite — визуализация через rollup-plugin-visualizer npm install -D rollup-plugin-visualizer // vite.config.ts
import { visualizer } from 'rollup-plugin-visualizer';
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [
visualizer({
filename: 'dist/stats.html',
open: true,
gzipSize: true,
})
],
build: {
rollupOptions: {
output: {
manualChunks: {
'vendor-react': ['react', 'react-dom', 'react-router-dom'],
'vendor-ui': ['@radix-ui/react-dialog', '@radix-ui/react-dropdown-menu'],
'vendor-query': ['@tanstack/react-query'],
},
}
},
chunkSizeWarningLimit: 500,
}
});
לאחר הבנייה, נפתחת מפת חבילה אינטראקטיבית. אנו מחפשים ספריות גדולות (moment.js, lodash), תלויות כפולות, וייבוא של ספריות שלמות במקום הפונקציה הדרושה. בממוצע, ביקורת כזו אורכת 1–2 שעות וחושפת את נקודות ה"השמנה" העיקריות.
פיצול קוד לפי נתיבים
פיצול קוד מחלק את החבילה לחלקים לפי נתיב. המשתמש מוריד רק את הקוד עבור העמוד הנוכחי. זה מקטין את גודל הטעינה הראשונית ומאיץ את INP. עבור React אנו משתמשים ב-# Vite — визуализация через rollup-plugin-visualizer npm install -D rollup-plugin-visualizer ו-// vite.config.ts import { visualizer } from 'rollup-plugin-visualizer'; import { defineConfig } from 'vite'; export default defineConfig({ plugins: [ visualizer({ filename: 'dist/stats.html', open: true, gzipSize: true, }) ], build: { rollupOptions: { output: { manualChunks: { 'vendor-react': ['react', 'react-dom', 'react-router-dom'], 'vendor-ui': ['@radix-ui/react-dialog', '@radix-ui/react-dropdown-menu'], 'vendor-query': ['@tanstack/react-query'], }, } }, chunkSizeWarningLimit: 500, } }); :
// React Router v6 — lazy loading страниц
import { lazy, Suspense } from 'react';
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
const ProductCatalog = lazy(() => import('./pages/ProductCatalog'));
const ProductDetail = lazy(() => import('./pages/ProductDetail'));
const Cart = lazy(() => import('./pages/Cart'));
const Checkout = lazy(() => import('./pages/Checkout'));
const router = createBrowserRouter([
{
path: '/catalog',
element: (
<Suspense fallback={<PageSkeleton />}>
<ProductCatalog />
</Suspense>
),
},
{
path: '/products/:slug',
element: (
<Suspense fallback={<PageSkeleton />}>
<ProductDetail />
</Suspense>
),
},
{
path: '/cart',
element: (
<Suspense fallback={<PageSkeleton />}>
<Cart />
</Suspense>
),
},
{
path: '/checkout',
element: (
<Suspense fallback={<PageSkeleton />}>
<Checkout />
</Suspense>
),
},
]);
// Динамический импорт тяжёлых компонентов (редактор, графики, карты)
const RichTextEditor = lazy(() => import('./components/RichTextEditor'));
const Chart = lazy(() => import('./components/Chart'));
function ProductForm() {
const [showEditor, setShowEditor] = useState(false);
return (
<>
<button onClick={() => setShowEditor(true)}>Добавить описание</button>
{showEditor && (
<Suspense fallback={<div>Загрузка редактора...</div>}>
<RichTextEditor />
</Suspense>
)}
</>
);
} Tree Shaking והחלפת ספריות
Tree shaking מסיר קוד שאינו בשימוש, ומקטין את גודל ה-JS. זה מפחית את זמן הניתוח וההרצה, ומשפר את FCP ו-INP. Vite ו-Webpack מוציאים אוטומטית קוד מת כאשר הייבוא מאורגן כראוי.
// Плохо: импорт всего lodash (~70кБ gzip)
import _ from 'lodash';
const sorted = _.sortBy(products, 'price');
// Хорошо: импорт только нужной функции
import sortBy from 'lodash/sortBy';
const sorted = sortBy(products, 'price');
// Ещё лучше: нативный JS
const sorted = [...products].sort((a, b) => a.price - b.price);
// date-fns вместо moment.js
import { format, addDays } from 'date-fns'; // tree-shakeable
import { ru } from 'date-fns/locale'; טבלת החלפת ספריות
| ספרייה | תחליף | חיסכון |
|---|---|---|
| moment.js (72kB) | date-fns (רק הפונקציות הדרושות) | ~60kB (קטן פי 3.6) |
| lodash (70kB) | lodash-es + tree-shaking | ~50kB |
| axios (13kB) | fetch מקורי | 13kB |
| jquery (87kB) | JS מקורי | 87kB |
| react-icons (הכל) | רק הדרוש מ-@heroicons | 100–500kB |
למה Tree Shaking לא תמיד עובד?
Tree shaking יעיל רק עבור מודולי ES. אם ספרייה מייצאת CommonJS (require), קוד מת לא יוסר. בדוק אם החבילה תומכת ב-React.lazy או השתמש ב-Suspense במקום // React Router v6 — lazy loading страниц import { lazy, Suspense } from 'react'; import { createBrowserRouter, RouterProvider } from 'react-router-dom'; const ProductCatalog = lazy(() => import('./pages/ProductCatalog')); const ProductDetail = lazy(() => import('./pages/ProductDetail')); const Cart = lazy(() => import('./pages/Cart')); const Checkout = lazy(() => import('./pages/Checkout')); const router = createBrowserRouter([ { path: '/catalog', element: <Suspense fallback={<PageSkeleton />}><ProductCatalog /></Suspense> }, { path: '/products/:slug', element: <Suspense fallback={<PageSkeleton />}><ProductDetail /></Suspense> }, { path: '/cart', element: <Suspense fallback={<PageSkeleton />}><Cart /></Suspense> }, { path: '/checkout', element: <Suspense fallback={<PageSkeleton />}><Checkout /></Suspense> }, ]); // Динамический импорт тяжёлых компонентов (редактор, графики, карты) const RichTextEditor = lazy(() => import('./components/RichTextEditor')); const Chart = lazy(() => import('./components/Chart')); function ProductForm() { const [showEditor, setShowEditor] = useState(false); return ( <> <button onClick={() => setShowEditor(true)}>Добавить описание</button> {showEditor && ( <Suspense fallback={<div>Загрузка редактора...</div>}> <RichTextEditor /> </Suspense> )} </> ); } . כמו כן, ודא שהפרויקט שלך לא מפעיל את הפלאגין // Плохо: импорт всего lodash (~70кБ gzip) import _ from 'lodash'; const sorted = _.sortBy(products, 'price'); // Хорошо: импорт только нужной функции import sortBy from 'lodash/sortBy'; const sorted = sortBy(products, 'price'); // Ещё лучше: нативный JS const sorted = [...products].sort((a, b) => a.price - b.price); // date-fns вместо moment.js import { format, addDays } from 'date-fns'; // tree-shakeable import { ru } from 'date-fns/locale'; ללא האפשרות "type": "module". ב-SPA בעיה זו נפוצה, במיוחד עם חבילות ישנות. אנו עוזרים לבצע מעבר לחלופות מודרניות, מה שמשפר את Core Web Vitals.
מה כלול בעבודה
- ניתוח הבנייה הנוכחית ודוח עם המלצות.
- יישום פיצול קוד וטעינה עצלנית.
- החלפת ספריות כבדות תוך שמירה על הפונקציונליות.
- הגדרת tree shaking ואופטימיזציה של chunks.
- שילוב ניטור גודל חבילה ב-CI/CD.
- תיעוד ומדריך לאופטימיזציה עתידית.
- תמיכת מהנדס למשך שבועיים לאחר הפריסה.
תהליך האופטימיזציה
- ביקורת על החבילה הנוכחית באמצעות Vite visualizer או Webpack Bundle Analyzer.
- זיהוי ספריות גדולות וכפילויות.
- פיתוח אסטרטגיית פיצול קוד לפי נתיבים ורכיבים.
- החלפת ספריות כבדות בחלופות קלות משקל.
- הגדרת tree shaking ו-chunks ידניים ב-Vite/Rollup.
- יישום prefetching של chunks קריטיים (prefetch בעת ריחוף).
- שילוב בדיקות גודל חבילה ב-CI.
- תיעוד תוצאות והדרכת הצוות.
לוחות זמנים ותוצאות
מיומיים עד 5 ימים בהתאם למורכבות הפרויקט. כולל ביקורת, פיצול קוד, החלפת ספריות והגדרת CI. אנו מבטיחים להפחית את ה-JavaScript הראשוני ל-50 kB. אופטימיזציה זו חוסכת תעבורה ומשפרת את חוויית המשתמש, ומשפיעה ישירות על המרה והכנסות. הזמן ביקורת כדי לקבל מספרים ספציפיים עבור הפרויקט שלך.
מקרה בוחן
עבור אתר מסחר אלקטרוני אחד (React, Next.js), החבילה המקורית שקלה 1.5 MB. לאחר פיצול קוד לפי עמודים והחלפת moment.js ב-date-fns, ה-JS הראשוני ירד ל-180 kB. LCP השתפר מ-4.2 ל-1.1 שניות, ו-INP מ-450 ל-120 אלפיות השנייה. ההמרה עלתה ב-12%. כל הפרויקט ארך 4 ימים. חיסכון בתעבורת CDN היה כ-30% בחודש.
ניטור CI ומדדים
ערכי יעד לאתר מסחר אלקטרוני (gzip):
| Chunk | יעד |
|---|---|
| JS ראשוני (נתיב קריטי) | < 50 kB |
| React + React DOM | ~42 kB |
| עמוד קטלוג | < 30 kB |
| עמוד מוצר | < 20 kB |
| עגלה/תשלום | < 40 kB |
---
# .github/workflows/bundle-size.yml
- name: Check bundle size
run: |
npm run build
MAIN_JS=$(ls dist/assets/index-*.js | xargs stat -c%s | head -1)
if [ "$MAIN_JS" -gt 200000 ]; then
echo "Bundle too large: ${MAIN_JS} bytes"
exit 1
fi
למידע נוסף על מודולי JavaScript, ראה תיעוד MDN. אנו מבטיחים שיפור במדדי Core Web Vitals. צור קשר כדי לקבל ייעוץ עבור הפרויקט שלך. האצת טעינת האתר היא השקעה בהצלחתו, ואנו כאן כדי לעזור להשיג זאת.







