مقدمه
HTML5 ابزارهای عالی برای بهبود ظاهر بصری برنامه های کاربردی وب به ما می دهد. این امر به ویژه در حوزه انیمیشن ها صادق است. با این حال، با این قدرت جدید چالش های جدیدی نیز به وجود می آید. در واقع این چالشها واقعاً جدید نیستند و گاهی اوقات ممکن است منطقی باشد که از همسایه میز خود، برنامهنویس Flash، بپرسید که چگونه در گذشته بر موارد مشابه غلبه کرده است.
به هر حال، وقتی شما در انیمیشن کار می کنید، بسیار مهم است که کاربران این انیمیشن ها را روان و روان درک کنند. چیزی که ما باید بدانیم این است که نرمی در انیمیشن ها واقعاً با افزایش فریم در ثانیه فراتر از هر آستانه شناختی ایجاد نمی شود. مغز ما متأسفانه باهوش تر از این حرف هاست. چیزی که یاد خواهید گرفت این است که 30 فریم واقعی انیمیشن (فریم در ثانیه) بسیار بهتر از 60 فریم در ثانیه است که فقط چند فریم در وسط افت می کند. مردم از تنبلی متنفرند.
این مقاله سعی میکند ابزارها و تکنیکهایی را در اختیار شما بگذارد تا روی بهبود تجربه اپلیکیشن خود کار کنید.
استراتژی
به هیچ وجه نمی خواهیم شما را از ساختن برنامه های بصری عالی و خیره کننده با HTML5 منصرف کنیم.
سپس وقتی متوجه شدید که عملکرد می تواند کمی بهتر باشد، به اینجا برگردید و در مورد چگونگی بهبود عناصر برنامه خود مطالعه کنید. البته میتواند در وهله اول به انجام درست برخی کارها کمک کند، اما هرگز اجازه ندهید که این کارها مانع از کارآمدی شما شود.
Visual fidelity++ با HTML5
شتاب سخت افزاری
شتاب سخت افزاری یک نقطه عطف مهم برای عملکرد کلی رندر در مرورگر است. طرح کلی این است که وظایفی را که در غیر این صورت توسط CPU اصلی محاسبه میشوند، به واحد پردازش گرافیکی (GPU) در آداپتور گرافیک رایانهتان بارگذاری کنید. این می تواند دستاوردهای عملکرد گسترده ای داشته باشد و همچنین می تواند مصرف منابع را در دستگاه های تلفن همراه کاهش دهد.
این جنبه های سند شما می تواند توسط GPU تسریع شود
- ترکیب چیدمان عمومی
- انتقال CSS3
- CSS3 3D تبدیل می شود
- طراحی بوم
- طراحی سه بعدی WebGL
در حالی که تسریع بوم و WebGL ویژگیهای هدف خاصی هستند که ممکن است برای برنامه خاص شما اعمال نشوند، سه جنبه اول تقریباً به هر برنامه کمک میکند تا سریعتر شود.
چه چیزی را می توان تسریع کرد؟
شتاب GPU با بارگذاری وظایف کاملاً تعریف شده و خاص به سخت افزار با هدف خاص کار می کند. طرح کلی این است که سند شما به چندین "لایه" تقسیم می شود که نسبت به جنبه های صفحه شما که تسریع می شود تغییر نمی کند. این لایه ها با استفاده از خط لوله رندر سنتی رندر می شوند. سپس از GPU برای ترکیب لایهها در یک صفحه استفاده میشود و «افکتهایی» را اعمال میکند که میتوانند در پرواز تسریع شوند. یک نتیجه احتمالی این است که یک شی که روی صفحه متحرک است نیازی به یک "relayout" صفحه نداشته باشد در حالی که انیمیشن اتفاق می افتد.
چیزی که باید از آن حذف کنید این است که باید کاری کنید که موتور رندر تشخیص دهد که چه زمانی می تواند جادوی شتاب GPU خود را اعمال کند. به مثال زیر توجه کنید:
در حالی که این کار می کند، مرورگر واقعاً نمی داند که شما چیزی را اجرا می کنید که قرار است توسط یک انسان به عنوان انیمیشن صاف درک شود. در نظر بگیرید که چه اتفاقی میافتد وقتی به همان ظاهر بصری با استفاده از انتقالهای CSS3 دست پیدا میکنید:
نحوه پیاده سازی این انیمیشن توسط مرورگر کاملاً از دید توسعه دهنده پنهان است. این به نوبه خود به این معنی است که مرورگر می تواند ترفندهایی مانند شتاب GPU را برای رسیدن به هدف تعریف شده اعمال کند.
دو پرچم خط فرمان مفید برای کروم برای کمک به رفع اشکال شتاب GPU وجود دارد:
-
--show-composited-layer-borders
یک حاشیه قرمز رنگ اطراف عناصری را نشان می دهد که در سطح GPU دستکاری می شوند. برای تأیید اینکه دستکاری های شما در لایه GPU رخ می دهد خوب است. -
--show-paint-rects
تمام تغییرات غیر GPU رنگ شده اند و این یک حاشیه روشن در اطراف همه مناطقی که دوباره رنگ شده است ایجاد می کند. شما می توانید مرورگر را در حال بهینه سازی مناطق رنگ آمیزی مشاهده کنید.
سافاری دارای پرچمهای زمان اجرا مشابهی است که در اینجا توضیح داده شده است .
انتقال CSS3
CSS Transitions انیمیشن سبک را برای همه بی اهمیت می کند، اما آنها همچنین یک ویژگی عملکرد هوشمند هستند. از آنجایی که یک انتقال CSS توسط مرورگر مدیریت می شود، وفاداری انیمیشن آن را می توان تا حد زیادی بهبود بخشید و در بسیاری از موارد سخت افزار را تسریع کرد. در حال حاضر WebKit (Chrome، Safari، iOS) تغییرات سخت افزاری CSS را تسریع کرده است، اما به سرعت به مرورگرها و پلتفرم های دیگر می آید.
میتوانید از رویدادهای transitionEnd
برای اسکریپت کردن آن در ترکیبهای قدرتمند استفاده کنید، اگرچه در حال حاضر، ثبت همه رویدادهای پایان انتقال پشتیبانیشده به معنای تماشای webkitTransitionEnd transitionend oTransitionEnd
است.
اکنون بسیاری از کتابخانهها APIهای انیمیشنی را معرفی کردهاند که در صورت وجود از انتقالها استفاده میکنند و در غیر این صورت به انیمیشنهای استاندارد DOM بازمیگردند. scripty2 , YUI Transition , jQuery anime بهبود یافته .
ترجمه CSS3
من مطمئن هستم که قبلاً متوجه شده اید که موقعیت x/y یک عنصر را در سراسر صفحه متحرک کرده اید. احتمالاً ویژگی های سمت چپ و بالای سبک درون خطی را دستکاری کرده اید. با تبدیل های دو بعدی، می توانیم از قابلیت translate()
برای تکرار این رفتار استفاده کنیم.
ما می توانیم این را با انیمیشن DOM ترکیب کنیم تا از بهترین چیز ممکن استفاده کنیم
<div style="position:relative; height:120px;" class="hwaccel">
<div style="padding:5px; width:100px; height:100px; background:papayaWhip;
position:absolute;" id="box">
</div>
</div>
<script>
document.querySelector('#box').addEventListener('click', moveIt, false);
function moveIt(evt) {
var elem = evt.target;
if (Modernizr.csstransforms && Modernizr.csstransitions) {
// vendor prefixes omitted here for brevity
elem.style.transition = 'all 3s ease-out';
elem.style.transform = 'translateX(600px)';
} else {
// if an older browser, fall back to jQuery animate
jQuery(elem).animate({ 'left': '600px'}, 3000);
}
}
</script>
ما از Modernizr برای تست ویژگی برای CSS 2D Transforms و CSS Transitions استفاده می کنیم، اگر چنین است، ما از translate برای تغییر موقعیت استفاده می کنیم. اگر این انیمیشن با استفاده از یک انتقال متحرک باشد، احتمال زیادی وجود دارد که مرورگر بتواند سخت افزار آن را تسریع کند. برای اینکه مرورگر را در جهت درست فشار دهیم، از "گلوله جادویی CSS" از بالا استفاده می کنیم.
اگر مرورگر ما توانایی کمتری دارد، برای جابجایی عنصر خود به جی کوئری بازگشته ایم. شما می توانید پلاگین jQuery Transform polyfill توسط Louis-Remi Babe را انتخاب کنید تا کل این کار به صورت خودکار انجام شود.
window.requestAnimationFrame
requestAnimationFrame
توسط Mozilla معرفی شد و توسط WebKit تکرار شد و هدف آن ارائه یک API بومی برای اجرای انیمیشنها، خواه مبتنی بر DOM/CSS یا <canvas>
یا WebGL باشد. مرورگر میتواند انیمیشنهای همزمان را با هم در یک چرخه مجدد و رنگآمیزی بهینهسازی کند، که منجر به انیمیشنهایی با وفاداری بالاتر میشود. به عنوان مثال، انیمیشن های مبتنی بر JS که با انتقال CSS یا SVG SMIL همگام شده اند. بهعلاوه، اگر حلقه انیمیشن را در برگهای اجرا میکنید که قابل مشاهده نیست، مرورگر آن را در حال اجرا نگه نمیدارد ، که به معنای استفاده کمتر از CPU، GPU و حافظه است که منجر به عمر باتری بسیار بیشتر میشود.
برای جزئیات بیشتر در مورد چگونگی و چرایی استفاده از requestAnimationFrame
، مقاله Paul Irish requestAnimationFrame برای انیمیشن هوشمند را مشاهده کنید.
پروفایل کردن
هنگامی که متوجه شدید که سرعت برنامه شما می تواند بهبود یابد، زمان آن فرا رسیده است که به نمایه سازی بپردازید تا دریابید که در کجا بهینه سازی می تواند بیشترین سود را داشته باشد. بهینه سازی ها اغلب تأثیر منفی بر قابلیت نگهداری کد منبع شما خواهند داشت و بنابراین فقط در صورت لزوم باید اعمال شوند. نمایه سازی به شما می گوید که کدام بخش از کد شما با بهبود عملکرد آنها بیشترین مزیت را دارد.
پروفایل جاوا اسکریپت
نمایه سازهای جاوا اسکریپت با اندازه گیری زمان لازم برای اجرای هر تابع از ابتدا تا انتهای آن، یک نمای کلی از عملکرد برنامه شما در سطح عملکرد جاوا اسکریپت به شما ارائه می دهند.
زمان اجرای ناخالص یک تابع زمان کلی است که برای اجرای آن از بالا به پایین طول می کشد. زمان اجرای خالص، زمان اجرای ناخالص منهای زمانی است که برای اجرای توابع فراخوانی شده از تابع صرف شده است.
برخی از توابع بیشتر از بقیه فراخوانی می شوند. پروفیلها معمولاً زمان لازم برای اجرای همه فراخوانها و همچنین میانگین و حداقل و حداکثر زمان اجرا را به شما میدهند.
برای جزئیات بیشتر، اسناد Chrome Dev Tools در مورد نمایه سازی را بررسی کنید.
DOM
عملکرد جاوا اسکریپت تأثیر زیادی بر میزان روان و پاسخگو بودن برنامه شما دارد. درک این نکته مهم است که در حالی که پروفایلکنندگان جاوا اسکریپت زمان اجرای جاوا اسکریپت شما را اندازهگیری میکنند، آنها بهطور غیرمستقیم زمان صرف شده برای انجام عملیات DOM را نیز اندازهگیری میکنند. این عملیات DOM اغلب در قلب مشکلات عملکرد شما قرار دارند.
function drawArray(array) {
for(var i = 0; i < array.length; i++) {
document.getElementById('test').innerHTML += array[i]; // No good :(
}
}
به عنوان مثال در کد بالا تقریباً هیچ زمانی برای اجرای جاوا اسکریپت واقعی صرف نمی شود. هنوز هم بسیار محتمل است که تابع drawArray در نمایههای شما نمایش داده شود، زیرا در حال تعامل با DOM به شیوهای بسیار بیهوده است.
نکات و ترفندها
توابع ناشناس
نمایه کردن توابع ناشناس آسان نیست زیرا آنها ذاتاً نامی ندارند که تحت آن بتوانند در نمایه ساز نشان داده شوند. دو راه برای حل این مشکل وجود دارد:
$('.stuff').each(function() { ... });
بازنویسی به:
$('.stuff').each(function workOnStuff() { ... });
معمولاً مشخص نیست که جاوا اسکریپت از عبارات نامگذاری تابع پشتیبانی می کند. انجام این کار باعث می شود که آنها کاملاً در پروفایلر ظاهر شوند. این راه حل یک مشکل دارد: عبارت نامگذاری شده در واقع نام تابع را در محدوده واژگانی فعلی قرار می دهد. این ممکن است نمادهای دیگر را به هم بزند، بنابراین مراقب باشید.
پروفایل توابع طولانی
تصور کنید یک عملکرد طولانی دارید و مشکوک هستید که بخش کوچکی از آن ممکن است دلیل مشکلات عملکرد شما باشد. دو راه برای فهمیدن مشکل در کدام قسمت وجود دارد:
- روش صحیح: کد خود را به گونه ای تغییر دهید که شامل هیچ توابع طولانی نباشد.
- روش شیطانی انجام کارها: عباراتی را در قالب توابع خود فراخوانی نامگذاری شده به کد خود اضافه کنید. اگر کمی مراقب باشید، این امر معنایی را تغییر نمیدهد و باعث میشود که بخشهایی از تابع شما بهعنوان توابع مجزا در نمایهگر نشان داده شوند:
js function myLongFunction() { ... (function doAPartOfTheWork() { ... })(); ... }
فراموش نکنید که این توابع اضافی را پس از انجام پروفایل حذف کنید. و یا حتی از آنها به عنوان نقطه شروع برای بازسازی کد خود استفاده کنید.
پروفایل DOM
آخرین ابزارهای توسعه Chrome Web Inspector حاوی "نمای خط زمانی" جدید است که جدول زمانی اقدامات سطح پایین انجام شده توسط مرورگر را نشان می دهد. می توانید از این اطلاعات برای بهینه سازی عملیات DOM خود استفاده کنید. هدف شما باید کاهش تعداد "عملکردهایی" باشد که مرورگر باید هنگام اجرای کد شما انجام دهد.
نمای جدول زمانی می تواند حجم عظیمی از اطلاعات را ایجاد کند. بنابراین باید سعی کنید حداقل موارد آزمایشی را ایجاد کنید که بتوانید مستقل آن را اجرا کنید.

