بهینه‌سازی Rendering

بسم الله الرّحمن الرّحیم

کاربران امروزه‌ی وب انتظار دارند صفحه‌ای که در آن حضور دارند خیلی نرم و روان باشد و تعامل با آن سریع انجام شود. اینجاست که شما باید خیلی بیشتر روی این موضوع تمرکز و تلاش کنید. صفحات وب نه تنها باید سریع لود شوند، بلکه باید خوب هم کار کنند؛ اسکرول باید خیلی سریع باشد و انیمیشن‌ها و تعامل کاربران با صفحه نیز باید خیلی نرم و روان باشد.

برای اینکه بتوانیم سایت‌ها و وب‌اپلیکیشن‌های بهینه‌ای تولید کنیم لازم است بدانیم که مرورگر چگونه با HTML، CSS و Javascript دست و پنجه نرم می‌کند. بعد از آن می‌توانید مطمئن شوید کدی که نوشته‌اید (یا کدی که دیگران نوشته‌اند و شما از آن استفاده می‌کنید) تا حدّ امکان خوب و بهینه نوشته شده است.

نرخ تازه‌سازی (Device Refresh Rate) و ۶۰ فریم بر ثانیه

بیشتر دستگاه‌ها (یعنی گوشی‌ها، تبلت‌ها، کامپیوترهای شخصی و…) صفحه‌ی نمایش خود را ۶۰ بار در هر ثانیه تازه‌سازی می‌کنند. حالا اگر transition یا انیمیشنی در حال اجرا باشد یا اینکه صفحه اسکرول شود مرورگر باید به ازای هر بار تازه‌سازی صفحه‌ی نمایش، یک عکس یا اصطلاحاً یک فریم جدید را بسازد تا آن را به صفحه‌ی نمایش بدهد.

هر فریم فقط ۱۶ هزارم ثانیه مهلت دارد تا ساخته شود (یک فریم = یک‌شصتم ثانیه = ۱۶ میلی‌ثانیه). امّا در دنیای واقعی مرورگر هم برای خودش یک سری کارهایی را در پشت پرده انجام می‌دهد، بنابراین کدها و کارهای شما باید فقط در ۱۰ میلی‌ثانیه انجام شوند. اگر به هر دلیلی این کارها بیش از ۱۰ میلی‌ثانیه طول بکشد فریم ساخته نمی‌شود و صفحه‌ی نمایش داغون می‌شود! و روی تجربه‌ی کاربری (UX) تأثیر منفی می‌گذارد. به این اتّفاق اصطلاحاً jank گفته می‌شود.

فرآیند تولید یک فریم توسّط مرورگر (Pixel Pipeline)

برای اینکه مرورگر بتواند یک فریم را بسازد ۵ مرحله‌ی اصلی طی می‌شود که به مجموعه‌ی این مراحل با هم Pixel Pipeline می‌گوییم. شما باید با این مراحل آشنا شوید و بدانید که چگونه کار می‌کنند. تمام این مراحل پشت سر هم نباید بیشتر از مدّت‌زمان یک فریم (۱۶ میلی‌ثانیه) طول بکشند، در غیر این صورت jank رخ می‌دهد.
pixel pipeline

  1. JavaScript: معمولاً یک کد جاوااسکریپت اجرا می‌شود و بعد در نتیجه‌ی آن، تغییر ظاهری در صفحه رخ می‌دهد. این کار می‌تواند استفاده از تابع animate کتابخانه‌ی jQuery باشد، یا تغییر attributeهای یک عنصر، یا تغییر و اضافه کردن مقداری کد HTML به صفحه. البته همیشه فقط جاوااسکریپت نیست که سبب تغییر ظاهری می‌شود، بلکه transitionها و انیمیشن‌های CSS و همچنین Web Animation API هم می‌تواند باعث این تغییر شود.
  2. Style Calculation یا محاسبه‌ی استایل: فرآیندی است که مرورگر در آن می‌فهمد بر اساس انتخابگرهای CSS باید چه ویژگی‌هایی به عناصر HTML داده شود، مثلاً انتخابگر .headline باید اعمال شود یا .nav > .nav__item همین که این موضوع مشخّص بشود، ویژگی‌های مورد نظر به عنصر داده می‌شوند و استایل نهایی هر عنصر محاسبه می‌شود.
  3. Layout یا صفحه‌آرایی: وقتی که مرورگر فهمید باید چه ویژگی‌هایی را به عنصرها بدهد شروع می‌کند به محاسبه‌ی جایی که اشغال می‌کنند و این‌که دقیقاً در کجای صفحه‌ی نمایش قرار می‌گیرند. در صفحه‌آرایی وب یک عنصر روی عنصرهای دیگر تأثیر می‌گذارد، مثلاً ویژگی width عنصر <body> معمولاً روی عرض فرزندان آن و کلّ عناصر دیگر تأثیر می‌گذارد. پس این فرآیند ممکن است برای مرورگر نسبتاً پیچیده باشد.
  4. Paint یا ترسیم: فرآیندی است که در آن پیکسل‌ها پر می‌شوند؛ که شامل رسم شدن متن‌ها، رنگ‌ها، عکس‌ها، حاشیه‌ها، و سایه‌ها، و اساساً هر قسمتی از ظاهر و قیافه‌ی عنصر می‌شود. این ترسیم‌ها روی چند سطح مختلف رسم می‌شوند که به آن‌ها لایه گفته می‌شود.
  5. Compositing یا ترکیب: از آن جایی که قسمت‌های مختلف صفحه در لایه‌های مختلفی رسم شده‌اند باید با ترتیب درست روی صفحه‌ی نمایش قرار بگیرند تا صفحه درست دیده شود. این موضوع مخصوصاً برای عناصری که روی هم افتاده‌اند اهمّیّت دارد، زیرا یک اشتباه در این قسمت می‌تواند منجر به این شود که یک عنصر اشتباهاً روی یک عنصر دیگر دیده شود.

