با استفاده از این تکنیک مفید، می توانید عملکردهایی را که مرتباً با آنها تماس می گیرید سرعت بخشیده و عملکرد زیادی را افزایش دهید.
یادداشت کردن یک تکنیک بهینه سازی است، شبیه به حافظه پنهان. با ذخیره نتایج قبلی یک فراخوانی تابع و استفاده از آن نتایج در دفعه بعد که تابع اجرا میشود، کار میکند. این به ویژه در برنامه های محاسباتی سنگین که فراخوانی های تابع را با پارامترهای یکسان تکرار می کنند مفید است.
شما می توانید از یادداشت در جاوا اسکریپت ساده و همچنین در React به چند روش مختلف استفاده کنید.
حفظ کردن در جاوا اسکریپت
برای حفظ کردن یک تابع در جاوا اسکریپت، باید نتایج آن تابع را در حافظه پنهان ذخیره کنید. کش می تواند یک شی با آرگومان ها به عنوان کلید و نتایج به عنوان مقادیر باشد.
وقتی این تابع را فراخوانی میکنید، ابتدا بررسی میکند که آیا نتیجه قبل از اجرا در حافظه پنهان وجود دارد یا خیر. اگر اینطور باشد، نتایج ذخیره شده را برمی گرداند. در غیر این صورت اجرا می شود.
این تابع را در نظر بگیرید:
function square(num) {
return num * num
}
تابع یک آرگومان می گیرد و مربع آن را برمی گرداند.
برای اجرای تابع، آن را با شماره ای مانند زیر فراخوانی کنید:
square(5) // 25
با 5 به عنوان آرگومان، Square() بسیار سریع اجرا می شود. با این حال، اگر بخواهید مربع 70000 را محاسبه کنید، تاخیر محسوسی وجود دارد. نه خیلی ولی با این وجود با تاخیر. حال، اگر چندین بار این تابع را فراخوانی کنید و از 70000 عبور کنید، در هر تماس با تاخیر مواجه خواهید شد.
شما می توانید این تاخیر را با استفاده از یادداشت برداری از بین ببرید.
const memoizedSquare = () => {
let cache = {};
return (num) => {
if (num in cache) {
console.log('Reusing cached value');
return cache[num];
} else {
console.log('Calculating result');
let result = num * num;
// cache the new result value for next time
cache[num] = result;
return result;
}
}
}
در این مثال، تابع بررسی می کند که آیا نتیجه را قبلاً محاسبه کرده است یا خیر، با بررسی اینکه آیا در شیء کش وجود دارد یا خیر. اگر داشته باشد مقدار محاسبه شده قبلی را برمی گرداند.
هنگامی که تابع یک عدد جدید دریافت می کند، مقدار جدیدی را محاسبه می کند و نتایج را قبل از بازگشت در حافظه پنهان ذخیره می کند.
باز هم این مثال بسیار ساده است، اما توضیح می دهد که چگونه یادداشت برای بهبود عملکرد یک برنامه کار می کند.
شما فقط باید توابع خالص را به خاطر بسپارید. این توابع زمانی که همان آرگومانها را وارد میکنید، همان نتیجه را برمیگردانند. اگر از حافظهگذاری روی توابع ناخالص استفاده کنید، عملکرد را بهبود نمیبخشید، بلکه سربار خود را افزایش میدهید. به این دلیل که هر بار که یک تابع را به خاطر میسپارید، سرعت را به حافظه انتخاب میکنید.
یادداشت در React
اگر به دنبال بهینه سازی کامپوننت های React هستید، React ذخیره سازی را از طریق قلاب useMemo() React.memo و useCallBack() فراهم می کند.
استفاده از useMemo()
useMemo() یک قلاب React است که یک تابع و یک آرایه وابستگی را می پذیرد.
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
مقدار بازگردانده شده از آن تابع را به خاطر بسپارد. مقادیر موجود در آرایه وابستگی زمان اجرای تابع را تعیین می کنند. تنها زمانی که آنها تغییر کنند، تابع دوباره اجرا می شود.
به عنوان مثال، مؤلفه App زیر دارای یک مقدار ذخیره شده به نام نتیجه است.
import { useMemo } from "react"
function App(value) {
const square = (value) => {
return value * value
}
const result = useMemo(
() => square(value),
[ value ]
);
return (
<div>{result(5)}</div>
)
}
مؤلفه App () Square را در هر رندر فراخوانی می کند. اگر مؤلفه App بارها به دلیل تغییر در React props یا بهروزرسانی وضعیت رندر شود، عملکرد کاهش مییابد، به خصوص اگر تابع Square() گران باشد.
با این حال، از آنجایی که useMemo() مقادیر بازگشتی را ذخیره می کند، تابع مربع در هر رندر مجدد اجرا نمی شود مگر اینکه آرگومان های آرایه وابستگی تغییر کنند.
استفاده از React.memo()
React.memo() یک جزء مرتبه بالاتر است که یک جزء React و یک تابع را به عنوان آرگومان می پذیرد. تابع تعیین می کند که چه زمانی جزء باید به روز شود.
این تابع اختیاری است و اگر ارائه نشده باشد، React.memo یک کپی سطحی از اجزای فعلی کامپوننت را با اجزای قبلی آن مقایسه می کند. اگر پروپوزال ها متفاوت باشند، به روز رسانی را راه اندازی می کند. اگر props یکسان باشد، از رندر مجدد صرفنظر میکند و از مقادیر ذخیرهشده مجددا استفاده میکند.
تابع اختیاری props قبلی و props بعدی را به عنوان آرگومان می پذیرد. سپس میتوانید صریحاً این ابزارها را مقایسه کنید تا تصمیم بگیرید که آیا مؤلفه را بهروزرسانی کنید یا خیر.
React.memo(Component, [areEqual(prevProps, nextProps)])
بیایید ابتدا به یک مثال بدون آرگومان تابع اختیاری نگاه کنیم. در زیر کامپوننتی به نام نظرات وجود دارد که نام و مشخصات ایمیل را می پذیرد.
function Comments ({name, comment, likes}) {
return (
<div>
<p>{name}</p>
<p>{comment}</p>
<p>{likes}</p>
</div>
)
}
کامپوننت نظرات به خاطر سپرده شده دارای React.memo خواهد بود که به صورت زیر در اطراف آن پیچیده شده است:
const MemoizedComment = React.memo(Comment)
می توانید تماس بگیرید و سپس آن را مانند هر مؤلفه دیگر React فراخوانی کنید.
<MemoizedComment name="Mary" comment="Memoization is great" likes=1/>
اگر می خواهید خودتان مقایسه props را انجام دهید، تابع زیر را به عنوان آرگومان دوم به React.memo منتقل کنید.
import React from "react"
function checkCommentProps(prevProps, nextProps) {
return prevProps.name === nextProps.name
&& prevProps.comment === nextProps.comment
&& prevProps.likes === nextProps.likes
}
const MemoizedComment = React.memo(Comments, checkCommentProps)
اگر checkProfileProps درست را برگرداند، مؤلفه بهروزرسانی نمیشود. در غیر این صورت، دوباره رندر می شود.
عملکرد سفارشی زمانی مفید است که می خواهید رندر مجدد را سفارشی کنید. برای مثال، میتوانید از آن برای بهروزرسانی مؤلفه نظرات تنها زمانی استفاده کنید که تعداد لایکها تغییر کند.
برخلاف قلاب useMemo() که فقط مقدار بازگشتی یک تابع را به خاطر میسپارد، React.memo کل تابع را به خاطر میسپارد.
از React.memo فقط برای اجزای خالص استفاده کنید. همچنین، برای کاهش هزینههای مقایسه، فقط اجزایی را به خاطر بسپارید که پایههای آنها اغلب تغییر میکند.
استفاده از useCallBack()
می توانید از قلاب useCallBack() برای به خاطر سپردن اجزای تابع استفاده کنید.
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);
تابع تنها زمانی به روز می شود که مقادیر موجود در آرایه وابستگی تغییر کند. هوک مانند callback ()useMemo عمل میکند، اما به جای حفظ کردن مقادیر، جزء تابع را بین رندرها به خاطر میسپارد.
مثال زیر را از یک تابع ذخیره شده در نظر بگیرید که یک API را فراخوانی می کند.
import { useCallback, useEffect } from "react";
const Component = () => {
const getData = useCallback(() => {
console.log('call an API');
}, []);
useEffect(() => {
getData();
}, [getData]);
};
تابع getData() که در useEffect فراخوانی میشود، تنها زمانی دوباره فراخوانی میشود که مقدار getData تغییر کند.
آیا باید یادداشت کنید؟
در این آموزش یاد گرفتید که حافظه نویسی چیست، مزایای آن و نحوه پیاده سازی آن در جاوا اسکریپت و React چیست. با این حال، باید بدانید که React در حال حاضر سریع است. در بیشتر موارد، به خاطر سپردن اجزا یا مقادیر، هزینه های مقایسه را افزایش می دهد و عملکرد را بهبود نمی بخشد. به همین دلیل، فقط اجزای گران قیمت را به خاطر بسپارید.
React 18 همچنین قلاب های جدیدی مانند useId، useTransition و useInsertionEffect را معرفی کرد. می توانید از اینها برای بهبود عملکرد و تجربه کاربری برنامه های React استفاده کنید.