تصویر بالا خروجی نمای تایم لاین را برای یک اسکریپت بسیار ساده نشان می دهد. پنجره سمت چپ عملیات انجام شده توسط مرورگر را به ترتیب مزمن نشان می دهد، در حالی که جدول زمانی در سمت راست زمان واقعی مصرف شده توسط یک عملیات جداگانه را نشان می دهد.
اطلاعات بیشتر در مورد نمای خط زمانی یک ابزار جایگزین برای پروفایل در اینترنت اکسپلورر است DynaTrace Ajax Edition .
استراتژی های پروفایل
جنبه ها را جدا کنید
وقتی میخواهید پروفایل برنامه خود را ایجاد کنید، سعی کنید جنبههایی از عملکرد آن را که ممکن است باعث کندی تا حد ممکن شود، مشخص کنید. سپس سعی کنید یک نمایه اجرا کنید که فقط بخش هایی از کد شما را اجرا کند که مربوط به این جنبه های برنامه شما است. این کار تفسیر دادههای پروفایل را آسانتر میکند زیرا با مسیرهای کدی که به مشکل واقعی شما مرتبط نیستند مخلوط نمیشوند. مثالهای خوب برای جنبههای فردی برنامه شما ممکن است این باشد:
- زمان راه اندازی (پروفایلر را فعال کنید، برنامه را دوباره بارگیری کنید، صبر کنید تا مقداردهی اولیه کامل شود، پروفایلر را متوقف کنید.
- روی یک دکمه و انیمیشن بعدی کلیک کنید (شروع نمایه ساز، روی دکمه کلیک کنید، صبر کنید تا انیمیشن کامل شود، نمایه ساز را متوقف کنید).
پروفایل رابط کاربری گرافیکی
اجرای تنها قسمت سمت راست برنامه شما می تواند در یک برنامه رابط کاربری گرافیکی سخت تر از زمانی باشد که مثلاً ردیاب پرتو موتور سه بعدی خود را بهینه کنید. برای مثال، وقتی میخواهید مواردی را که هنگام کلیک کردن روی دکمهای اتفاق میافتد، نمایه کنید، ممکن است رویدادهای غیر مرتبط با ماوس را در طول مسیر ایجاد کنید که نتایج شما را قطعیتر کند. سعی کن ازش دوری کنی :)
رابط برنامه ای
همچنین یک رابط برنامه نویسی برای فعال کردن دیباگر وجود دارد. این امکان کنترل دقیق زمان شروع و پایان پروفایل را فراهم می کند.
شروع یک پروفایل با:
console.profile()
توقف نمایه سازی با:
console.profileEnd()
تکرارپذیری
وقتی پروفایل انجام می دهید مطمئن شوید که واقعاً می توانید نتایج خود را بازتولید کنید. تنها در این صورت میتوانید بگویید که آیا بهینهسازیهای شما واقعاً چیزها را بهبود بخشیده است یا خیر. همچنین پروفایل سطح عملکرد در زمینه کل رایانه شما انجام می شود. علم دقیقی نیست. اجرای نمایههای فردی ممکن است تحت تأثیر بسیاری از چیزهای دیگر که در رایانه شما اتفاق میافتد باشد:
- یک تایمر نامرتبط در برنامه خودتان که در حالی که چیز دیگری را اندازه میگیرید، فعال میشود.
- زباله جمع کن کارش را می کند.
- برگه دیگری در مرورگر شما که کار سختی را در همان رشته عملیاتی انجام می دهد.
- برنامه دیگری در رایانه شما که از CPU استفاده می کند، بنابراین برنامه شما را کندتر می کند.
- تغییرات ناگهانی در میدان گرانشی زمین.
همچنین اجرای یک مسیر کد چندین بار در یک جلسه نمایه سازی منطقی است. به این ترتیب تأثیر عوامل فوق را کاهش می دهید و قطعات کند ممکن است حتی واضح تر ظاهر شوند.
اندازه گیری، بهبود، اندازه گیری
وقتی نقطه کندی را در برنامه خود شناسایی کردید، سعی کنید به راه هایی برای بهبود رفتار اجرا فکر کنید. بعد از اینکه کد خود را تغییر دادید، دوباره نمایه کنید. اگر از نتیجه راضی هستید، ادامه دهید، اگر بهبودی نمی بینید، احتمالاً باید تغییر خود را به عقب برگردانید و آن را رها نکنید "زیرا نمی تواند صدمه بزند".
استراتژی های بهینه سازی
تعامل DOM را به حداقل برسانید
یک موضوع رایج برای بهبود سرعت برنامههای کلاینت وب، به حداقل رساندن تعامل DOM است. در حالی که سرعت موتورهای جاوا اسکریپت با یک مرتبه افزایش یافته است، دسترسی به DOM با همان سرعت سریعتر نشده است. این نیز به دلایل بسیار عملی است که هرگز اتفاق نخواهد افتاد (چیزهایی مانند چیدمان و ترسیم چیزها روی صفحه فقط زمان می برد).
گره های DOM کش
هر زمان که یک گره یا لیستی از گره ها را از DOM بازیابی می کنید، سعی کنید به این فکر کنید که آیا ممکن است بتوانید آنها را در محاسبات بعدی (یا حتی تکرار حلقه بعدی) دوباره استفاده کنید. تا زمانی که گرهها را در ناحیه مربوطه اضافه یا حذف نکنید، اغلب چنین است.
قبل از:
function getElements() {
return $('.my-class');
}
بعد از:
var cachedElements;
function getElements() {
if (cachedElements) {
return cachedElements;
}
cachedElements = $('.my-class');
return cachedElements;
}
مقادیر ویژگی کش
به همان روشی که می توانید گره های DOM را کش کنید، می توانید مقادیر ویژگی ها را نیز کش کنید. تصور کنید که در حال متحرک سازی یک ویژگی از سبک یک گره هستید. اگر میدانید که شما (مانند آن قسمت از کد) تنها کسی هستید که آن ویژگی را لمس میکنید، میتوانید آخرین مقدار را در هر تکرار ذخیره کنید تا مجبور نباشید آن را به طور مکرر بخوانید.
قبل از:
setInterval(function() {
var ele = $('#element');
var left = parseInt(ele.css('left'), 10);
ele.css('left', (left + 5) + 'px');
}, 1000 / 30);
بعد از: js var ele = $('#element'); var left = parseInt(ele.css('left'), 10); setInterval(function() { left += 5; ele.css('left', left + 'px'); }, 1000 / 30);
دستکاری DOM را از حلقه ها خارج کنید
حلقه ها اغلب نقاط داغ برای بهینه سازی هستند. سعی کنید راه هایی را برای جدا کردن اعداد واقعی از کار با DOM بیاندیشید. اغلب می توان یک محاسبه را انجام داد و پس از انجام آن، همه نتایج را یکجا اعمال کرد.
قبل از:
document.getElementById('target').innerHTML = '';
for(var i = 0; i < array.length; i++) {
var val = doSomething(array[i]);
document.getElementById('target').innerHTML += val;
}
بعد از:
var stringBuilder = [];
for(var i = 0; i < array.length; i++) {
var val = doSomething(array[i]);
stringBuilder.push(val);
}
document.getElementById('target').innerHTML = stringBuilder.join('');
دوباره ترسیم و دوباره جریان می یابد
همانطور که قبلا بحث شد دسترسی به DOM نسبتا کند است. زمانی که کد شما مقداری را می خواند که باید دوباره محاسبه شود، بسیار کند می شود زیرا کد شما اخیراً چیزی مربوط به DOM را تغییر داده است. بنابراین، باید از اختلاط دسترسی خواندن و نوشتن به DOM اجتناب شود. در حالت ایده آل کد شما باید همیشه در دو مرحله گروه بندی شود:
- فاز 1: مقادیر DOM لازم برای کد خود را بخوانید
- فاز 2: DOM را اصلاح کنید
سعی کنید الگوهایی مانند:
- فاز 1: خواندن مقادیر DOM
- فاز 2: DOM را اصلاح کنید
- فاز 3: بیشتر بخوانید
- فاز 4: DOM را در جای دیگری تغییر دهید.
قبل از:
function paintSlow() {
var left1 = $('#thing1').css('left');
$('#otherThing1').css('left', left);
var left2 = $('#thing2').css('left');
$('#otherThing2').css('left', left);
}
بعد از:
function paintFast() {
var left1 = $('#thing1').css('left');
var left2 = $('#thing2').css('left');
$('#otherThing1').css('left', left);
$('#otherThing2').css('left', left);
}
این توصیه باید برای اقداماتی که در یک زمینه اجرای جاوا اسکریپت اتفاق می افتد در نظر گرفته شود. (به عنوان مثال در یک کنترل کننده رویداد، در یک کنترل کننده بازه زمانی یا هنگام رسیدگی به یک پاسخ آژاکس.)
اجرای تابع paintSlow()
از بالا این تصویر را ایجاد می کند:

با جابجایی به اجرای سریعتر این تصویر به دست می آید:

این تصاویر نشان می دهد که مرتب کردن مجدد نحوه دسترسی کد شما به DOM می تواند عملکرد رندر را تا حد زیادی افزایش دهد. در این مورد، کد اصلی باید استایلها را دوباره محاسبه کند و صفحه را دو بار طرحبندی کند تا نتیجه یکسانی ایجاد شود. بهینه سازی مشابهی را می توان اساساً برای همه کدهای "دنیای واقعی" اعمال کرد و نتایج واقعاً چشمگیری را به همراه داشت.
بیشتر بخوانید: رندر: repaint، reflow/relayout، restyle توسط Stoyan Stefanov
ترسیم مجدد و حلقه رویداد
اجرای جاوا اسکریپت در مرورگر از مدل "حلقه رویداد" پیروی می کند. به طور پیش فرض مرورگر در حالت "بیکار" است. این حالت می تواند توسط رویدادهایی از تعاملات کاربر یا مواردی مانند تایمرهای جاوا اسکریپت یا تماس های Ajax قطع شود. هر زمان که یک قطعه از جاوا اسکریپت در چنین نقطه وقفه ای اجرا شود، مرورگر معمولاً منتظر می ماند تا آن را به پایان برساند تا زمانی که صفحه را دوباره رنگ کند (ممکن است استثناهایی برای جاوا اسکریپت های طولانی در حال اجرا یا در مواردی مانند جعبه های هشدار که به طور موثر اجرای جاوا اسکریپت را مختل می کنند وجود داشته باشد).
عواقب
- اگر اجرای چرخه های انیمیشن جاوا اسکریپت شما بیش از 1/30 ثانیه طول بکشد، نمی توانید انیمیشن های صاف ایجاد کنید زیرا مرورگر در طول اجرای JS دوباره رنگ آمیزی نمی کند. هنگامی که انتظار دارید رویدادهای کاربر را نیز مدیریت کنید، باید بسیار سریعتر باشید.
- گاهی اوقات تأخیر برخی از اقدامات جاوا اسکریپت تا کمی بعد مفید است. به عنوان مثال
setTimeout(function() { ... }, 0)
این به طور موثر به مرورگر میگوید که به محض اینکه حلقه رویداد دوباره بیحرکت میشود، پاسخ تماس را اجرا کند (در عمل برخی از مرورگرها حداقل 10 میلیثانیه صبر خواهند کرد). باید توجه داشته باشید که با این کار دو چرخه اجرای جاوا اسکریپت ایجاد می شود که در زمان بسیار نزدیک به هم هستند. هر دو ممکن است باعث رنگ آمیزی مجدد صفحه شوند که ممکن است زمان کلی صرف شده برای نقاشی را دو برابر کند. اینکه آیا این واقعاً باعث دو رنگ می شود یا خیر، بستگی به اکتشافی در مرورگر دارد.
نسخه معمولی:
function paintFast() {
var height1 = $('#thing1').css('height');
var height2 = $('#thing2').css('height');
$('#otherThing1').css('height', '20px');
$('#otherThing2').css('height', '20px');
}

اجازه دهید کمی تاخیر اضافه کنیم:
function paintALittleLater() {
var height1 = $('#thing1').css('height');
var height2 = $('#thing2').css('height');
$('#otherThing1').css('height', '20px');
setTimeout(function() {
$('#otherThing2').css('height', '20px');
}, 10)
}

نسخه تاخیری نشان میدهد که مرورگر دو بار نقاشی میکند، اگرچه دو تغییر در صفحه فقط 1/100 ثانیه است.
Lazy Initialization
کاربران برنامه های وب را می خواهند که سریع بارگذاری شوند و احساس پاسخگویی داشته باشند. با این حال، کاربران بسته به عملکردی که انجام میدهند، آستانههای متفاوتی برای آهسته بودن آنها دارند. به عنوان مثال، یک برنامه هرگز نباید محاسبات زیادی را روی یک رویداد ماوس انجام دهد زیرا ممکن است در حالی که کاربر به حرکت ماوس خود ادامه می دهد، تجربه کاربری بدی ایجاد کند. با این حال، کاربران عادت دارند پس از کلیک بر روی یک دکمه، کمی تاخیر را بپذیرند.
بنابراین ممکن است منطقی باشد که کد مقداردهی اولیه خود را منتقل کنید تا تا حد امکان دیر اجرا شود (مثلا زمانی که کاربر روی دکمه ای کلیک می کند که یک جزء خاص از برنامه شما را فعال می کند).
قبل: js var things = $('.ele > .other * div.className'); $('#button').click(function() { things.show() });
بعد از: js $('#button').click(function() { $('.ele > .other * div.className').show() });
هیئت رویداد
انتشار کنترلکنندههای رویداد در یک صفحه ممکن است زمان نسبتاً زیادی طول بکشد و همچنین میتواند زمانی که عناصر به صورت پویا جایگزین شوند خستهکننده باشد که پس از آن مستلزم اتصال مجدد کنترلکنندههای رویداد به عناصر جدید است.
راه حل در این مورد استفاده از تکنیکی به نام نمایندگی رویداد است. بهجای پیوستن کنترلکنندههای رویداد جداگانه به عناصر، ماهیت حبابدار بسیاری از رویدادهای مرورگر با پیوست کردن کنترلکننده رویداد به یک گره والد و بررسی گره هدف رویداد برای دیدن اینکه آیا رویداد مورد علاقه است، استفاده میشود.
در jQuery این را می توان به راحتی به صورت زیر بیان کرد:
$('#parentNode').delegate('.button', 'click', function() { ... });
چه زمانی از نمایندگی رویداد استفاده نکنید
گاهی اوقات برعکس میتواند صادق باشد: شما از نمایندگی رویداد استفاده میکنید و مشکل عملکردی دارید. اساساً تفویض رویداد زمان اولیه سازی با پیچیدگی ثابت را امکان پذیر می کند. با این حال، بهای بررسی اینکه آیا یک رویداد مورد علاقه است باید برای هر فراخوانی از آن رویداد پرداخت شود. این ممکن است گران باشد، مخصوصاً برای رویدادهایی که اغلب رخ می دهند مانند "Moeover" یا حتی "Mouve".
مشکلات و راه حل های معمولی
کارهایی که در $(document).ready
انجام می دهم زمان زیادی می برد
توصیه شخصی مالت: هرگز کاری را در $(document).ready
انجام ندهید. سعی کنید سند خود را به شکل نهایی تحویل دهید. خوب، شما مجاز به ثبت شنوندگان رویداد هستید، اما فقط با استفاده از id-selector و/یا استفاده از تفویض رویداد. برای رویدادهای گران قیمت مانند "Moemove"، ثبت نام را تا زمانی که مورد نیاز است به تاخیر بیندازید (رویداد ماوس روی عنصر مربوطه).
و اگر واقعاً نیاز به انجام کارهایی دارید، مانند درخواست Ajax برای دریافت اطلاعات واقعی، سپس یک انیمیشن زیبا نشان دهید. اگر یک GIF متحرک یا موارد مشابه باشد، ممکن است بخواهید انیمیشن را به عنوان URI داده اضافه کنید.
از آنجایی که یک فیلم فلش را به صفحه اضافه کردم، همه چیز واقعا کند است
افزودن فلش به یک صفحه همیشه رندر را کمی کند می کند زیرا طرح نهایی پنجره باید بین مرورگر و افزونه Flash "مذاکره" شود. هنگامی که نمی توانید به طور کامل از قرار دادن فلش در صفحات خود اجتناب کنید، مطمئن شوید که پارامتر Flash "wmode" را روی مقدار "window" (که پیش فرض است) تنظیم کرده اید. این قابلیت ترکیب عناصر HTML و Flash را غیرفعال میکند (شما نمیتوانید عنصر HTML را که بالای فیلم Flash قرار دارد ببینید و فیلم فلش شما شفاف نباشد). این ممکن است یک ناراحتی باشد اما عملکرد شما را به طور چشمگیری بهبود می بخشد. برای مثال روشی را که youtube.com با دقت از قرار دادن لایهها در بالای پخشکننده اصلی فیلم اجتناب میکند، بررسی کنید.
من چیزها را در localStorage ذخیره می کنم، اکنون برنامه من دچار لکنت می شود
نوشتن در localStorage یک عملیات همزمان است که شامل چرخاندن هارد دیسک شما می شود. شما هرگز نمی خواهید هنگام انجام انیمیشن ها عملیات همزمان "طولانی" را انجام دهید. دسترسی به LocalStorage را به نقطه ای از کد خود منتقل کنید که مطمئن هستید کاربر بیکار است و هیچ انیمیشنی در حال انجام نیست.
نمایه سازی نشان می دهد که انتخابگر جی کوئری واقعا کند است
ابتدا می خواهید مطمئن شوید که انتخابگر شما می تواند از طریق document.querySelectorAll اجرا شود. می توانید آن را در کنسول جاوا اسکریپت تست کنید. اگر استثنا وجود دارد، انتخابگر خود را بازنویسی کنید تا از هیچ پسوند خاصی از چارچوب جاوا اسکریپت خود استفاده نکنید. این کار انتخابگر شما را در مرورگرهای مدرن با مرتبه بزرگی سرعت می بخشد.
اگر این کمک نمی کند یا اگر می خواهید در مرورگرهای مدرن سریع باشید، این دستورالعمل ها را دنبال کنید:
- تا حد امکان در سمت راست انتخابگر خود مشخص باشید.
- از نام برچسبی استفاده کنید که اغلب از آن به عنوان سمت راست ترین قسمت انتخابگر استفاده نمی کنید.
- اگر هیچ کمکی نکرد، به بازنویسی چیزها فکر کنید تا بتوانید از یک id-selector استفاده کنید
تمام این دستکاری های DOM زمان زیادی می برد
دستهای از درجها، حذفها و بهروزرسانیهای گره DOM میتوانند واقعاً کند باشند. به طور کلی می توان با ایجاد یک رشته بزرگ از html و استفاده از domNode.innerHTML = newHTML
برای جایگزینی محتوای قدیمی بهینه سازی کرد. توجه داشته باشید که این ممکن است برای نگهداری بسیار بد باشد و ممکن است پیوندهای حافظه را در IE ایجاد کند، پس مراقب باشید.
یکی دیگر از مشکلات رایج این است که کد اولیه شما ممکن است مقدار زیادی HTML ایجاد کند. به عنوان مثال یک پلاگین jQuery که یک جعبه انتخاب را به مجموعهای از div تبدیل میکند، زیرا این همان چیزی است که افراد طراحی بدون اطلاع از بهترین شیوههای UX میخواستند. اگر واقعاً می خواهید صفحه شما سریع باشد، هرگز این کار را نکنید. در عوض تمام نشانهگذاریها را از سمت سرور به شکل نهایی تحویل دهید. این دوباره مشکلات زیادی دارد، بنابراین خوب فکر کنید که آیا سرعت ارزش آن را دارد یا خیر.
ابزار
- JSPerf - نمونه های کوچک جاوا اسکریپت را معیار قرار دهید
- Firebug - برای پروفایل در فایرفاکس
- ابزارهای توسعه دهنده Google Chrome (در دسترس به عنوان WebInspector در سافاری)
- DOM Monster - برای بهینه سازی عملکرد DOM
- DynaTrace Ajax Edition - برای پروفایل و بهینه سازی رنگ در اینترنت اکسپلورر
در ادامه مطلب
،مقدمه
HTML5 ابزارهای عالی برای بهبود ظاهر بصری برنامه های کاربردی وب به ما می دهد. این امر به ویژه در حوزه انیمیشن ها صادق است. با این حال، با این قدرت جدید چالش های جدیدی نیز به وجود می آید. در واقع این چالشها واقعاً جدید نیستند و گاهی اوقات ممکن است منطقی باشد که از همسایه میز خود، برنامهنویس Flash، بپرسید که چگونه در گذشته بر موارد مشابه غلبه کرده است.
به هر حال، وقتی شما در انیمیشن کار می کنید، بسیار مهم است که کاربران این انیمیشن ها را روان و روان درک کنند. چیزی که ما باید بدانیم این است که نرمی در انیمیشن ها واقعاً با افزایش فریم در ثانیه فراتر از هر آستانه شناختی ایجاد نمی شود. مغز ما متأسفانه باهوش تر از این حرف هاست. چیزی که یاد خواهید گرفت این است که 30 فریم واقعی انیمیشن (فریم در ثانیه) بسیار بهتر از 60 فریم در ثانیه است که فقط چند فریم در وسط افت می کند. مردم از تنبلی متنفرند.
این مقاله سعی میکند ابزارها و تکنیکهایی را در اختیار شما بگذارد تا روی بهبود تجربه اپلیکیشن خود کار کنید.
استراتژی
به هیچ وجه نمی خواهیم شما را از ساختن برنامه های بصری عالی و خیره کننده با HTML5 منصرف کنیم.
سپس وقتی متوجه شدید که عملکرد می تواند کمی بهتر باشد، به اینجا برگردید و در مورد چگونگی بهبود عناصر برنامه خود مطالعه کنید. البته میتواند در وهله اول به انجام درست برخی کارها کمک کند، اما هرگز اجازه ندهید که این کارها مانع از کارآمدی شما شود.
Visual fidelity++ با HTML5
شتاب سخت افزاری
شتاب سخت افزاری یک نقطه عطف مهم برای عملکرد کلی رندر در مرورگر است. طرح کلی این است که وظایفی را که در غیر این صورت توسط CPU اصلی محاسبه میشوند، به واحد پردازش گرافیکی (GPU) در آداپتور گرافیک رایانهتان بارگذاری کنید. این می تواند دستاوردهای عملکرد گسترده ای داشته باشد و همچنین می تواند مصرف منابع را در دستگاه های تلفن همراه کاهش دهد.
این جنبه های سند شما می تواند توسط GPU تسریع شود
- ترکیب چیدمان عمومی
- انتقال CSS3
- CSS3 3D تبدیل می شود
- طراحی بوم
- طراحی سه بعدی WebGL
در حالی که تسریع بوم و WebGL ویژگیهای هدف خاصی هستند که ممکن است برای برنامه خاص شما اعمال نشوند، سه جنبه اول تقریباً به هر برنامه کمک میکند تا سریعتر شود.
چه چیزی را می توان تسریع کرد؟
شتاب GPU با بارگذاری وظایف کاملاً تعریف شده و خاص به سخت افزار با هدف خاص کار می کند. طرح کلی این است که سند شما به چندین "لایه" تقسیم می شود که نسبت به جنبه های صفحه شما که تسریع می شود تغییر نمی کند. این لایه ها با استفاده از خط لوله رندر سنتی رندر می شوند. سپس از GPU برای ترکیب لایهها در یک صفحه استفاده میشود و «افکتهایی» را اعمال میکند که میتوانند در پرواز تسریع شوند. یک نتیجه احتمالی این است که یک شی که روی صفحه متحرک است نیازی به یک "relayout" صفحه نداشته باشد در حالی که انیمیشن اتفاق می افتد.
چیزی که باید از آن حذف کنید این است که باید کاری کنید که موتور رندر تشخیص دهد که چه زمانی می تواند جادوی شتاب GPU خود را اعمال کند. به مثال زیر توجه کنید:
در حالی که این کار می کند، مرورگر واقعاً نمی داند که شما چیزی را اجرا می کنید که قرار است توسط یک انسان به عنوان انیمیشن صاف درک شود. در نظر بگیرید که چه اتفاقی میافتد وقتی به همان ظاهر بصری با استفاده از انتقالهای CSS3 دست پیدا میکنید:
نحوه پیاده سازی این انیمیشن توسط مرورگر کاملاً از دید توسعه دهنده پنهان است. این به نوبه خود به این معنی است که مرورگر می تواند ترفندهایی مانند شتاب GPU را برای رسیدن به هدف تعریف شده اعمال کند.
دو پرچم خط فرمان مفید برای کروم برای کمک به رفع اشکال شتاب GPU وجود دارد:
-
--show-composited-layer-borders
یک حاشیه قرمز رنگ اطراف عناصری را نشان می دهد که در سطح GPU دستکاری می شوند. برای تأیید اینکه دستکاری های شما در لایه GPU رخ می دهد خوب است. -
--show-paint-rects
تمام تغییرات غیر GPU رنگ شده اند و این یک حاشیه روشن در اطراف همه مناطقی که دوباره رنگ شده است ایجاد می کند. شما می توانید مرورگر را در حال بهینه سازی مناطق رنگ آمیزی مشاهده کنید.
سافاری دارای پرچمهای زمان اجرا مشابهی است که در اینجا توضیح داده شده است .
انتقال CSS3
CSS Transitions انیمیشن سبک را برای همه بی اهمیت می کند، اما آنها همچنین یک ویژگی عملکرد هوشمند هستند. از آنجایی که یک انتقال CSS توسط مرورگر مدیریت می شود، وفاداری انیمیشن آن را می توان تا حد زیادی بهبود بخشید و در بسیاری از موارد سخت افزار را تسریع کرد. در حال حاضر WebKit (Chrome، Safari، iOS) تغییرات سخت افزاری CSS را تسریع کرده است، اما به سرعت به مرورگرها و پلتفرم های دیگر می آید.
میتوانید از رویدادهای transitionEnd
برای اسکریپت کردن آن در ترکیبهای قدرتمند استفاده کنید، اگرچه در حال حاضر، ثبت همه رویدادهای پایان انتقال پشتیبانیشده به معنای تماشای webkitTransitionEnd transitionend oTransitionEnd
است.
اکنون بسیاری از کتابخانهها APIهای انیمیشنی را معرفی کردهاند که در صورت وجود از انتقالها استفاده میکنند و در غیر این صورت به انیمیشنهای استاندارد DOM بازمیگردند. scripty2 , YUI Transition , jQuery anime بهبود یافته .
ترجمه CSS3
من مطمئن هستم که قبلاً متوجه شده اید که موقعیت x/y یک عنصر را در سراسر صفحه متحرک کرده اید. احتمالاً ویژگی های سمت چپ و بالای سبک درون خطی را دستکاری کرده اید. با تبدیل های دو بعدی، می توانیم از قابلیت translate()
برای تکرار این رفتار استفاده کنیم.
ما می توانیم این را با انیمیشن DOM ترکیب کنیم تا از بهترین چیز ممکن استفاده کنیم
<div style="position:relative; height:120px;" class="hwaccel">
<div style="padding:5px; width:100px; height:100px; background:papayaWhip;
position:absolute;" id="box">
</div>
</div>
<script>
document.querySelector('#box').addEventListener('click', moveIt, false);
function moveIt(evt) {
var elem = evt.target;
if (Modernizr.csstransforms && Modernizr.csstransitions) {
// vendor prefixes omitted here for brevity
elem.style.transition = 'all 3s ease-out';
elem.style.transform = 'translateX(600px)';
} else {
// if an older browser, fall back to jQuery animate
jQuery(elem).animate({ 'left': '600px'}, 3000);
}
}
</script>
ما از Modernizr برای تست ویژگی برای CSS 2D Transforms و CSS Transitions استفاده می کنیم، اگر چنین است، ما از translate برای تغییر موقعیت استفاده می کنیم. اگر این انیمیشن با استفاده از یک انتقال متحرک باشد، احتمال زیادی وجود دارد که مرورگر بتواند سخت افزار آن را تسریع کند. برای اینکه مرورگر را در جهت درست فشار دهیم، از "گلوله جادویی CSS" از بالا استفاده می کنیم.
اگر مرورگر ما توانایی کمتری دارد، برای جابجایی عنصر خود به جی کوئری بازگشته ایم. شما می توانید پلاگین jQuery Transform polyfill توسط Louis-Remi Babe را انتخاب کنید تا کل این کار به صورت خودکار انجام شود.
window.requestAnimationFrame
requestAnimationFrame
توسط Mozilla معرفی شد و توسط WebKit تکرار شد و هدف آن ارائه یک API بومی برای اجرای انیمیشنها، خواه مبتنی بر DOM/CSS یا <canvas>
یا WebGL باشد. مرورگر میتواند انیمیشنهای همزمان را با هم در یک چرخه مجدد و رنگآمیزی بهینهسازی کند، که منجر به انیمیشنهایی با وفاداری بالاتر میشود. به عنوان مثال، انیمیشن های مبتنی بر JS که با انتقال CSS یا SVG SMIL همگام شده اند. بهعلاوه، اگر حلقه انیمیشن را در برگهای اجرا میکنید که قابل مشاهده نیست، مرورگر آن را در حال اجرا نگه نمیدارد ، که به معنای استفاده کمتر از CPU، GPU و حافظه است که منجر به عمر باتری بسیار بیشتر میشود.
برای جزئیات بیشتر در مورد چگونگی و چرایی استفاده requestAnimationFrame
، مقاله Paul Irish requestAnimationFrame برای انیمیشن هوشمند را مشاهده کنید.
پروفایل کردن
هنگامی که متوجه شدید که سرعت برنامه شما می تواند بهبود یابد، زمان آن فرا رسیده است که به نمایه سازی بپردازید تا دریابید که در کجا بهینه سازی می تواند بیشترین سود را داشته باشد. بهینه سازی ها اغلب تأثیر منفی بر قابلیت نگهداری کد منبع شما خواهند داشت و بنابراین فقط در صورت لزوم باید اعمال شوند. نمایه سازی به شما می گوید که کدام بخش از کد شما با بهبود عملکرد آنها بیشترین مزیت را دارد.
پروفایل جاوا اسکریپت
نمایه سازهای جاوا اسکریپت با اندازه گیری زمان لازم برای اجرای هر تابع از ابتدا تا انتهای آن، یک نمای کلی از عملکرد برنامه شما در سطح عملکرد جاوا اسکریپت به شما ارائه می دهند.
زمان اجرای ناخالص یک تابع زمان کلی است که برای اجرای آن از بالا به پایین طول می کشد. زمان اجرای خالص، زمان اجرای ناخالص منهای زمانی است که برای اجرای توابع فراخوانی شده از تابع صرف شده است.
برخی از توابع بیشتر از بقیه فراخوانی می شوند. پروفیلها معمولاً زمان لازم برای اجرای همه فراخوانها و همچنین میانگین و حداقل و حداکثر زمان اجرا را به شما میدهند.
برای جزئیات بیشتر، اسناد Chrome Dev Tools در مورد نمایه سازی را بررسی کنید.
DOM
عملکرد جاوا اسکریپت تأثیر زیادی بر میزان روان و پاسخگو بودن برنامه شما دارد. درک این نکته مهم است که در حالی که پروفایلکنندگان جاوا اسکریپت زمان اجرای جاوا اسکریپت شما را اندازهگیری میکنند، آنها بهطور غیرمستقیم زمان صرف شده برای انجام عملیات DOM را نیز اندازهگیری میکنند. این عملیات DOM اغلب در قلب مشکلات عملکرد شما قرار دارند.
function drawArray(array) {
for(var i = 0; i < array.length; i++) {
document.getElementById('test').innerHTML += array[i]; // No good :(
}
}
به عنوان مثال در کد بالا تقریباً هیچ زمانی برای اجرای جاوا اسکریپت واقعی صرف نمی شود. هنوز هم بسیار محتمل است که عملکرد Drawray در پروفایل های شما ظاهر شود زیرا در تعامل با DOM با روشی بسیار بی فایده است.
نکات و ترفندها
توابع ناشناس
عملکردهای ناشناس به نمایش درآمده است زیرا ذاتاً نامی ندارند که تحت آن بتوانند در پروفایل ظاهر شوند. دو روش برای کار در این زمینه وجود دارد:
$('.stuff').each(function() { ... });
بازنویسی به:
$('.stuff').each(function workOnStuff() { ... });
معمولاً مشخص نیست که JavaScript از عبارات عملکردی پشتیبانی می کند. انجام این کار باعث می شود که آنها کاملاً در پروفایل ظاهر شوند. یک مشکل در این راه حل وجود دارد: عبارت نامگذاری شده در واقع نام عملکرد را در دامنه واژگانی فعلی قرار می دهد. این ممکن است نمادهای دیگر را ببندد ، بنابراین مراقب باشید.
پروفایل عملکردهای طولانی
تصور کنید که عملکرد طولانی دارید و گمان می کنید که بخش کوچکی از آن ممکن است دلیل مشکلات عملکرد شما باشد. دو روش برای یافتن مشکل وجود دارد:
- روش صحیح: کد خود را بازپرداخت کنید تا هیچ کارکرد طولانی را شامل نشود.
- روش شیطانی دریافت-این روش: بیانیه هایی را به صورت توابع خود فراخوانی نامگذاری شده به کد خود اضافه کنید. اگر کمی مراقب باشید ، این معناشناسی را تغییر نمی دهد و باعث می شود بخش هایی از عملکرد شما به عنوان توابع فردی در پروفایل نمایش داده شوند:
js function myLongFunction() { ... (function doAPartOfTheWork() { ... })(); ... }
فراموش نکنید که این کارکردهای اضافی را پس از انجام پروفایل انجام دهید. یا حتی از آنها به عنوان نقطه شروع برای بازپرداخت کد خود استفاده کنید.
پروفایل DOM
آخرین ابزارهای توسعه بازرس وب Chrome شامل "نمای جدول زمانی" جدید است که جدول زمانی از اقدامات سطح پایین را که توسط مرورگر انجام می شود ، نشان می دهد. می توانید از این اطلاعات برای بهینه سازی عملیات دامنه خود استفاده کنید. شما باید هدف شما کاهش تعداد "اقدامات" مرورگر هنگام اجرای کد شما باشد.
نمای جدول زمانی می تواند اطلاعات عظیمی را ایجاد کند. بنابراین باید سعی کنید موارد آزمایش حداقل ایجاد کنید که بتوانید به طور مستقل اجرا کنید.

تصویر بالا خروجی نمای جدول زمانی را برای یک اسکریپت بسیار ساده نشان می دهد. صفحه سمت چپ عملیات انجام شده توسط مرورگر را به ترتیب مزمن نشان می دهد ، در حالی که جدول زمانی در صفحه سمت راست زمان واقعی مصرف شده توسط یک عمل فردی را نشان می دهد.
اطلاعات بیشتر در مورد نمای جدول زمانی. ابزاری جایگزین برای پروفایل در اینترنت اکسپلورر است Dynatrace Ajax Edition .
استراتژی های پروفایل
جنبه های مجرد
هنگامی که می خواهید برنامه خود را مشخص کنید ، سعی کنید جنبه های عملکرد آن را که ممکن است باعث کندتر کند تا حد ممکن باشد ، تکراری کنید. سپس سعی کنید یک پروفایل را اجرا کنید که فقط بخش هایی از کد شما را که مربوط به این جنبه های برنامه شما است ، اجرا می کند. این باعث می شود تفسیر داده های پروفایل آسان تر شود زیرا با مسیرهای کد که مربوط به مشکل واقعی شما نیست ، در هم آمیخته نمی شود. مثالهای خوب برای جنبه های فردی برنامه شما ممکن است:
- زمان راه اندازی (Profiler ، برنامه بارگیری مجدد را فعال کنید ، صبر کنید تا اولیه سازی کامل شود ، پروفایل را متوقف کنید.
- روی یک دکمه و انیمیشن بعدی کلیک کنید (شروع به کار کنید ، دکمه را کلیک کنید ، صبر کنید تا انیمیشن کامل شود ، Profiler را متوقف کنید).
پروفایل
اجرای فقط قسمت مناسب برنامه شما می تواند در یک برنامه GUI سخت تر از زمان بهینه سازی باشد ، مثلاً ردیاب ری موتور سه بعدی شما. به عنوان مثال ، هنگامی که شما می خواهید مواردی را که هنگام کلیک بر روی یک دکمه اتفاق می افتد ، مشخص کنید ، ممکن است حوادث ماوس را در طول راه ایجاد کنید که نتایج شما را کمتر کند. سعی کنید از آن جلوگیری کنید :)
رابط برنامه ای
همچنین یک رابط برنامه ای برای فعال کردن اشکال زدایی وجود دارد. این اجازه می دهد تا هنگام شروع پروفایل کنترل دقیق بر روی پروفایل انجام شود.
یک پروفایل را با:
console.profile()
متوقف کردن پروفایل با:
console.profileEnd()
تکرارپذیری
هنگامی که پروفایل را انجام می دهید ، اطمینان حاصل کنید که در واقع می توانید نتایج خود را بازتولید کنید. فقط در این صورت شما می توانید بگویید که آیا بهینه سازی های شما در واقع باعث بهبود امور شده است یا خیر. همچنین پروفایل سطح عملکرد در متن کل رایانه شما انجام می شود. علم دقیقی نیست. اجرای مشخصات فردی ممکن است تحت تأثیر بسیاری از موارد دیگر در رایانه شما باشد:
- یک تایمر نامربوط در برنامه شخصی شما که هنگام اندازه گیری چیز دیگری آتش می گیرد.
- جمع کننده زباله کار خود را انجام می دهد.
- برگه دیگری در مرورگر شما در همان موضوع عملیاتی کار سختی را انجام می دهد.
- برنامه دیگری در رایانه شما با استفاده از CPU از این طریق باعث کندتر برنامه شما می شود.
- تغییرات ناگهانی در میدان گرانشی زمین.
همچنین منطقی است که چندین بار در یک جلسه پروفایل همان مسیر کد را اجرا کنید. به این ترتیب شما تأثیر عوامل فوق را کاهش می دهید و قسمت های آهسته ممکن است با وضوح بیشتری از آن استفاده کنند.
اندازه گیری ، بهبود ، اندازه گیری
هنگامی که یک نقطه آهسته در برنامه خود را مشخص کردید ، سعی کنید به روش هایی برای بهبود رفتار اجرای فکر کنید. بعد از تغییر کد ، نمایه دوباره. اگر از نتیجه راضی هستید ، حرکت کنید ، اگر شاهد پیشرفت نیستید ، احتمالاً باید تغییر خود را به عقب برگردانید و آن را در "زیرا نمی تواند صدمه ببیند" بگذارید.
استراتژی های بهینه سازی
تعامل DOM را به حداقل برسانید
یک موضوع مشترک برای بهبود سرعت برنامه های مشتری وب ، به حداقل رساندن تعامل DOM است. در حالی که سرعت موتورهای جاوا اسکریپت با ترتیب بزرگی افزایش یافته است ، دسترسی به DOM با همان سرعت سریعتر نمی شود. این همچنین به دلایل بسیار عملی هرگز اتفاق نمی افتد (مواردی مانند چیدمان و ترسیم وسایل روی صفحه نمایش فقط به زمان می رسد).
گره های حافظه پنهان
هر زمان که یک گره یا لیستی از گره ها را از DOM بازیابی کنید ، سعی کنید در مورد اینکه آیا ممکن است در محاسبات بعدی (یا حتی فقط تکرار حلقه بعدی) از آنها استفاده مجدد کنید ، فکر کنید. تا زمانی که در واقع گره ها را در منطقه مربوطه اضافه یا حذف نکنید ، این مورد اغلب است.
قبل از:
function getElements() {
return $('.my-class');
}
بعد از:
var cachedElements;
function getElements() {
if (cachedElements) {
return cachedElements;
}
cachedElements = $('.my-class');
return cachedElements;
}
مقادیر ویژگی حافظه پنهان
به همان روشی که می توانید گره های DOM را ذخیره کنید ، می توانید مقادیر ویژگی ها را ذخیره کنید. تصور کنید که شما یک ویژگی از سبک یک گره را متحرک می کنید. اگر می دانید که شما (مانند آن قسمت از کد) تنها کسی هستید که تا به حال آن ویژگی را لمس می کنید ، می توانید آخرین مقدار را در هر تکرار ذخیره کنید تا نیازی به خواندن آن نباشید.
قبل از:
setInterval(function() {
var ele = $('#element');
var left = parseInt(ele.css('left'), 10);
ele.css('left', (left + 5) + 'px');
}, 1000 / 30);
بعد از: js var ele = $('#element'); var left = parseInt(ele.css('left'), 10); setInterval(function() { left += 5; ele.css('left', left + 'px'); }, 1000 / 30);
دستکاری دام را از حلقه ها منتقل کنید
حلقه ها اغلب برای بهینه سازی نقاط داغ هستند. سعی کنید به روش هایی برای تجزیه شماره واقعی خرد کردن برای کار با DOM فکر کنید. انجام یک محاسبه اغلب امکان پذیر است و پس از اتمام آن ، تمام نتایج را در یک حرکت اعمال کنید.
قبل از:
document.getElementById('target').innerHTML = '';
for(var i = 0; i < array.length; i++) {
var val = doSomething(array[i]);
document.getElementById('target').innerHTML += val;
}
بعد از:
var stringBuilder = [];
for(var i = 0; i < array.length; i++) {
var val = doSomething(array[i]);
stringBuilder.push(val);
}
document.getElementById('target').innerHTML = stringBuilder.join('');
redraws و reflows
همانطور که قبلاً در مورد دسترسی به DOM بحث شد ، نسبتاً کند است. وقتی کد شما در حال خواندن مقداری است که باید محاسبه شود بسیار کند می شود زیرا کد شما اخیراً چیزی را که در DOM مربوط است اصلاح کرده است. بنابراین ، باید از آن جلوگیری کرد تا خواندن و نوشتن دسترسی به DOM را در هم آمیخته شود. در حالت ایده آل ، کد شما همیشه باید در دو مرحله گروه بندی شود:
- مرحله 1: مقادیر DOM لازم برای کد خود را بخوانید
- فاز 2: DOM را اصلاح کنید
سعی کنید الگویی مانند:
- فاز 1: مقادیر DOM را بخوانید
- فاز 2: DOM را اصلاح کنید
- فاز 3: موارد دیگری را بخوانید
- فاز 4: DOM را در جایی دیگر اصلاح کنید.
قبل از:
function paintSlow() {
var left1 = $('#thing1').css('left');
$('#otherThing1').css('left', left);
var left2 = $('#thing2').css('left');
$('#otherThing2').css('left', left);
}
بعد از:
function paintFast() {
var left1 = $('#thing1').css('left');
var left2 = $('#thing2').css('left');
$('#otherThing1').css('left', left);
$('#otherThing2').css('left', left);
}
این توصیه باید برای اقداماتی که در یک زمینه اجرای JavaScript اتفاق می افتد در نظر گرفته شود. (به عنوان مثال در یک کنترل کننده رویداد ، در یک کنترل کننده فواصل یا هنگام رسیدگی به پاسخ AJAX.)
اجرای تابع paintSlow()
از بالا این تصویر را ایجاد می کند:

جابجایی به اجرای سریعتر این تصویر را بازده می دهد:

این تصاویر نشان می دهد که تغییر مسیر به نحوه دسترسی کد شما به DOM می تواند عملکرد رندر را تا حد زیادی افزایش دهد. در این حالت ، کد اصلی باید دو بار را محاسبه کرده و صفحه را دو بار چیدمان کند تا نتیجه مشابه ایجاد شود. بهینه سازی مشابه می تواند برای همه کد "دنیای واقعی" اعمال شود و نتایج بسیار چشمگیری را به همراه داشته باشد.
بیشتر بخوانید: Rendering: repaint ، Reflow/Relayout ، Restyle توسط Stoyan Stefanov
redraws و حلقه رویداد
اجرای JavaScript در مرورگر از یک مدل "حلقه رویداد" پیروی می کند. به طور پیش فرض مرورگر در حالت "بیکار" قرار دارد. این حالت را می توان با وقایع از تعامل کاربر یا مواردی مانند تایمرهای JavaScript یا تماس های برگشتی AJAX قطع کرد. هر زمان که یک قطعه جاوا اسکریپت در چنین نقطه وقفه ای اجرا شود ، مرورگر معمولاً منتظر می ماند تا آن را به پایان برساند تا صفحه را دوباره رنگ آمیزی کند (ممکن است استثنائاتی برای جاوا اسکریپت های بسیار طولانی یا در مواردی مانند جعبه های هشدار وجود داشته باشد که به طور مؤثر اجرای Javascript را قطع می کند).
عواقب
- اگر چرخه انیمیشن JavaScript شما برای اجرای بیش از 1/30 ثانیه طول بکشد ، شما قادر به ایجاد انیمیشن های صاف نخواهید بود زیرا مرورگر در طول اجرای JS دوباره رنگ نمی شود. وقتی انتظار دارید که رویدادهای کاربر را نیز اداره کنید ، باید خیلی سریعتر باشید.
- گاهی اوقات به تأخیر انداختن برخی از اقدامات جاوا اسکریپت تا کمی بعد ، مفید است. به عنوان مثال
setTimeout(function() { ... }, 0)
این به طور مؤثر به مرورگر می گوید که به محض اینکه حلقه رویداد دوباره بیکار است ، پاسخ به تماس را انجام دهد (به طور موثری برخی از مرورگرها حداقل 10 متر صبر می کنند). شما باید توجه داشته باشید که این دو چرخه اجرای JavaScript را ایجاد می کند که به موقع بسیار نزدیک هستند. هر دو ممکن است باعث رنگ آمیزی صفحه نمایش شوند که ممکن است زمان کلی صرف شده با نقاشی را دو برابر کند. این که آیا این در واقع باعث ایجاد دو رنگ می شود ، به اکتشافی در مرورگر بستگی دارد.
نسخه منظم:
function paintFast() {
var height1 = $('#thing1').css('height');
var height2 = $('#thing2').css('height');
$('#otherThing1').css('height', '20px');
$('#otherThing2').css('height', '20px');
}

بیایید مقداری تأخیر اضافه کنیم:
function paintALittleLater() {
var height1 = $('#thing1').css('height');
var height2 = $('#thing2').css('height');
$('#otherThing1').css('height', '20px');
setTimeout(function() {
$('#otherThing2').css('height', '20px');
}, 10)
}

نسخه تأخیر نشان می دهد که مرورگر دو بار نقاشی می کند اگرچه دو تغییر در صفحه فقط 1/100 قسمت دوم است.
Lazy Initialization
کاربران برنامه های وب را می خواهند که به سرعت بارگیری می شوند و احساس پاسخگو می کنند. با این حال ، کاربران بسته به عملی که انجام می دهند ، آستانه های متفاوتی در مورد آنچه که آنها را کند می دانند ، دارند. به عنوان مثال یک برنامه هرگز نباید محاسبات زیادی را در یک رویداد ماوسور انجام دهد زیرا این ممکن است تجربه کاربری بدی را ایجاد کند در حالی که کاربر همچنان به حرکت ماوس خود ادامه می دهد. با این حال ، کاربران برای پذیرش کمی تأخیر پس از کلیک بر روی یک دکمه استفاده می شوند.
بنابراین ممکن است منطقی باشد که کد اولیه سازی خود را تا حد ممکن اجرا کنید (به عنوان مثال وقتی کاربر روی دکمه ای کلیک می کند که یک مؤلفه خاص از برنامه شما را فعال می کند).
قبل از: js var things = $('.ele > .other * div.className'); $('#button').click(function() { things.show() });
بعد از: js $('#button').click(function() { $('.ele > .other * div.className').show() });
نمایندگی
گسترش دستگیران رویداد در یک صفحه ممکن است مدت زمان نسبتاً طولانی داشته باشد و همچنین می تواند خسته کننده باشد ، پس از تعویض عناصر به صورت پویا که پس از آن نیاز به مجدداً به دستگیرندگان رویداد را به عناصر جدید نیاز دارد.
راه حل در این مورد استفاده از تکنیکی به نام نمایندگی رویداد است. به جای وصل کردن دستیار رویدادهای انفرادی به عناصر ، ماهیت حباب بسیاری از رویدادهای مرورگر با وصل کردن کنترل کننده رویداد به گره والدین و بررسی گره هدف این رویداد برای دیدن اینکه آیا این رویداد مورد علاقه است یا خیر ، استفاده می شود.
در jQuery این می تواند به راحتی بیان شود:
$('#parentNode').delegate('.button', 'click', function() { ... });
در صورت عدم استفاده از نمایندگی رویداد
بعضی اوقات برعکس می تواند درست باشد: شما از نمایندگی رویداد استفاده می کنید و مشکل عملکرد دارید. اساساً نمایندگی رویداد اجازه می دهد تا زمان اولیه سازی تداعی ثابت. با این حال ، قیمت بررسی در صورتی که یک رویداد مورد علاقه باشد برای هر دعوت از آن رویداد باید پرداخت شود. این ممکن است گران باشد ، به خصوص برای وقایعی که اغلب مانند "ماوس" یا حتی "mousemove" اتفاق می افتد.
مشکلات و راه حل های معمولی
کارهایی که من در $(document).ready
مشاوره شخصی Malte: هرگز کاری را با $(document).ready
انجام ندهید. سعی کنید سند خود را به شکل نهایی خود تحویل دهید. خوب ، شما مجاز به ثبت نام شنوندگان رویداد هستید ، اما فقط با استفاده از شناسه شناسه و/یا با استفاده از نمایندگی رویداد. برای رویدادهای گران قیمت مانند "Mousemove" ، ثبت نام را تا زمان نیاز به تأخیر می اندازد (رویداد ماوس روی عنصر مربوطه).
و اگر واقعاً نیاز به انجام کارها دارید ، مانند درخواست AJAX برای دریافت داده های واقعی ، یک انیمیشن زیبا را نشان دهید. اگر این یک GIF متحرک یا موارد مشابه باشد ، ممکن است بخواهید انیمیشن را به عنوان یک داده URI درج کنید.
از آنجا که من یک فیلم فلش به صفحه اضافه کردم همه چیز واقعاً کند است
اضافه کردن فلاش به یک صفحه همیشه کمی سرعت را کاهش می دهد زیرا طرح نهایی پنجره باید بین مرورگر و افزونه فلش "مذاکره شود". هنگامی که نمی توانید به طور کامل از قرار دادن فلاش در صفحات خود جلوگیری کنید ، حتماً پارامتر "WMode" را به مقدار "پنجره" تنظیم کنید (که پیش فرض است). این امر توانایی کامپوزیت HTML و عناصر فلش را غیرفعال می کند (شما قادر به دیدن یک عنصر HTML نخواهید بود که در بالای فیلم فلش قرار دارد و فیلم فلش شما نمی تواند شفاف باشد). این ممکن است یک ناراحتی باشد اما عملکرد شما را به طرز چشمگیری بهبود می بخشد. به عنوان مثال ، روشی را که YouTube.com با دقت از قرار دادن لایه های بالای پخش کننده فیلم اصلی جلوگیری می کند ، بررسی کنید.
من در حال ذخیره چیزهایی در LocalStorage هستم ، اکنون لکنت های برنامه من
نوشتن به LocalStorage یک عملیات همزمان است که شامل چرخش دیسک سخت شما است. شما هرگز نمی خواهید در حین انجام انیمیشن ، عملیات همزمان "دویدن طولانی" را انجام دهید. دسترسی به LocalStorage را به نقطه ای از کد خود منتقل کنید که در آن مطمئن هستید که کاربر بیکار است و هیچ انیمیشن در جریان نیست.
نمایه سازی به انتخاب کننده jQuery بسیار کند است
ابتدا می خواهید اطمینان حاصل کنید که انتخاب کننده شما می تواند از طریق Document.QuerySelectorall اجرا شود. می توانید آن را در کنسول JavaScript آزمایش کنید. اگر یک استثنا وجود دارد ، انتخاب کننده خود را بازنویسی کنید تا از هیچ برنامه ویژه ای از چارچوب JavaScript خود استفاده نکنید. این امر باعث می شود تا انتخاب شما در مرورگرهای مدرن با ترتیب بزرگی سرعت بخشید.
اگر این کمک نمی کند یا اگر می خواهید در مرورگرهای مدرن نیز سریع باشید ، این دستورالعمل ها را دنبال کنید:
- تا حد امکان در سمت راست انتخاب خود خاص باشید.
- از یک نام برچسب استفاده کنید که اغلب به عنوان مناسب ترین قسمت انتخابی از آن استفاده نمی کنید.
- اگر هیچ چیز کمک نمی کند ، در مورد بازنویسی چیزها فکر کنید تا بتوانید از یک شناسه شناسه استفاده کنید
همه این دستکاری های DOM مدت زیادی طول می کشد
دسته ای از گره های DOM درج ، حذف و به روزرسانی ها می توانند واقعاً کند باشند. این به طور کلی می تواند با تولید رشته بزرگی از HTML و استفاده از domNode.innerHTML = newHTML
برای جایگزینی محتوای قدیمی بهینه شود. توجه داشته باشید که این ممکن است برای حفظ قابلیت بسیار بد باشد و ممکن است پیوندهای حافظه را در اینترنت اکسپلورر ایجاد کند ، بنابراین مراقب باشید.
مشکل متداول دیگر این است که کد اولیه سازی شما ممکن است HTML زیادی ایجاد کند. به عنوان مثال یک افزونه jQuery که یک جعبه انتخابی را به یک دسته از قسمت ها تبدیل می کند ، زیرا این همان چیزی است که افراد طراحی می خواستند در جهل بهترین روشهای UX. اگر واقعاً می خواهید صفحه شما سریع باشد ، هرگز این کار را نکنید. در عوض ، تمام نشانه ها را از سمت سرور در فرم نهایی خود تحویل دهید. این دوباره مشکلات بسیاری دارد ، بنابراین فکر می کنید آیا سرعت ارزش تجارت را دارد یا خیر.
ابزار
- JSPERF - قطعه قطعه کوچک JavaScript
- Firebug - برای پروفایل در Firefox
- ابزارهای توسعه دهنده Google Chrome (به عنوان WebInspector در Safari موجود است)
- DOM MONSTER - برای بهینه سازی عملکرد DOM
- Dynatrace Ajax Edition - برای پروفایل و بهینه سازی در اینترنت اکسپلورر
در ادامه مطلب
،مقدمه
HTML5 ابزارهای خوبی برای تقویت ظاهر بصری برنامه های وب به ما می دهد. این به ویژه در قلمرو انیمیشن ها صادق است. با این حال ، با این قدرت جدید نیز چالش های جدیدی به وجود آمده است. در واقع این چالش ها واقعاً جدید نیستند و ممکن است گاهی اوقات منطقی باشد که از همسایه دوستانه خود ، برنامه نویس فلش بپرسید ، چگونه او در گذشته بر چیزهای مشابه غلبه کرده است.
به هر حال ، هنگامی که شما در انیمیشن کار می کنید ، بسیار مهم می شود که کاربران این انیمیشن ها را صاف کنند. آنچه ما باید بدانیم این است که با افزایش فریم ها در ثانیه فراتر از هر آستانه شناختی ، نمی توان صافی در انیمیشن ها ایجاد شد. متأسفانه مغز ما باهوش تر از این است. آنچه شما خواهید آموخت این است که 30 فریم از انیمیشن در ثانیه (فریم در ثانیه) بسیار بهتر از 60 فریم در ثانیه است و فقط چند فریم در وسط کاهش یافته است. مردم از جنجال متنفر هستند.
این مقاله سعی خواهد کرد تا ابزارها و تکنیک هایی را برای کار در بهبود تجربه برنامه شخصی خود به شما ارائه دهد.
استراتژی
به هیچ وجه نمی خواهیم شما را از ساخت برنامه های عالی و خیره کننده بصری با HTML5 دلسرد کنیم.
سپس وقتی متوجه شدید که عملکرد می تواند کمی بهتر باشد ، به اینجا برگردید و در مورد چگونگی بهبود عناصر برنامه خود بخوانید. البته این می تواند در وهله اول به انجام برخی کارها کمک کند اما هرگز اجازه نمی دهد که این کار در راه تولید شما باشد.
وفاداری بصری ++ با HTML5
شتاب سخت افزاری
شتاب سخت افزار یک نقطه عطف مهم برای عملکرد کلی رندر در مرورگر است. طرح کلی ، بارگیری وظایفی است که در غیر این صورت توسط پردازنده اصلی به واحد پردازش گرافیک (GPU) در آداپتور گرافیکی رایانه شما محاسبه می شود. این می تواند سود عملکرد گسترده ای داشته باشد و همچنین می تواند مصرف منابع را در دستگاه های تلفن همراه کاهش دهد.
این جنبه های سند شما را می توان با GPU تسریع کرد
- آهنگسازی طرح کلی
- انتقال CSS3
- CSS3 3D تبدیل می شود
- طراحی بوم
- نقاشی سه بعدی WebGL
در حالی که شتاب بوم و WebGL ویژگی های ویژه ای هستند که ممکن است برای برنامه خاص شما صدق نکند ، سه جنبه اول می تواند تقریباً به هر برنامه سریعتر کمک کند.
چه چیزی می تواند تسریع شود؟
شتاب GPU با بارگیری کارهای خوب تعریف شده و خاص به سخت افزار هدف خاص کار می کند. طرح کلی این است که سند شما به چندین "لایه" تقسیم می شود که به جنبه های صفحه شما که شتاب می گیرند متغیر هستند. این لایه ها با استفاده از خط لوله رندر سنتی ارائه می شوند. سپس از GPU برای کامپوزیت لایه ها بر روی یک صفحه واحد استفاده می شود که از "جلوه هایی" استفاده می کند که می تواند در پرواز تسریع شود. نتیجه احتمالی این است که یک شیء که روی صفحه انیمیشن می شود ، در حالی که انیمیشن اتفاق می افتد ، نیازی به "رله" صفحه ندارد.
آنچه شما باید از این امر دور شوید این است که شما باید این کار را برای موتور رندر آسان کنید تا مشخص شود چه زمانی می تواند جادوی شتاب GPU را اعمال کند. به مثال زیر توجه کنید:
در حالی که این کار می کند ، مرورگر واقعاً نمی داند که شما کاری را انجام می دهید که قرار است توسط یک انسان به عنوان انیمیشن صاف تلقی شود. در نظر بگیرید چه اتفاقی می افتد وقتی با استفاده از انتقال CSS3 به همان ظاهر بصری دست می یابید:
نحوه اجرای مرورگر این انیمیشن کاملاً از توسعه دهنده پنهان است. این به نوبه خود به این معنی است که مرورگر قادر به استفاده از ترفندهایی مانند شتاب GPU برای دستیابی به هدف تعریف شده است.
برای کمک به اشکال زدایی در شتاب GPU ، دو پرچم خط فرمان مفید وجود دارد:
-
--show-composited-layer-borders
مرز قرمز را در اطراف عناصری که در سطح GPU دستکاری می شوند ، نشان می دهد. خوب برای تأیید دستکاری های شما در لایه GPU اتفاق می افتد. -
--show-paint-rects
تمام تغییرات غیر GPU رنگ آمیزی شده و این یک مرز سبک را در همه مناطق که رنگ آمیزی شده اند ، پرتاب می کند. می توانید مرورگر را بهینه سازی مناطق نقاشی را در عمل مشاهده کنید.
سافاری دارای پرچم های زمان اجرا مشابه در اینجا است.
انتقال CSS3
انتقال CSS باعث می شود که انیمیشن سبک برای همه بی اهمیت باشد ، اما آنها همچنین یک ویژگی عملکرد هوشمندانه هستند. از آنجا که یک انتقال CSS توسط مرورگر اداره می شود ، وفاداری انیمیشن آن می تواند تا حد زیادی بهبود یابد و در بسیاری از موارد سخت افزار شتاب می یابد. در حال حاضر WebKit (Chrome ، Safari ، iOS) دارای سخت افزار شتاب CSS است ، اما به سرعت به مرورگرها و سیستم عامل های دیگر می رسد.
شما می توانید از رویدادهای transitionEnd
استفاده کنید تا بتوانید این ترکیب را به ترکیبات قدرتمند تبدیل کنید ، اگرچه در حال حاضر ، ضبط تمام رویدادهای انتهای پشتیبانی پشتیبانی شده به معنای تماشای webkitTransitionEnd transitionend oTransitionEnd
است.
بسیاری از کتابخانه ها اکنون API های انیمیشن را معرفی کرده اند که در صورت وجود و انتقال به انیمیشن استاندارد DOM در غیر این صورت ، از انتقال استفاده می کنند. Scripty2 ، Yui Transition ، jQuery Animate پیشرفته .
CSS3 ترجمه
من مطمئن هستم که قبلاً خود را در حال انیمیشن در موقعیت x/y یک عنصر در صفحه قبل از صفحه هستید. احتمالاً خواص سمت چپ و برتر سبک درون خطی را دستکاری کرده اید. با تبدیل 2D ، می توانیم از عملکرد translate()
برای تکرار این رفتار استفاده کنیم.
ما می توانیم این را با انیمیشن DOM جمع کنیم تا از بهترین کار ممکن استفاده کنیم
<div style="position:relative; height:120px;" class="hwaccel">
<div style="padding:5px; width:100px; height:100px; background:papayaWhip;
position:absolute;" id="box">
</div>
</div>
<script>
document.querySelector('#box').addEventListener('click', moveIt, false);
function moveIt(evt) {
var elem = evt.target;
if (Modernizr.csstransforms && Modernizr.csstransitions) {
// vendor prefixes omitted here for brevity
elem.style.transition = 'all 3s ease-out';
elem.style.transform = 'translateX(600px)';
} else {
// if an older browser, fall back to jQuery animate
jQuery(elem).animate({ 'left': '600px'}, 3000);
}
}
</script>
ما از Modernizr برای تست تست برای تبدیل CSS 2D و انتقال CSS استفاده می کنیم ، اگر می خواهیم از ترجمه برای تغییر موقعیت استفاده کنیم. اگر این امر با استفاده از یک انتقال متحرک باشد ، یک فرصت خوب وجود دارد که مرورگر می تواند سخت افزار آن را تسریع کند. برای اینکه به مرورگر فشار دیگری در جهت درست بدهیم ، از بالا از "Bullet Magic CSS" استفاده خواهیم کرد.
اگر مرورگر ما توانایی کمتری داشته باشد ، ما برای جابجایی عنصر خود به jQuery می رویم. شما می توانید افزونه jQuery Transform Polyfill توسط Louis-Remi Babe را انتخاب کنید تا این همه چیز به صورت خودکار باشد.
window.requestAnimationFrame
requestAnimationFrame
توسط Mozilla معرفی شد و توسط WebKit با هدف تهیه یک API بومی برای اجرای انیمیشن ها ، اعم از DOM/CSS مبتنی بر CSS و یا در <canvas>
یا WebGL ، در آن تکرار شد. مرورگر می تواند انیمیشن های همزمان را در یک چرخه بازتاب و رنگ آمیزی مجدد بهینه کند و منجر به انیمیشن وفاداری بالاتر شود. به عنوان مثال ، انیمیشن های مبتنی بر JS با انتقال CSS یا SVG SMIL همزمان می شوند. بعلاوه ، اگر حلقه انیمیشن را در یک برگه اجرا می کنید که قابل مشاهده نیست ، مرورگر آن را اجرا نمی کند ، به این معنی که CPU ، GPU و استفاده از حافظه کمتر است و منجر به عمر باتری بسیار طولانی تر می شود.
برای اطلاعات بیشتر در مورد چگونگی و چرا استفاده requestAnimationFrame
، مقاله Paul Irish را درخواست AnearAmationFrame برای انیمیشن هوشمند مشاهده کنید.
پروفایل کردن
وقتی فهمید که سرعت برنامه شما می تواند بهبود یابد ، زمان آن رسیده است که به پروفایل حفر کنیم تا دریابید که بهینه سازی ها می توانند بیشترین سود را داشته باشند. بهینه سازی ها اغلب تأثیر منفی بر حفظ کد منبع شما خواهد داشت و بنابراین فقط در صورت لزوم باید اعمال شود. پروفایل به شما می گوید که در صورت بهبود عملکرد آنها ، کدام قسمت از کد شما بیشترین مزایا را به همراه خواهد داشت.
پروفایل جاوا اسکریپت
پروفایل های JavaScript با اندازه گیری زمان لازم برای اجرای هر یک از عملکردهای جداگانه از شروع کار خود ، به شما یک مرور کلی در مورد عملکرد برنامه خود در سطح عملکرد JavaScript می دهند.
زمان اجرای ناخالص یک تابع زمان کلی است که برای اجرای آن از بالا به پایین لازم است. زمان اجرای خالص زمان اجرای ناخالص منهای زمان لازم برای اجرای توابع است که از این عملکرد استفاده می شود.
برخی از کارکردها بیشتر از سایرین خوانده می شوند. پروفایل ها معمولاً زمان لازم برای اجرای همه دعوت ها و همچنین میانگین و حداقل و حداکثر زمان اجرای را به شما می دهند.
برای اطلاعات بیشتر ، اسناد Chrome Dev Tools را در مورد پروفایل بررسی کنید.
DOM
عملکرد JavaScript تأثیر زیادی در احساس روان و پاسخگو بودن برنامه شما دارد. درک این نکته حائز اهمیت است که ، در حالی که پروفایل های JavaScript زمان اجرای JavaScript شما را اندازه گیری می کنند ، آنها همچنین به طور غیرمستقیم زمان صرف شده برای انجام عملیات دامنه را اندازه گیری می کنند. این عملیات DOM اغلب در قلب مسائل مربوط به عملکرد شما قرار دارد.
function drawArray(array) {
for(var i = 0; i < array.length; i++) {
document.getElementById('test').innerHTML += array[i]; // No good :(
}
}
به عنوان مثال در کد فوق تقریباً هیچ وقت صرف اجرای واقعی جاوا اسکریپت نمی شود. هنوز هم بسیار محتمل است که عملکرد Drawray در پروفایل های شما ظاهر شود زیرا در تعامل با DOM با روشی بسیار بی فایده است.
نکات و ترفندها
توابع ناشناس
عملکردهای ناشناس به نمایش درآمده است زیرا ذاتاً نامی ندارند که تحت آن بتوانند در پروفایل ظاهر شوند. دو روش برای کار در این زمینه وجود دارد:
$('.stuff').each(function() { ... });
بازنویسی به:
$('.stuff').each(function workOnStuff() { ... });
معمولاً مشخص نیست که JavaScript از عبارات عملکردی پشتیبانی می کند. انجام این کار باعث می شود که آنها کاملاً در پروفایل ظاهر شوند. یک مشکل در این راه حل وجود دارد: عبارت نامگذاری شده در واقع نام عملکرد را در دامنه واژگانی فعلی قرار می دهد. این ممکن است نمادهای دیگر را ببندد ، بنابراین مراقب باشید.
پروفایل عملکردهای طولانی
تصور کنید که عملکرد طولانی دارید و گمان می کنید که بخش کوچکی از آن ممکن است دلیل مشکلات عملکرد شما باشد. دو روش برای یافتن مشکل وجود دارد:
- روش صحیح: کد خود را بازپرداخت کنید تا هیچ کارکرد طولانی را شامل نشود.
- روش شیطانی دریافت-این روش: بیانیه هایی را به صورت توابع خود فراخوانی نامگذاری شده به کد خود اضافه کنید. اگر کمی مراقب باشید ، این معناشناسی را تغییر نمی دهد و باعث می شود بخش هایی از عملکرد شما به عنوان توابع فردی در پروفایل نمایش داده شوند:
js function myLongFunction() { ... (function doAPartOfTheWork() { ... })(); ... }
فراموش نکنید که این کارکردهای اضافی را پس از انجام پروفایل انجام دهید. یا حتی از آنها به عنوان نقطه شروع برای بازپرداخت کد خود استفاده کنید.
پروفایل DOM
آخرین ابزارهای توسعه بازرس وب Chrome شامل "نمای جدول زمانی" جدید است که جدول زمانی از اقدامات سطح پایین را که توسط مرورگر انجام می شود ، نشان می دهد. می توانید از این اطلاعات برای بهینه سازی عملیات دامنه خود استفاده کنید. شما باید هدف شما کاهش تعداد "اقدامات" مرورگر هنگام اجرای کد شما باشد.
نمای جدول زمانی می تواند اطلاعات عظیمی را ایجاد کند. بنابراین باید سعی کنید موارد آزمایش حداقل ایجاد کنید که بتوانید به طور مستقل اجرا کنید.