هر کدام از این مراحل می‌تواند منجر به jank شود. به همین دلیل خیلی مهم است بفهمید کدی که می‌نویسید باعث می‌شود چه اتّفاقاتی در این مراحل بیفتد.

گاهی اوقات ممکن است کلمه‌ی rasterize را در کنار paint بشنوید. دلیل آن این است که مرحله‌ی paint دو وظیفه دارد: اوّل ایجاد لیستی از draw callها و دوم پر کردن پیکسل‌ها. وظیفه‌ی دوم rasterize نامیده می‌شود. پس وقتی paintها را در DevTools ضبط می‌کنید آن را شامل rasterize در نظر بگیرید. (در برخی معماری‌ها ایجاد لیست draw call ها و rasterize در دو thread جداگانه انجام می‌شوند، ولی به هر حال این چیزی نیست که تحت کنترل شما باشد.)

همیشه لازم نیست تک‌تک این مراحل را برای هر فریم بررسی کنید. معمولاً هر وقت تغییر ظاهری در صفحه به وجود می‌آید سه حالت برای کلّ این مراحل به وجود می‌آید. و فرقی نمی‌کند که این تغییر به خاطر جاوااسکریپت باشد، یا انیمیشن‌های CSS یا Web Animations:

حالت اوّل (JS / CSS > Style > Layout > Paint > Composite)
pixel pipeline mode 1
اگر یک ویژگی مربوط به صفحه‌آرایی (layout) را تغییر دهید، یعنی ویژگی‌هایی که روی مکان و اندازه‌ی هندسی عنصر تأثیر می‌گذارند، مثل width، height یا تغییر مکان با ویژگی‌های left یا top، در این صورت مرورگر مجبور می‌شود وضعیّت بقیّه‌ی عنصرها را نیز بررسی کند و کلّ صفحه را دوباره صفحه‌آرایی کند. هر قسمتی از صفحه که از این لحاظ تغییر پیدا کرده باشد باید دوباره paint شود، و عناصری که در انتها paint شده‌اند باید با هم ترکیب شوند (یعنی مرحله‌ی compositing انجام شود).

حالت دوم (JS / CSS > Style > Paint > Composite)
pixel pipeline mode 2
اگر یک ویژگی را تغییر دهید که فقط مرتبط با paint باشد (paint only)، مثل رنگ پس‌زمینه، رنگ متن، یا سایه‌ها و به طور کلّی هر ویژگی‌ای که تأثیری در چیدمان صفحه و مکان و اندازه‌ی هندسی عنصر نداشته باشد، مرورگر مرحله‌ی layout را اصلاً انجام نمی‌دهد، ولی هنوز paint را انجام می‌دهد.

حالت سوم (JS / CSS > Style > Composite)
pixel pipeline mode 3
اگر یک ویژگی را تغییر دهید که نه باعث تغییر چیدمان صفحه شود و نه باعث انجام paint شود حالت سوم پیش می‌آید.

حالت سوم سبک‌ترین و بهترین حالت برای وقت‌هاییست که فشار پردازش سایت ما زیاد است (مثل انیمیشن‌ها یا اسکرول).

نکته: اگر می‌خواهید بدانید کدام یک از ویژگی‌های css باعث فعّال‌شدن کدام مرحله‌ها (layout و paint) می‌شوند به سایت CSS Triggers یک سر بزنید. و اگر می‌خواهید خیلی سریع بفهمید چه طور می‌توان انیمیشن‌هایی بسیار بهینه و نرم و روان ساخت به «بهینه‌سازی مرحله‌ی composite» مراجعه کنید.

بهینه‌سازی یک هنر است که در آن جلوی کارهای اضافی توسّط مرورگر گرفته می‌شود و طوری عمل می‌کنید که کارهایی که انجام می‌دهید تا جایی که امکان دارد خوب و سریع انجام شود. در بیشتر اوقات بهینه‌سازی، درست کار کردن با مرورگر است، نه انجام کاری بر علیه مرورگر. خوب است بدانید مراحلی که در بالا گفته شد از نظر هزینه‌ی محاسباتی و زمانی یکسان نیستند، و بعضی مراحل سنگین‌تر از مرحله‌های دیگر هستند.

در پست‌های بعدی تک‌تک مرحله‌های فوق را مورد بررسی قرار می‌دهیم و در مورد اینکه چه طور می‌توان کاری کرد که هر کدام از این مراحل در بهینه‌ترین حالت ممکن انجام شوند بحث می‌کنیم.

قسمت اوّل: بهینه‌سازی مرحله‌ی JavaScript

قسمت دوم: بهینه‌سازی مرحله‌ی محاسبه‌ی استایل (Style calculation)

قسمت سوم: بهینه‌سازی مرحله‌ی صفحه‌آرایی (Layout)

قسمت چهارم: بهینه‌سازی مرحله‌ی ترسیم (Paint)

قسمت پنجم: بهینه‌سازی مرحله‌ی ترکیب (Composite)

پاسخی بگذارید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *