بر API پروفایلر ریاکت مسلط شوید. یاد بگیرید چگونه گلوگاههای عملکردی را تشخیص دهید، رندرهای غیرضروری را رفع کنید و اپلیکیشن خود را با مثالهای عملی و بهترین شیوهها بهینه سازید.
دستیابی به اوج عملکرد: نگاهی عمیق به API پروفایلر ریاکت
در دنیای توسعه وب مدرن، تجربه کاربری حرف اول را میزند. یک رابط کاربری روان و پاسخگو میتواند عامل تعیینکننده بین یک کاربر خشنود و یک کاربر ناامید باشد. برای توسعهدهندگانی که از ریاکت استفاده میکنند، ساخت رابطهای کاربری پیچیده و پویا آسانتر از همیشه شده است. با این حال، با افزایش پیچیدگی اپلیکیشنها، خطر گلوگاههای عملکردی نیز افزایش مییابد—ناکامیهای ظریفی که میتوانند منجر به تعاملات کند، انیمیشنهای پرشدار و در نهایت تجربه کاربری ضعیف شوند. اینجاست که API پروفایلر ریاکت به ابزاری ضروری در زرادخانه یک توسعهدهنده تبدیل میشود.
این راهنمای جامع شما را به یک بررسی عمیق از پروفایلر ریاکت میبرد. ما بررسی خواهیم کرد که این ابزار چیست، چگونه میتوان از آن به طور مؤثر از طریق ابزارهای توسعهدهنده ریاکت و API برنامهنویسی آن استفاده کرد، و مهمتر از همه، چگونه خروجی آن را برای تشخیص و رفع مشکلات رایج عملکردی تفسیر کنیم. در پایان، شما مجهز خواهید شد تا تحلیل عملکرد را از یک کار دلهرهآور به بخشی سیستماتیک و ارزشمند از گردش کار توسعه خود تبدیل کنید.
API پروفایلر ریاکت چیست؟
پروفایلر ریاکت یک ابزار تخصصی است که برای کمک به توسعهدهندگان در اندازهگیری عملکرد یک اپلیکیشن ریاکت طراحی شده است. وظیفه اصلی آن جمعآوری اطلاعات زمانبندی درباره هر کامپوننتی است که در اپلیکیشن شما رندر میشود، و به شما این امکان را میدهد که تشخیص دهید کدام بخشهای اپلیکیشن شما برای رندر شدن هزینه بالایی دارند و ممکن است باعث مشکلات عملکردی شوند.
این ابزار به سوالات حیاتی مانند اینها پاسخ میدهد:
- رندر شدن یک کامپوننت خاص چقدر طول میکشد؟
- یک کامپوننت در طول یک تعامل کاربر چند بار دوباره رندر میشود؟
- چرا یک کامپوننت خاص دوباره رندر شد؟
مهم است که پروفایلر ریاکت را از ابزارهای عمومی عملکرد مرورگر مانند تب Performance در Chrome DevTools یا Lighthouse متمایز کنیم. در حالی که آن ابزارها برای اندازهگیری بارگذاری کلی صفحه، درخواستهای شبکه و زمان اجرای اسکریپت عالی هستند، پروفایلر ریاکت یک دید متمرکز و در سطح کامپوننت از عملکرد درون اکوسیستم ریاکت به شما میدهد. این ابزار چرخه حیات ریاکت را درک میکند و میتواند ناکارآمدیهای مربوط به تغییرات state، props و context را که ابزارهای دیگر نمیتوانند ببینند، مشخص کند.
پروفایلر به دو شکل اصلی در دسترس است:
- افزونه React DevTools: یک رابط کاربری گرافیکی و کاربرپسند که مستقیماً در ابزارهای توسعهدهنده مرورگر شما ادغام شده است. این رایجترین راه برای شروع پروفایلینگ است.
- کامپوننت برنامهنویسی `
`: کامپوننتی که میتوانید مستقیماً به کد JSX خود اضافه کنید تا اندازهگیریهای عملکرد را به صورت برنامهنویسی جمعآوری کنید، که برای تست خودکار یا ارسال متریکها به یک سرویس تحلیلی مفید است.
نکته مهم این است که پروفایلر برای محیطهای توسعه طراحی شده است. اگرچه یک بیلد پروداکشن ویژه با پروفایلینگ فعال وجود دارد، بیلد استاندارد پروداکشن ریاکت این قابلیت را حذف میکند تا کتابخانه برای کاربران نهایی شما تا حد امکان سبک و سریع باقی بماند.
شروع کار: چگونه از پروفایلر ریاکت استفاده کنیم
بیایید عملی کار کنیم. پروفایل کردن اپلیکیشن شما یک فرآیند ساده است و درک هر دو روش به شما حداکثر انعطافپذیری را میدهد.
روش ۱: تب Profiler در React DevTools
برای اکثر دیباگهای عملکردی روزمره، تب Profiler در React DevTools ابزار اصلی شماست. اگر آن را نصب نکردهاید، این اولین قدم است—افزونه را برای مرورگر مورد نظر خود (کروم، فایرفاکس، اج) دریافت کنید.
در اینجا یک راهنمای گام به گام برای اجرای اولین جلسه پروفایلینگ شما آورده شده است:
- اپلیکیشن خود را باز کنید: به اپلیکیشن ریاکت خود که در حالت توسعه در حال اجرا است، بروید. اگر آیکون ریاکت را در نوار افزونههای مرورگر خود مشاهده کنید، متوجه میشوید که DevTools فعال است.
- ابزارهای توسعهدهنده را باز کنید: ابزارهای توسعهدهنده مرورگر خود را باز کنید (معمولاً با F12 یا Ctrl+Shift+I / Cmd+Option+I) و تب "Profiler" را پیدا کنید. اگر تبهای زیادی دارید، ممکن است پشت یک فلش "»" پنهان شده باشد.
- شروع پروفایلینگ: یک دایره آبی (دکمه ضبط) در رابط کاربری Profiler خواهید دید. برای شروع ضبط دادههای عملکرد، روی آن کلیک کنید.
- با اپلیکیشن خود تعامل کنید: عملی را که میخواهید اندازهگیری کنید، انجام دهید. این میتواند هر چیزی باشد، از بارگذاری یک صفحه، کلیک کردن روی دکمهای که یک مودال را باز میکند، تایپ کردن در یک فرم، یا فیلتر کردن یک لیست بزرگ. هدف بازتولید تعامل کاربری است که کند به نظر میرسد.
- توقف پروفایلینگ: پس از اتمام تعامل، دوباره روی دکمه ضبط کلیک کنید (اکنون قرمز خواهد بود) تا جلسه متوقف شود.
همین! Profiler دادههایی را که جمعآوری کرده پردازش میکند و یک نمایش تصویری دقیق از عملکرد رندر اپلیکیشن شما در طول آن تعامل را به شما ارائه میدهد.
روش ۲: کامپوننت برنامهنویسی `Profiler`
در حالی که DevTools برای دیباگ تعاملی عالی است، گاهی اوقات نیاز دارید دادههای عملکرد را به صورت خودکار جمعآوری کنید. کامپوننت `
شما میتوانید هر بخشی از درخت کامپوننت خود را با کامپوننت `
- `id` (رشته): یک شناسه منحصر به فرد برای بخشی از درخت که در حال پروفایل کردن آن هستید. این به شما کمک میکند تا اندازهگیریهای پروفایلرهای مختلف را از هم تشخیص دهید.
- `onRender` (تابع): یک تابع callback که ریاکت هر بار که یک کامپوننت در داخل درخت پروفایل شده یک آپدیت را "commit" میکند، آن را فراخوانی میکند.
در اینجا یک نمونه کد آورده شده است:
import React, { Profiler } from 'react';
// کالبک onRender
function onRenderCallback(
id, // پراپ "id" درختی از Profiler که به تازگی commit شده است
phase, // "mount" (اگر درخت برای اولین بار mount شده باشد) یا "update" (اگر دوباره رندر شده باشد)
actualDuration, // زمان صرف شده برای رندر آپدیت commit شده
baseDuration, // زمان تخمینی برای رندر کل زیردرخت بدون مموسازی (memoization)
startTime, // زمانی که ریاکت شروع به رندر این آپدیت کرد
commitTime, // زمانی که ریاکت این آپدیت را commit کرد
interactions // مجموعهای از تعاملات که باعث این آپدیت شدند
) {
// میتوانید این دادهها را لاگ کنید، به یک سرویس تحلیلی ارسال کنید یا آنها را جمعآوری کنید.
console.log({
id,
phase,
actualDuration,
baseDuration,
startTime,
commitTime,
});
}
function App() {
return (
);
}
درک پارامترهای کالبک `onRender`:
- `id`: رشته `id` که به کامپوننت `
` پاس دادهاید. - `phase`: یا `"mount"` (کامپوننت برای اولین بار mount شد) یا `"update"` (به دلیل تغییرات در props، state یا hooks دوباره رندر شد).
- `actualDuration`: زمانی که برای رندر `
` و فرزندانش برای این آپدیت خاص صرف شده است، به میلیثانیه. این متریک کلیدی شما برای شناسایی رندرهای کند است. - `baseDuration`: تخمینی از مدت زمانی که برای رندر کل زیردرخت از ابتدا لازم است. این سناریوی "بدترین حالت" است و برای درک پیچیدگی کلی یک درخت کامپوننت مفید است. اگر `actualDuration` بسیار کوچکتر از `baseDuration` باشد، نشان میدهد که بهینهسازیهایی مانند مموسازی (memoization) به طور مؤثر کار میکنند.
- `startTime` و `commitTime`: تایماستمپهایی برای زمانی که ریاکت شروع به رندر کرد و زمانی که آپدیت را به DOM کامیت کرد. اینها میتوانند برای ردیابی عملکرد در طول زمان استفاده شوند.
- `interactions`: مجموعهای از "تعاملات" که هنگام برنامهریزی آپدیت در حال ردیابی بودند (این بخشی از یک API آزمایشی برای ردیابی علت آپدیتها است).
تفسیر خروجی پروفایلر: یک تور راهنما
پس از توقف یک جلسه ضبط در React DevTools، با انبوهی از اطلاعات روبرو میشوید. بیایید بخشهای اصلی رابط کاربری را بررسی کنیم.
انتخابگر Commit
در بالای پروفایلر، یک نمودار میلهای خواهید دید. هر میله در این نمودار نشاندهنده یک "commit" است که ریاکت در طول ضبط شما به DOM انجام داده است. ارتفاع و رنگ میله نشان میدهد که رندر آن commit چقدر طول کشیده است—میلههای بلندتر و زرد/نارنجی گرانتر از میلههای کوتاهتر و آبی/سبز هستند. میتوانید روی این میلهها کلیک کنید تا جزئیات هر چرخه رندر خاص را بررسی کنید.
نمودار Flamegraph
این قدرتمندترین ابزار تصویری است. برای یک commit انتخاب شده، flamegraph به شما نشان میدهد که کدام کامپوننتها در اپلیکیشن شما رندر شدهاند. در اینجا نحوه خواندن آن آمده است:
- سلسله مراتب کامپوننتها: نمودار مانند درخت کامپوننت شما ساختار یافته است. کامپوننتهای بالایی کامپوننتهای پایینی خود را فراخوانی کردهاند.
- زمان رندر: عرض میله یک کامپوننت با مدت زمانی که آن و فرزندانش برای رندر شدن صرف کردهاند، مطابقت دارد. میلههای عریضتر آنهایی هستند که باید ابتدا بررسی کنید.
- کدگذاری رنگی: رنگ میله نیز زمان رندر را نشان میدهد، از رنگهای سرد (آبی، سبز) برای رندرهای سریع تا رنگهای گرم (زرد، نارنجی، قرمز) برای رندرهای کند.
- کامپوننتهای خاکستری: یک میله خاکستری به این معنی است که کامپوننت در طول این commit خاص دوباره رندر نشده است. این یک نشانه عالی است! به این معنی است که استراتژیهای مموسازی شما احتمالاً برای آن کامپوننت کار میکنند.
نمودار Ranked
اگر flamegraph بیش از حد پیچیده به نظر میرسد، میتوانید به نمای نمودار Ranked بروید. این نما به سادگی تمام کامپوننتهایی را که در طول commit انتخاب شده رندر شدهاند، لیست میکند و بر اساس اینکه کدام یک طولانیترین زمان را برای رندر شدن صرف کرده، مرتب شدهاند. این یک راه فوقالعاده برای شناسایی فوری گرانترین کامپوننتهای شماست.
پنل جزئیات کامپوننت
وقتی روی یک کامپوننت خاص در نمودار Flamegraph یا Ranked کلیک میکنید، یک پنل جزئیات در سمت راست ظاهر میشود. اینجاست که شما کاربردیترین اطلاعات را پیدا میکنید:
- مدت زمان رندر: این پنل `actualDuration` و `baseDuration` را برای آن کامپوننت در commit انتخاب شده نشان میدهد.
- "Rendered at": این قسمت تمام commitهایی را که این کامپوننت در آنها رندر شده است لیست میکند و به شما امکان میدهد به سرعت ببینید که چقدر آپدیت میشود.
- "Why did this render?": این اغلب با ارزشترین قطعه اطلاعات است. React DevTools تمام تلاش خود را میکند تا به شما بگوید چرا یک کامپوننت دوباره رندر شده است. دلایل رایج عبارتند از:
- پراپها تغییر کردهاند
- هوکها تغییر کردهاند (مثلاً مقدار یک `useState` یا `useReducer` آپدیت شده است)
- کامپوننت والد رندر شده است (این یک علت شایع برای رندرهای مجدد غیرضروری در کامپوننتهای فرزند است)
- کانتکست تغییر کرده است
گلوگاههای عملکردی رایج و نحوه رفع آنها
اکنون که میدانید چگونه دادههای عملکرد را جمعآوری و بخوانید، بیایید مشکلات رایجی را که پروفایلر به کشف آنها کمک میکند و الگوهای استاندارد ریاکت برای حل آنها را بررسی کنیم.
مشکل ۱: رندرهای مجدد غیرضروری
این شایعترین مشکل عملکردی در اپلیکیشنهای ریاکت است. زمانی رخ میدهد که یک کامپوننت دوباره رندر میشود حتی اگر خروجی آن دقیقاً یکسان باشد. این کار چرخههای CPU را هدر میدهد و میتواند باعث شود رابط کاربری شما کند به نظر برسد.
تشخیص:
- در پروفایلر، میبینید که یک کامپوننت به طور مکرر در بسیاری از commitها رندر میشود.
- بخش "Why did this render?" نشان میدهد که دلیل آن رندر شدن کامپوننت والد است، حتی اگر پراپهای خود آن کامپوننت تغییر نکرده باشند.
- بسیاری از کامپوننتها در flamegraph رنگی هستند، حتی اگر فقط بخش کوچکی از stateای که به آن وابسته هستند واقعاً تغییر کرده باشد.
راه حل ۱: `React.memo()`
`React.memo` یک کامپوننت مرتبه بالاتر (HOC) است که کامپوننت شما را ممو میکند (memoizes). این تابع یک مقایسه سطحی بین پراپهای قبلی و جدید کامپوننت انجام میدهد. اگر پراپها یکسان باشند، ریاکت از رندر مجدد کامپوننت صرفنظر کرده و از آخرین نتیجه رندر شده استفاده میکند.
قبل از `React.memo`:**
function UserAvatar({ userName, avatarUrl }) {
console.log(`Rendering UserAvatar for ${userName}`)
return
;
}
// در کامپوننت والد:
// اگر والد به هر دلیلی دوباره رندر شود (مثلاً state خود آن تغییر کند)،
// UserAvatar دوباره رندر خواهد شد، حتی اگر userName و avatarUrl یکسان باشند.
بعد از `React.memo`:**
import React from 'react';
const UserAvatar = React.memo(function UserAvatar({ userName, avatarUrl }) {
console.log(`Rendering UserAvatar for ${userName}`)
return
;
});
// اکنون، UserAvatar فقط در صورتی دوباره رندر میشود که پراپهای userName یا avatarUrl واقعاً تغییر کنند.
راه حل ۲: `useCallback()`
`React.memo` میتواند توسط پراپهایی که مقادیر غیر اولیه (non-primitive) هستند، مانند اشیاء یا توابع، شکست بخورد. در جاوااسکریپت، `() => {} !== () => {}`. یک تابع جدید در هر رندر ایجاد میشود، بنابراین اگر یک تابع را به عنوان پراپ به یک کامپوننت ممو شده ارسال کنید، آن کامپوننت همچنان دوباره رندر میشود.
هوک `useCallback` این مشکل را با برگرداندن یک نسخه ممو شده از تابع callback حل میکند که فقط در صورتی تغییر میکند که یکی از وابستگیهای آن تغییر کرده باشد.
قبل از `useCallback`:**
function ParentComponent() {
const [count, setCount] = useState(0);
// این تابع در هر رندر ParentComponent دوباره ایجاد میشود
const handleItemClick = (id) => {
console.log('Clicked item', id);
};
return (
{/* MemoizedListItem هر بار که count تغییر میکند دوباره رندر میشود، زیرا handleItemClick یک تابع جدید است */}
);
}
بعد از `useCallback`:**
import { useState, useCallback } from 'react';
function ParentComponent() {
const [count, setCount] = useState(0);
// این تابع اکنون ممو شده است و تا زمانی که وابستگیهایش (آرایه خالی) تغییر نکنند، دوباره ایجاد نخواهد شد.
const handleItemClick = useCallback((id) => {
console.log('Clicked item', id);
}, []); // آرایه وابستگی خالی به این معنی است که فقط یک بار ایجاد میشود
return (
{/* اکنون، MemoizedListItem هنگام تغییر count دوباره رندر نخواهد شد */}
);
}
راه حل ۳: `useMemo()`
مشابه `useCallback`، `useMemo` برای ممو کردن مقادیر استفاده میشود. این هوک برای محاسبات سنگین یا برای ایجاد اشیاء/آرایههای پیچیدهای که نمیخواهید در هر رندر دوباره تولید شوند، عالی است.
قبل از `useMemo`:**
function ProductList({ products, filterTerm }) {
// این عملیات فیلتر کردن سنگین در هر رندر ProductList اجرا میشود،
// حتی اگر فقط یک پراپ نامرتبط تغییر کرده باشد.
const visibleProducts = products.filter(p => p.name.includes(filterTerm));
return (
{visibleProducts.map(p => - {p.name}
)}
);
}
بعد از `useMemo`:**
import { useMemo } from 'react';
function ProductList({ products, filterTerm }) {
// این محاسبه اکنون فقط زمانی اجرا میشود که `products` یا `filterTerm` تغییر کنند.
const visibleProducts = useMemo(() => {
return products.filter(p => p.name.includes(filterTerm));
}, [products, filterTerm]);
return (
{visibleProducts.map(p => - {p.name}
)}
);
}
مشکل ۲: درختهای کامپوننت بزرگ و سنگین
گاهی اوقات مشکل رندرهای مجدد غیرضروری نیست، بلکه این است که یک رندر واحد واقعاً کند است زیرا درخت کامپوننت بسیار بزرگ است یا محاسبات سنگینی انجام میدهد.
تشخیص:
- در Flamegraph، یک کامپوننت واحد با یک میله بسیار عریض، زرد یا قرمز میبینید که نشاندهنده `baseDuration` و `actualDuration` بالا است.
- رابط کاربری هنگام ظاهر شدن یا آپدیت شدن این کامپوننت، فریز میشود یا پرشدار عمل میکند.
راه حل: Windowing / Virtualization
برای لیستهای طولانی یا شبکههای داده بزرگ، مؤثرترین راهحل این است که فقط آیتمهایی را رندر کنید که در حال حاضر برای کاربر در viewport قابل مشاهده هستند. این تکنیک "windowing" یا "virtualization" نامیده میشود. به جای رندر کردن ۱۰,۰۰۰ آیتم لیست، شما فقط ۲۰ موردی را که در صفحه جا میشوند رندر میکنید. این کار به شدت تعداد نودهای DOM و زمان صرف شده برای رندر را کاهش میدهد.
پیادهسازی این روش از ابتدا میتواند پیچیده باشد، اما کتابخانههای عالی وجود دارند که این کار را آسان میکنند:
- `react-window` و `react-virtualized` کتابخانههای محبوب و قدرتمندی برای ایجاد لیستها و شبکههای مجازیسازی شده هستند.
- اخیراً، کتابخانههایی مانند `TanStack Virtual` رویکردهای headless و مبتنی بر هوک ارائه میدهند که بسیار انعطافپذیر هستند.
مشکل ۳: دامهای Context API
Context API ریاکت ابزاری قدرتمند برای جلوگیری از prop drilling است، اما یک مشکل عملکردی قابل توجه دارد: هر کامپوننتی که از یک context استفاده میکند، هر زمان که هر مقداری در آن context تغییر کند، دوباره رندر میشود، حتی اگر آن کامپوننت از آن قطعه داده خاص استفاده نکند.
تشخیص:
- شما یک مقدار واحد را در context سراسری خود آپدیت میکنید (مثلاً تغییر تم).
- پروفایلر نشان میدهد که تعداد زیادی از کامپوننتها در سراسر اپلیکیشن شما دوباره رندر میشوند، حتی کامپوننتهایی که کاملاً به تم بیربط هستند.
- پنل "Why did this render?" برای این کامپوننتها "Context changed" را نشان میدهد.
راه حل: کانتکستهای خود را تقسیم کنید
بهترین راه برای حل این مشکل این است که از ایجاد یک `AppContext` غولپیکر و یکپارچه خودداری کنید. به جای آن، state سراسری خود را به چندین کانتکست کوچکتر و جزئیتر تقسیم کنید.
قبل (روش نامناسب):**
// AppContext.js
const AppContext = createContext({
currentUser: null,
theme: 'light',
language: 'en',
setTheme: () => {},
// ... و ۲۰ مقدار دیگر
});
// MyComponent.js
// این کامپوننت فقط به currentUser نیاز دارد، اما وقتی تم تغییر کند دوباره رندر میشود!
const { currentUser } = useContext(AppContext);
بعد (روش مناسب):**
// UserContext.js
const UserContext = createContext(null);
// ThemeContext.js
const ThemeContext = createContext({ theme: 'light', setTheme: () => {} });
// MyComponent.js
// این کامپوننت اکنون فقط زمانی دوباره رندر میشود که currentUser تغییر کند.
const currentUser = useContext(UserContext);
تکنیکهای پیشرفته پروفایلینگ و بهترین شیوهها
ساخت برای پروفایلینگ در پروداکشن
به طور پیشفرض، کامپوننت `
نحوه فعال کردن این بستگی به ابزار ساخت شما دارد. به عنوان مثال، با Webpack، ممکن است از یک alias در پیکربندی خود استفاده کنید:
// webpack.config.js
module.exports = {
// ... سایر تنظیمات
resolve: {
alias: {
'react-dom$': 'react-dom/profiling',
},
},
};
این به شما امکان میدهد تا از پروفایلر React DevTools در سایت مستقر شده و بهینهسازی شده برای پروداکشن خود برای دیباگ کردن مشکلات عملکردی در دنیای واقعی استفاده کنید.
یک رویکرد پیشگیرانه به عملکرد
منتظر نمانید تا کاربران از کندی شکایت کنند. اندازهگیری عملکرد را در گردش کار توسعه خود ادغام کنید:
- زود پروفایل کنید، زیاد پروفایل کنید: به طور منظم ویژگیهای جدید را در حین ساخت آنها پروفایل کنید. رفع یک گلوگاه زمانی که کد هنوز در ذهن شما تازه است بسیار آسانتر است.
- بودجههای عملکردی تعیین کنید: از API برنامهنویسی `
` برای تعیین بودجه برای تعاملات حیاتی استفاده کنید. به عنوان مثال، میتوانید اطمینان حاصل کنید که mount شدن داشبورد اصلی شما هرگز بیش از 200 میلیثانیه طول نکشد. - تستهای عملکرد را خودکار کنید: میتوانید از API برنامهنویسی در کنار فریمورکهای تست مانند Jest یا Playwright برای ایجاد تستهای خودکاری استفاده کنید که اگر یک رندر بیش از حد طول بکشد، با شکست مواجه شوند و از ادغام رگرسیونهای عملکردی جلوگیری کنند.
نتیجهگیری
بهینهسازی عملکرد یک فکر ثانویه نیست؛ بلکه یک جنبه اصلی از ساخت اپلیکیشنهای وب با کیفیت بالا و حرفهای است. API پروفایلر ریاکت، هم در فرم DevTools و هم در فرم برنامهنویسی، فرآیند رندرینگ را رمزگشایی میکند و دادههای ملموسی را که برای تصمیمگیری آگاهانه لازم است، فراهم میکند.
با تسلط بر این ابزار، میتوانید از حدس زدن در مورد عملکرد به سمت شناسایی سیستماتیک گلوگاهها، اعمال بهینهسازیهای هدفمند مانند `React.memo`، `useCallback` و مجازیسازی، و در نهایت، ساخت تجربیات کاربری سریع، روان و لذتبخشی که اپلیکیشن شما را متمایز میکند، حرکت کنید. همین امروز پروفایلینگ را شروع کنید و سطح بعدی عملکرد را در پروژههای ریاکت خود باز کنید.