تصویر بالا خروجی نمای جدول زمانی را برای یک اسکریپت بسیار ساده نشان می دهد. صفحه سمت چپ عملیات انجام شده توسط مرورگر را به ترتیب مزمن نشان می دهد ، در حالی که جدول زمانی در صفحه سمت راست زمان واقعی مصرف شده توسط یک عمل فردی را نشان می دهد.
اطلاعات بیشتر در مورد نمای جدول زمانی. ابزاری جایگزین برای پروفایل در اینترنت اکسپلورر است Dynatrace Ajax Edition .
استراتژی های پروفایل
جنبه های مجرد
هنگامی که می خواهید برنامه خود را مشخص کنید ، سعی کنید جنبه های عملکرد آن را که ممکن است باعث کندتر کند تا حد ممکن باشد ، تکراری کنید. سپس سعی کنید یک پروفایل را اجرا کنید که فقط بخش هایی از کد شما را که مربوط به این جنبه های برنامه شما است ، اجرا می کند. این باعث می شود تفسیر داده های پروفایل آسان تر شود زیرا با مسیرهای کد که مربوط به مشکل واقعی شما نیست ، در هم آمیخته نمی شود. مثالهای خوب برای جنبه های فردی برنامه شما ممکن است:
- زمان راه اندازی (Profiler ، برنامه بارگیری مجدد را فعال کنید ، صبر کنید تا اولیه سازی کامل شود ، پروفایل را متوقف کنید.
- روی یک دکمه و انیمیشن بعدی کلیک کنید (شروع به کار کنید ، دکمه را کلیک کنید ، صبر کنید تا انیمیشن کامل شود ، Profiler را متوقف کنید).
پروفایل
اجرای فقط قسمت مناسب برنامه شما می تواند در یک برنامه GUI سخت تر از زمان بهینه سازی باشد ، مثلاً ردیاب ری موتور سه بعدی شما. به عنوان مثال ، هنگامی که شما می خواهید مواردی را که هنگام کلیک بر روی یک دکمه اتفاق می افتد ، مشخص کنید ، ممکن است حوادث ماوس را در طول راه ایجاد کنید که نتایج شما را کمتر کند. سعی کنید از آن جلوگیری کنید :)
رابط برنامه ای
همچنین یک رابط برنامه ای برای فعال کردن اشکال زدایی وجود دارد. این اجازه می دهد تا هنگام شروع پروفایل کنترل دقیق بر روی پروفایل انجام شود.
یک پروفایل را با:
console.profile()
متوقف کردن پروفایل با:
console.profileEnd()
تکرارپذیری
هنگامی که پروفایل را انجام می دهید ، اطمینان حاصل کنید که در واقع می توانید نتایج خود را بازتولید کنید. فقط در این صورت شما می توانید بگویید که آیا بهینه سازی های شما در واقع باعث بهبود امور شده است یا خیر. همچنین پروفایل سطح عملکرد در متن کل رایانه شما انجام می شود. علم دقیقی نیست. اجرای مشخصات فردی ممکن است تحت تأثیر بسیاری از موارد دیگر در رایانه شما باشد:
- یک تایمر نامربوط در برنامه شخصی شما که هنگام اندازه گیری چیز دیگری آتش می گیرد.
- جمع کننده زباله کار خود را انجام می دهد.
- برگه دیگری در مرورگر شما در همان موضوع عملیاتی کار سختی را انجام می دهد.
- برنامه دیگری در رایانه شما با استفاده از CPU از این طریق باعث کندتر برنامه شما می شود.
- تغییرات ناگهانی در میدان گرانشی زمین.
همچنین منطقی است که چندین بار در یک جلسه پروفایل همان مسیر کد را اجرا کنید. به این ترتیب شما تأثیر عوامل فوق را کاهش می دهید و قسمت های آهسته ممکن است با وضوح بیشتری از آن استفاده کنند.
اندازه گیری ، بهبود ، اندازه گیری
هنگامی که یک نقطه آهسته در برنامه خود را مشخص کردید ، سعی کنید به روش هایی برای بهبود رفتار اجرای فکر کنید. بعد از تغییر کد ، نمایه دوباره. اگر از نتیجه راضی هستید ، حرکت کنید ، اگر شاهد پیشرفت نیستید ، احتمالاً باید تغییر خود را به عقب برگردانید و آن را در "زیرا نمی تواند صدمه ببیند" بگذارید.
استراتژی های بهینه سازی
تعامل DOM را به حداقل برسانید
یک موضوع مشترک برای بهبود سرعت برنامه های مشتری وب ، به حداقل رساندن تعامل DOM است. در حالی که سرعت موتورهای جاوا اسکریپت با ترتیب بزرگی افزایش یافته است ، دسترسی به DOM با همان سرعت سریعتر نمی شود. این همچنین به دلایل بسیار عملی هرگز اتفاق نمی افتد (مواردی مانند چیدمان و ترسیم وسایل روی صفحه نمایش فقط به زمان می رسد).
گره های حافظه پنهان
هر زمان که یک گره یا لیستی از گره ها را از DOM بازیابی کنید ، سعی کنید در مورد اینکه آیا ممکن است در محاسبات بعدی (یا حتی فقط تکرار حلقه بعدی) از آنها استفاده مجدد کنید ، فکر کنید. تا زمانی که در واقع گره ها را در منطقه مربوطه اضافه یا حذف نکنید ، این مورد اغلب است.
قبل از:
function getElements() {
return $('.my-class');
}
بعد از:
var cachedElements;
function getElements() {
if (cachedElements) {
return cachedElements;
}
cachedElements = $('.my-class');
return cachedElements;
}
مقادیر ویژگی حافظه پنهان
به همان روشی که می توانید گره های DOM را ذخیره کنید ، می توانید مقادیر ویژگی ها را ذخیره کنید. تصور کنید که شما یک ویژگی از سبک یک گره را متحرک می کنید. اگر می دانید که شما (مانند آن قسمت از کد) تنها کسی هستید که تا به حال آن ویژگی را لمس می کنید ، می توانید آخرین مقدار را در هر تکرار ذخیره کنید تا نیازی به خواندن آن نباشید.
قبل از:
setInterval(function() {
var ele = $('#element');
var left = parseInt(ele.css('left'), 10);
ele.css('left', (left + 5) + 'px');
}, 1000 / 30);
بعد از: js var ele = $('#element'); var left = parseInt(ele.css('left'), 10); setInterval(function() { left += 5; ele.css('left', left + 'px'); }, 1000 / 30);
دستکاری دام را از حلقه ها منتقل کنید
حلقه ها اغلب برای بهینه سازی نقاط داغ هستند. سعی کنید به روش هایی برای تجزیه شماره واقعی خرد کردن برای کار با DOM فکر کنید. انجام یک محاسبه اغلب امکان پذیر است و پس از اتمام آن ، تمام نتایج را در یک حرکت اعمال کنید.
قبل از:
document.getElementById('target').innerHTML = '';
for(var i = 0; i < array.length; i++) {
var val = doSomething(array[i]);
document.getElementById('target').innerHTML += val;
}
بعد از:
var stringBuilder = [];
for(var i = 0; i < array.length; i++) {
var val = doSomething(array[i]);
stringBuilder.push(val);
}
document.getElementById('target').innerHTML = stringBuilder.join('');
redraws و reflows
همانطور که قبلاً در مورد دسترسی به DOM بحث شد ، نسبتاً کند است. وقتی کد شما در حال خواندن مقداری است که باید محاسبه شود بسیار کند می شود زیرا کد شما اخیراً چیزی را که در DOM مربوط است اصلاح کرده است. بنابراین ، باید از آن جلوگیری کرد تا خواندن و نوشتن دسترسی به DOM را در هم آمیخته شود. در حالت ایده آل ، کد شما همیشه باید در دو مرحله گروه بندی شود:
- مرحله 1: مقادیر DOM لازم برای کد خود را بخوانید
- فاز 2: DOM را اصلاح کنید
سعی کنید الگویی مانند:
- فاز 1: مقادیر DOM را بخوانید
- فاز 2: DOM را اصلاح کنید
- فاز 3: موارد دیگری را بخوانید
- فاز 4: DOM را در جایی دیگر اصلاح کنید.
قبل از:
function paintSlow() {
var left1 = $('#thing1').css('left');
$('#otherThing1').css('left', left);
var left2 = $('#thing2').css('left');
$('#otherThing2').css('left', left);
}
بعد از:
function paintFast() {
var left1 = $('#thing1').css('left');
var left2 = $('#thing2').css('left');
$('#otherThing1').css('left', left);
$('#otherThing2').css('left', left);
}
این توصیه باید برای اقداماتی که در یک زمینه اجرای JavaScript اتفاق می افتد در نظر گرفته شود. (به عنوان مثال در یک کنترل کننده رویداد ، در یک کنترل کننده فواصل یا هنگام رسیدگی به پاسخ AJAX.)
اجرای تابع paintSlow()
از بالا این تصویر را ایجاد می کند:

جابجایی به اجرای سریعتر این تصویر را بازده می دهد:

این تصاویر نشان می دهد که تغییر مسیر به نحوه دسترسی کد شما به DOM می تواند عملکرد رندر را تا حد زیادی افزایش دهد. در این حالت ، کد اصلی باید دو بار را محاسبه کرده و صفحه را دو بار چیدمان کند تا نتیجه مشابه ایجاد شود. بهینه سازی مشابه می تواند برای همه کد "دنیای واقعی" اعمال شود و نتایج بسیار چشمگیری را به همراه داشته باشد.
بیشتر بخوانید: Rendering: repaint ، Reflow/Relayout ، Restyle توسط Stoyan Stefanov
redraws و حلقه رویداد
اجرای JavaScript در مرورگر از یک مدل "حلقه رویداد" پیروی می کند. به طور پیش فرض مرورگر در حالت "بیکار" قرار دارد. این حالت را می توان با وقایع از تعامل کاربر یا مواردی مانند تایمرهای JavaScript یا تماس های برگشتی AJAX قطع کرد. هر زمان که یک قطعه جاوا اسکریپت در چنین نقطه وقفه ای اجرا شود ، مرورگر معمولاً منتظر می ماند تا آن را به پایان برساند تا صفحه را دوباره رنگ آمیزی کند (ممکن است استثنائاتی برای جاوا اسکریپت های بسیار طولانی یا در مواردی مانند جعبه های هشدار وجود داشته باشد که به طور مؤثر اجرای Javascript را قطع می کند).
عواقب
- If your JavaScript animation cycles take longer than 1/30 seconds to execute, you will not be able to create smooth animations because the browser will not repaint during the JS execution. When you expect to also handle user events you need to be much faster.
- Sometimes it comes in handy to delay some JavaScript actions until just a little bit later. Eg
setTimeout(function() { ... }, 0)
This effectively tells the browser to execute the callback as soon as the event loop is idle again (effectively some browsers will wait at least 10ms). You need to be aware that this will create two JavaScript execution cycles which are very close together in time. Both might trigger a repaint of the screen which might double the overall time spent with painting. Whether this actually triggers two paints depends on heuristics in the browser.
Regular version:
function paintFast() {
var height1 = $('#thing1').css('height');
var height2 = $('#thing2').css('height');
$('#otherThing1').css('height', '20px');
$('#otherThing2').css('height', '20px');
}

Lets add some delay:
function paintALittleLater() {
var height1 = $('#thing1').css('height');
var height2 = $('#thing2').css('height');
$('#otherThing1').css('height', '20px');
setTimeout(function() {
$('#otherThing2').css('height', '20px');
}, 10)
}

The delayed version shows that the browser paints twice although the two changes to the page are only 1/100 of a second a part.
Lazy Initialization
Users want web apps that load fast and feel responsive. However, users have different thresholds into what they perceive as slow depending on the action that they do. Eg an app should never do a lot of computation on a mouseover event because this might create a bad user experience while the user continues to move his mouse. However, users are used to accepting a little delay after they clicked on a button.
Thus it might make sense to move your initialization code to be executed as late as possible (eg when the user clicks a button that activates a particular component of your application).
Before: js var things = $('.ele > .other * div.className'); $('#button').click(function() { things.show() });
After: js $('#button').click(function() { $('.ele > .other * div.className').show() });
Event Delegation
Spreading event handlers across a page might take a relatively long time and can also be tedious once elements are dynamically replaced which then requires reattaching event handlers to the new elements.
The solution in this case is to use a technique called event delegation. Instead of attaching individual event handlers to elements, the bubbling nature of many browser events is used by actually attaching the event handler to a parent node and checking the target node of the event to see if the event is of interest.
In jQuery this can be easily expressed as:
$('#parentNode').delegate('.button', 'click', function() { ... });
When not to use event delegation
Sometimes the opposite can be true: You are using event delegation and you're having a performance problem. Basically event delegation allows constant-complexity initialization time. However, the price of checking if an event is of interest has to be paid for every invocation of that event. This might come in expensive, especially for events that occur frequently like "mouseover" or even "mousemove".
Typical Problems and Solutions
The stuff I do in $(document).ready
takes a long time
Malte's personal advice: Never do anything in $(document).ready
. Try to deliver your document in its final form. OK, you are allowed to register event listeners, but only using id-selector and/or using event delegation. For expensive events such as "mousemove", delay the registration until they are needed (mouseover event on the relevant element).
And if you really need to do stuff, such as making an Ajax request to get actual data, then show a nice animation; you might want to include the animation as a data URI if it is an animated GIF or the like.
Since I added a Flash movie to the page everything is really slow
Adding Flash to a page will always slow down rendering a little because the final layout of the window has to be "negotiated" between the browser and the Flash plugin. When you cannot completely avoid putting Flash on your pages, make sure you set the "wmode" Flash-parameter to the value "window" (which is the default). This will disable the ability to composite HTML and Flash elements (You won't be able to see an HTML element that lies above the Flash movie and your Flash movie cannot be transparent). This might be an inconvenience but it will dramatically improve your performance. For example check out the way that youtube.com carefully avoids placing layers above the main movie player.
I'm saving things to localStorage, now my application stutters
Writing to localStorage is a synchronous operations that involves spinning up your hard disk. You never want to do "long running" synchronous operations while doing animations. Move the access to localStorage to a spot in your code where you are sure that the user is idle and no animations are going on.
Profiling points to a jQuery selector being really slow
First you want to make sure that your selector can be run through document.querySelectorAll . You can test that in the JavaScript console. If there is an exception rewrite your selector to not use any special extension of your JavaScript framework. This will speed up your selector in modern browsers by an order of magnitude.
If this doesn't help or if you also want to be fast in modern browsers, follow these guidelines:
- Be as specific on the right side of your selector as possible.
- Use a tag name that you don't use often as the rightmost selector part.
- If nothing helps, think about rewriting things so you can use an id-selector
All these DOM manipulations take a long time
A bunch of DOM node inserts, removes and updates can be really slow. This can generally be optimized by generating a large string of html and the using domNode.innerHTML = newHTML
to replace the old content. Note that this might be really bad for maintainability and might create memory links in IE so be careful.
Another common problem is that your initialization code might create a lot of HTML. Eg a jQuery plugin that transforms a select box into a bunch of divs because that is what the design people wanted in ignorance of UX best practices. If you really want your page to be fast, never do that. Instead deliver all the markup from the server side in its final form. This again has many problems so think hard whether the speed is worth the tradeoff.
ابزار
- JSPerf - Benchmark little snippets of JavaScript
- Firebug - For profiling in Firefox
- Google Chrome Developer Tools (Available as WebInspector in Safari)
- DOM Monster - For optimizing DOM performance
- DynaTrace Ajax Edition - For profiling and paint optimizations in Internet Explorer
در ادامه مطلب
،مقدمه
HTML5 gives us great tools to enhance the visual appearance of web applications. This is especially true in the realm of animations. However, with this new power also come new challenges. Actually these challenges aren't really that new and it might sometimes make sense to ask your friendly desk neighbor, the Flash programmer, how she has overcome similar things in the past.
Anyway, when you work in animation it becomes hugely important that users perceive these animations to be smooth. What we need to realize is that smoothness in animations can't really be created by simply increasing the frames per second beyond any cognitive threshold. Our brain is, unfortunately, smarter than that. What you will learn is that true 30 frames of animation per second (fps) is much better than 60 fps with just a few frames dropped in the middle. People hate jaggedness.
This article will try to give you the tools and techniques to work on improving the experience of your own application.
استراتژی
By no means do we want to discourage you from building awesome, stunningly visual apps with HTML5.
Then when you notice that performance could be a little better, come back here and read up on how you can improve the elements of your application. It can, of course, help to do some things right in the first place but never let that get in your way of being productive.
Visual fidelity++ with HTML5
شتاب سخت افزاری
Hardware acceleration is an important milestone for overall render performance in the browser. The general scheme is to offload tasks that would otherwise be calculated by the main CPU to the graphics processing unit (GPU) in your computer's graphics adapter. This can yield massive performance gains and can also reduce resource consumption on mobile devices.
These aspects of your document can be accelerated by the GPU
- General layout compositing
- CSS3 transitions
- CSS3 3D transforms
- طراحی بوم
- WebGL 3D Drawing
While acceleration of canvas and WebGL are special purpose features that might not apply to your specific application the first three aspects can help pretty much every app to become faster.
What can be accelerated?
GPU acceleration works by offloading well-defined and specific tasks to special purpose hardware. The general scheme is that your document is broken down into multiple "layers" which are invariant to the aspects of your page that are accelerated. These layers are rendered using the traditional render pipeline. The GPU is then used to composite the layers onto a single page applying the "effects" that can be accelerated on the fly. A possible outcome is that an object that is animated on the screen does not require a single "relayout" of the page while the animation happens.
What you need to take away from that is that you need to make it easy for the rendering engine to identify when it can apply it's GPU acceleration magic. به مثال زیر توجه کنید:
While this works, the browser doesn't really know that you are performing something that is supposed to be perceived as smooth animation by a human being. Consider what happens when you achieve the same visual appearance using CSS3 transitions instead:
How the browser implements this animation is completely hidden from the developer. This in turn means that the browser is able to apply tricks such as GPU acceleration to achieve the defined goal.
There are two useful command-line flags for Chrome to help debugging GPU acceleration:
-
--show-composited-layer-borders
shows a red border around elements that are being manipulated at the GPU level. Good for confirming your manipulations occur within the GPU layer. -
--show-paint-rects
all non-GPU changes are painted and this throws a light border around all areas that are repainted. You can see the browser optimizing paint areas in action.
Safari has similar runtime flags described here .
انتقال CSS3
CSS Transitions make style animation trivial for everyone, but they also are a smart performance feature. Because a CSS transition is managed by the browser, the fidelity of its animation can be greatly improved, and in many cases hardware accelerated. Currently WebKit (Chrome, Safari, iOS) have hardware accelerated CSS transforms, but it's coming quickly to other browsers and platforms.
You can use transitionEnd
events in order to script this into powerful combinations, though right now, capturing all supported transition end events means watching webkitTransitionEnd transitionend oTransitionEnd
.
Many libraries have now introduced animation APIs that leverage transitions if present and fall back to standard DOM style animation otherwise. scripty2 , YUI transition , jQuery animate enhanced .
CSS3 Translate
I'm sure you've found yourself animating the x/y position of an element across the page before. You probably manipulated the inline style's left and top properties. With 2D transforms, we can use the translate()
functionality to replicate this behavior.
We can combo this with DOM animation to use the best thing possible
<div style="position:relative; height:120px;" class="hwaccel">
<div style="padding:5px; width:100px; height:100px; background:papayaWhip;
position:absolute;" id="box">
</div>
</div>
<script>
document.querySelector('#box').addEventListener('click', moveIt, false);
function moveIt(evt) {
var elem = evt.target;
if (Modernizr.csstransforms && Modernizr.csstransitions) {
// vendor prefixes omitted here for brevity
elem.style.transition = 'all 3s ease-out';
elem.style.transform = 'translateX(600px)';
} else {
// if an older browser, fall back to jQuery animate
jQuery(elem).animate({ 'left': '600px'}, 3000);
}
}
</script>
We use Modernizr to feature test for CSS 2D Transforms and CSS Transitions, if so we're going to use translate to shift the position. If this is animated using a transition there is a good chance the browser can hardware accelerate it. To give the browser another push in the right direction we'll use the "magic CSS bullet" from above.
If our browser is less capable, we'll fallback to jQuery to move our element. You can pick up the jQuery Transform polyfill plugin by Louis-Remi Babe to make this whole thing automatic.
window.requestAnimationFrame
requestAnimationFrame
was introduced by Mozilla and iterated on by WebKit with the goal of providing you a native API for running animations, whether they be DOM/CSS-based or on <canvas>
or WebGL. The browser can optimize concurrent animations together into a single reflow and repaint cycle, leading to higher fidelity animation. For example, JS-based animations synchronized with CSS transitions or SVG SMIL. Plus, if you're running the animation loop in a tab that's not visible, the browser won't keep it running , which means less CPU, GPU, and memory usage, leading to much longer battery life.
For more details on how and why to use requestAnimationFrame
, view Paul Irish's article requestAnimationFrame for smart animating .
پروفایل کردن
When you discover that the speed of your application can be improved, it is time to dig into profiling to find out where optimizations could yield the greatest benefit. Optimizations will often have negative impact on the maintainability of your source code and should thus only be applied if necessary. Profiling tells you which parts of your code would yield the greatest benefits when their performance would be improved.
JavaScript Profiling
JavaScript profilers give you an overview on the performance of your application on the JavaScript function level by measuring the time it takes to execute each individual function from its starts to its end.
The gross execution time of a function is the overall time it takes to execute it from top to bottom. The net execution time is the gross execution time minus the time it took to execute functions called from the function.
Some functions get called more often than others. Profilers usually give you the time it took for all invocations to run as well as the average and minimum and maximum execution time.
For more details, check out the Chrome Dev Tools docs on profiling .
The DOM
The performance of JavaScript has a strong influence in to how fluid and responsive your application will feel. It is important to understand that, while JavaScript profilers measure the execution time of your JavaScript, they also indirectly measure the time spent doing DOM operations. These DOM operations are often at the heart of your performance issues.
function drawArray(array) {
for(var i = 0; i < array.length; i++) {
document.getElementById('test').innerHTML += array[i]; // No good :(
}
}
Eg in the code above almost no time is spent executing actual JavaScript. It is still very likely that the drawArray-function will show up in your profiles because it is interacting with the DOM in a very wasteful fashion.
نکات و ترفندها
توابع ناشناس
Anonymous functions are not easy to profile because they inherently don't have a name under which they could show up in the profiler. There are two ways to work around this:
$('.stuff').each(function() { ... });
rewrite to:
$('.stuff').each(function workOnStuff() { ... });
It is not commonly known that JavaScript supports naming function expressions. Doing this will make them show up perfectly in the profiler. There is one problem with this solution: The named expression actually puts the function name into the current lexical scope. This might clobber other symbols, so be careful.
Profiling long functions
Imagine you have a long function and you suspect that a small part of it might be the reason for your performance problems. There are two ways to find out which part is the problem:
- The correct method: Refactor your code to not include any long functions.
- The evil getting-things-done method: add statements in the form of named self calling functions to your code. If you are a little careful this does not change the semantics and it makes parts of your function show up as individual functions in the profiler:
js function myLongFunction() { ... (function doAPartOfTheWork() { ... })(); ... }
Don't forget to remove these extra functions after profiling is done; or even use them as a starting point to refactor your code.
DOM Profiling
The latest Chrome Web Inspector development tools contain the new "Timeline View" which shows a timeline of the low level actions performed by the browser. You can use this information to optimize your DOM operations. You should aim to reduce the number of "actions" the browser has to perform while your code executes.
The timeline view can create an immense amount of information. You should thus try to create minimal test cases that you can execute independently.

The image above shows the output of the timeline view for a very simple script. The left pane shows the operations performed by the browser in chronical order, while the timeline in the right pane shows the actual time consumed by an individual operation.
More info on the timeline view. An alternative tool for profiling in Internet Explorer is DynaTrace Ajax Edition .
Profiling Strategies
Single out aspects
When you want to profile your application, try to single out the aspects of its functionality that might trigger slowness as close as possible. Then try to do a profile run that only executes parts of your code that are relevant to these aspects of your application. This will make the profiling data easier to interpret because it is not intermixed with code paths that are not related to your actual problem. Good examples for individual aspects of your application might be:
- Start up time (activate the profiler, reload application, wait until initialization is complete, stop the profiler.
- Click a button and subsequent animation (start profiler, click button, wait until animation is complete, stop profiler).
GUI Profiling
Executing only the right part of your application can be harder in a GUI program than when you optimize, say, the ray tracer of your 3D engine. When you, for example, want to profile the stuff that happens when you click a button, you might trigger unrelated mouseover events along the way that make your results less conclusive. Try to avoid that :)
Programatic Interface
There is also a programatic interface to activate the debugger. This allows precise control over when profiling starts and when it ends.
Start a profiling with:
console.profile()
Stop profiling with:
console.profileEnd()
تکرارپذیری
When you do profiling make sure you can actually reproduce your results. Only then will you be able to tell whether your optimizations did actually improve things. Also function level profiling is done in the context of your whole computer. علم دقیقی نیست. Individual profile runs might be influenced by many other things happening on your computer:
- An unrelated timer in your own application that fires while you measure something else.
- The garbage collector doing its work.
- Another tab in your browser doing hard work in the same operating thread.
- Another program on your computer using up the CPU thus making your application slower.
- Sudden changes in the gravitational field of the earth.
It also makes sense to execute the same code path multiple times in one profiling session. This way you decrease the influence of above factors and the slow parts may stand out even more clearly.
Measure, improve, measure
When you identified a slow spot in your program, try to think of ways to improve the execution behavior. After you changed your code, profile again. If you are satisfied with the result, move on, if you are not seeing an improvement you should probably roll back your change and not leave it in "because it can't hurt".
استراتژی های بهینه سازی
Minimize DOM interaction
A common theme for improving the speed of web client applications is to minimize DOM interaction. While the speed of JavaScript engines has increased by an order of magnitude, accessing the DOM has not gotten faster at the same rate. This is also for very practical reasons never going to happen (things like layouting and drawing stuff on a screen just take time).
Cache DOM Nodes
Whenever you retrieve a node or a list of nodes from the DOM, try to think about whether you might be able to reuse them in a later computation (or even just the next loop iteration). As long as you don't actually add or delete nodes in the relevant area, this is often the case.
قبل از:
function getElements() {
return $('.my-class');
}
بعد از:
var cachedElements;
function getElements() {
if (cachedElements) {
return cachedElements;
}
cachedElements = $('.my-class');
return cachedElements;
}
Cache Attribute Values
The same way you can cache DOM nodes you can also cache the values of attributes. Imagine you are animating an attribute of a node's style. If you know that you (as in that part of the code) are the only one that will ever touch that attribute you can cache the last value on every iteration so that you will not have to read it repeatedly.
قبل از:
setInterval(function() {
var ele = $('#element');
var left = parseInt(ele.css('left'), 10);
ele.css('left', (left + 5) + 'px');
}, 1000 / 30);
After: js var ele = $('#element'); var left = parseInt(ele.css('left'), 10); setInterval(function() { left += 5; ele.css('left', left + 'px'); }, 1000 / 30);
Move DOM Manipulation Out of Loops
Loops are often hot points for optimization. Try to think of ways to decouple actual number crunching to working with the DOM. It is often possible to do a calculation and then, after it is done, apply all the results in one go.
قبل از:
document.getElementById('target').innerHTML = '';
for(var i = 0; i < array.length; i++) {
var val = doSomething(array[i]);
document.getElementById('target').innerHTML += val;
}
بعد از:
var stringBuilder = [];
for(var i = 0; i < array.length; i++) {
var val = doSomething(array[i]);
stringBuilder.push(val);
}
document.getElementById('target').innerHTML = stringBuilder.join('');
Redraws and Reflows
As discussed earlier accessing the DOM is relatively slow. It becomes very slow when your code is reading a value which has to be recalculated because your code recently modified something related in the DOM. Thus, it should be avoided to intermix reading and writing access to the DOM. Ideally your code should always be grouped in two phases:
- Phase 1: Read DOM values necessary for your code
- Phase 2: Modify the DOM
Try not to program a pattern such as:
- Phase 1: Read DOM values
- Phase 2: Modify the DOM
- Phase 3: Read some more
- Phase 4: Modify the DOM somewhere else.
قبل از:
function paintSlow() {
var left1 = $('#thing1').css('left');
$('#otherThing1').css('left', left);
var left2 = $('#thing2').css('left');
$('#otherThing2').css('left', left);
}
بعد از:
function paintFast() {
var left1 = $('#thing1').css('left');
var left2 = $('#thing2').css('left');
$('#otherThing1').css('left', left);
$('#otherThing2').css('left', left);
}
This advice should be considered for actions happening within one JavaScript execution context. (eg within an event handler, within an interval handler or when handling an ajax response.)
Executing the function paintSlow()
from above creates this image:

Switching to the faster implementation yields this image:

These images show that reordering the way your code accesses the DOM can greatly enhance render performance. In this case the original code has to recalculate styles and layout the page twice to create the same result. Similar optimization can be applied to basically all "real world" code and yield some really dramatic results.
Read more: Rendering: repaint, reflow/relayout, restyle by Stoyan Stefanov
Redraws and the Event Loop
JavaScript execution in the browser follows an "Event Loop" model. By default the browser is in an "idle" state. This state can be interrupted by events from user interactions or such things as JavaScript timers or Ajax callbacks. Whenever a piece of JavaScript runs at such an interruption point, the browser will usually wait for it to finish until it repaints the screen (There might be exceptions for extremely long running JavaScripts or in cases such as alert-boxes which effectively interrupt the JavaScript execution).
عواقب
- If your JavaScript animation cycles take longer than 1/30 seconds to execute, you will not be able to create smooth animations because the browser will not repaint during the JS execution. When you expect to also handle user events you need to be much faster.
- Sometimes it comes in handy to delay some JavaScript actions until just a little bit later. Eg
setTimeout(function() { ... }, 0)
This effectively tells the browser to execute the callback as soon as the event loop is idle again (effectively some browsers will wait at least 10ms). You need to be aware that this will create two JavaScript execution cycles which are very close together in time. Both might trigger a repaint of the screen which might double the overall time spent with painting. Whether this actually triggers two paints depends on heuristics in the browser.
Regular version:
function paintFast() {
var height1 = $('#thing1').css('height');
var height2 = $('#thing2').css('height');
$('#otherThing1').css('height', '20px');
$('#otherThing2').css('height', '20px');
}

Lets add some delay:
function paintALittleLater() {
var height1 = $('#thing1').css('height');
var height2 = $('#thing2').css('height');
$('#otherThing1').css('height', '20px');
setTimeout(function() {
$('#otherThing2').css('height', '20px');
}, 10)
}

The delayed version shows that the browser paints twice although the two changes to the page are only 1/100 of a second a part.
Lazy Initialization
Users want web apps that load fast and feel responsive. However, users have different thresholds into what they perceive as slow depending on the action that they do. Eg an app should never do a lot of computation on a mouseover event because this might create a bad user experience while the user continues to move his mouse. However, users are used to accepting a little delay after they clicked on a button.
Thus it might make sense to move your initialization code to be executed as late as possible (eg when the user clicks a button that activates a particular component of your application).
Before: js var things = $('.ele > .other * div.className'); $('#button').click(function() { things.show() });
After: js $('#button').click(function() { $('.ele > .other * div.className').show() });
Event Delegation
Spreading event handlers across a page might take a relatively long time and can also be tedious once elements are dynamically replaced which then requires reattaching event handlers to the new elements.
The solution in this case is to use a technique called event delegation. Instead of attaching individual event handlers to elements, the bubbling nature of many browser events is used by actually attaching the event handler to a parent node and checking the target node of the event to see if the event is of interest.
In jQuery this can be easily expressed as:
$('#parentNode').delegate('.button', 'click', function() { ... });
When not to use event delegation
Sometimes the opposite can be true: You are using event delegation and you're having a performance problem. Basically event delegation allows constant-complexity initialization time. However, the price of checking if an event is of interest has to be paid for every invocation of that event. This might come in expensive, especially for events that occur frequently like "mouseover" or even "mousemove".
Typical Problems and Solutions
The stuff I do in $(document).ready
takes a long time
Malte's personal advice: Never do anything in $(document).ready
. Try to deliver your document in its final form. OK, you are allowed to register event listeners, but only using id-selector and/or using event delegation. For expensive events such as "mousemove", delay the registration until they are needed (mouseover event on the relevant element).
And if you really need to do stuff, such as making an Ajax request to get actual data, then show a nice animation; you might want to include the animation as a data URI if it is an animated GIF or the like.
Since I added a Flash movie to the page everything is really slow
Adding Flash to a page will always slow down rendering a little because the final layout of the window has to be "negotiated" between the browser and the Flash plugin. When you cannot completely avoid putting Flash on your pages, make sure you set the "wmode" Flash-parameter to the value "window" (which is the default). This will disable the ability to composite HTML and Flash elements (You won't be able to see an HTML element that lies above the Flash movie and your Flash movie cannot be transparent). This might be an inconvenience but it will dramatically improve your performance. For example check out the way that youtube.com carefully avoids placing layers above the main movie player.
I'm saving things to localStorage, now my application stutters
Writing to localStorage is a synchronous operations that involves spinning up your hard disk. You never want to do "long running" synchronous operations while doing animations. Move the access to localStorage to a spot in your code where you are sure that the user is idle and no animations are going on.
Profiling points to a jQuery selector being really slow
First you want to make sure that your selector can be run through document.querySelectorAll . You can test that in the JavaScript console. If there is an exception rewrite your selector to not use any special extension of your JavaScript framework. This will speed up your selector in modern browsers by an order of magnitude.
If this doesn't help or if you also want to be fast in modern browsers, follow these guidelines:
- Be as specific on the right side of your selector as possible.
- Use a tag name that you don't use often as the rightmost selector part.
- If nothing helps, think about rewriting things so you can use an id-selector
All these DOM manipulations take a long time
A bunch of DOM node inserts, removes and updates can be really slow. This can generally be optimized by generating a large string of html and the using domNode.innerHTML = newHTML
to replace the old content. Note that this might be really bad for maintainability and might create memory links in IE so be careful.
Another common problem is that your initialization code might create a lot of HTML. Eg a jQuery plugin that transforms a select box into a bunch of divs because that is what the design people wanted in ignorance of UX best practices. If you really want your page to be fast, never do that. Instead deliver all the markup from the server side in its final form. This again has many problems so think hard whether the speed is worth the tradeoff.
ابزار
- JSPerf - Benchmark little snippets of JavaScript
- Firebug - For profiling in Firefox
- Google Chrome Developer Tools (Available as WebInspector in Safari)
- DOM Monster - For optimizing DOM performance
- DynaTrace Ajax Edition - For profiling and paint optimizations in Internet Explorer