Reflow ، Repaint و بهبود پرفورمنس

چند وقت پیش توی یکی از پروژه ها به یک موردی برخوردم برای بهبود پرفورمنس که بدنبال سرچ هام برای برطرف کردنش به یک مفهومی برخوردم بنام reflow و batch کردن reflow ها. در نهایت طبق یک سری تحقیقاتی که تو این زمینه داشتم، این موضوع reflow و repaint بهم کمک کرد که دید خوبی پیدا کنم در مورد اینکه که چرا virtual Dom در ریکت سرعت بالایی داره، چطور تا حدی به نرخ تبدیل و تجربه کاربری بهتری برسیم و همچنین هنگام مدیریت دام دید عمیق تری نسبت به اتفاقات پشت پرده داشته باشیم و همچنین در این روند بتونیم تاثیر بیشتری بذاریم.مرورگر و نمایش المان هااول از همه بریم سراغ پایه ماجرا و درباره خود مرورگر و مراحل نمایش المان های یک صفحه در دستگاه کاربر رو یه بررسی ریز باهم بکنیم.وقتی که توی مرورگر خودتون یک صفحه رو در url وارد می کنید و کلید enter رو فشار میدین یک درخواست HTTP به اون صفحه و سرور متناظر ارسال میشه که یک داکیومنت HTML در جواب برمیگرده.حالا میرسیم به موضوع عکس بالا و باهم بصورت کلی مرحله به مرحله بررسیش کنیم.ابتدا render engine مرورگر، کد های HTML رو Parse میکنه و یک DOM tree میسازه. DOM یک ساختار درختی هستش که هر تگ HTML یک node متناظر داخل خودش داره، همینطور همه text ها هم یک text node متناظر دارند.یک نمونه از ساختار یک دام سادهنکته: node پایه و ریشه ای توی این ساختار documentElement (تگ HTML) هستش.همچنین مرورگر کدهای CSS رو parse و معنی میکنه. ابتدا User-Agent stylesheet (کدهای پیش فرض css) و سپس کد های css کاربر.حالا به قسمت جذاب ماجرا میرسیم یعنی ایجاد ساختار render tree، کلیت این ساختار شبیه DOM tree هستش اما تفاوت هایی باهم دارند که اصلی ترین اونها اینه که این ساختار علاوه بر تگ ها درباره style ها و css ها هم اطلاعات داره. بطور مثال اگر شما یک div داشته باشی که display اون none باشه اون div در render tree نمایش داده نخواهد شد.هر node توی این ساختار اصطلاحا یک box نامیده میشه که هر کدوم از این node ها یک CSS box properties که شامل اطلاعات Css اون node هست رو شامل میشه.نمونه یک render tree بعد از ساخت مرحله render tree مرورگر آماده میشه برای کشیدن و render این ساختار بر روی صفحه کاربر.قطعا هر چقدر ما بیشتر عمیق بشیم داخل هر مرحله موارد بسیار بیشتری هست که بخوایم دربارش صحبت کنیم ولی هدف این مقاله اینه که بصورت کلی و تا حدی که بصورت عملی، کمک کننده باشه صحبت کنیم.Reflow و Repaintمعمولا توی دنیای مرورگر ها هرکجا بحث تاثیرات ظاهری و اعمال اونها میشه دو مفهوم حرف اول رو میزنه، reflow و repaint.توی این پست میخوایم ببینیم این دوتا چی هستن و فهمیدنشون به چه کاری میاد؟بعد از ساخت render tree و ایجاد طرح بندی (نحوه چیدمان کلی المان ها و موقعیت و جایگاه اونها نسبت به بهم) و اعمال تغییرات ظاهری برای المان های داخل صفحه، هر تغییری در اطلاعات ورودی، که برای ساخت render tree استفاده شده بود، منجر به اعمال یکی از دو مورد زیر می شه.Repaint:این مورد مربوط به تغییراتی میشه که مربوط به تغییرات ظاهری المان ها میشه نه طرح بندی و چیدمان اونها.بطور مثال تغییراتی مثل تغییر در opacity، background-color, visibility و outline.این تغییرات برای مرورگر تغییرات سنگینی حساب میشن چون مرورگر مجبور میشه یکبار دیگه نمایش همه node های داخل dom رو چک بکنه ( بدلیل اینکه ممکن است یک یا چند node تحت تاثیر این نوع تغییرات قابل نمایش بشن)Reflow:این تغییرات نسبت به تغییرات قبلی تاثیرات بیشتری میذاره، چون مربوط میشه به دوباره محاسبه کردن جایگاه و ابعاد هندسی همه المان های document، که منجر میشه به دوباره رندر شدن قسمتی و یا حتی همه document چون تغییر هر یک از المان ها میتونه باعث تغییر هر کدوم از المان های والد، فرزند و یا المان های همسطح دیگه بشه.هر دو این تغییرات منجر به browser-blocking میشه ( یعنی کاربر و کدهای شما قادر به انجام عمل یا وظیفه ای در حین این تغییرات نیستند) در موارد تغییرات خیلی سنگین، یک تغییر css میتونه باعث کندی در اجرا کد جاوا اسکریپت شما بشه. یکی از دلایل مواردی مثل اسکرول نامنظم و کند در برخی سایت ها همین مورد میتونه باشه.مواردی که منجر به reflow میشنتا اینجا کار فهمیدیم که reflow که تغییرات سنگین هستند و باعث محاسبه ابعاد هندسی همه المان های document میشه.خب پس الآن میریم سراغ اینکه بفهمیم چه مواردی باعث reflow میشن که بتونیم از ایجاد موارد غیر ضروری جلوگیری کنیم یا اینکه تغییرات موارد ضروری رو هم تا حد ممکن به حداقل برسونیم.افزودن، حذف کردن یا تغییر قابلیت مشاهده بودن المان ها:این اولین و واضح ترین تغییریه که وقتی با جاوا اسکریپت دام رو تغییر میدیم reflow اتفاق میفتهافزودن یا حذف استایل های css:تغییر در استایل یا تغییر در کلاس المان ها که منجر به تغییر ابعاد المان بشه روی باقی المان ها در یک دام یکسان و المان های اطراف خودش تاثیر میذارهانیمیشن های css3 و transition ها:هر فریم انیمیشن باعث ایجاد ریفلو میشه (خیلی مهمه این موضوع که کاربرها و مخاطب هدفتون بشناسید که چه دستگاه ها با چه توانایی هایی بطور معمول استفاده می کنند و شما چه مقدار دارید انیمیشن استفاده می کنید)محاسبه offsetWidth و offsetHeight:بطور عجیبی محاسبه این دو مورد از یک المان باعث محاسبه reflow میشه.یک ترفندی که با استفاده از این مورد میشه ازش استفاده کرد و خودم به شخصه ازش استفاده میکنم (مخصوصا وقتی که از فریم ورک های فرانت استفاده نمی کنم) اینه که میتونید یک سری تغییرات دام رو خودتون برای reflow به صورت دسته ای اعمال کنید؛مثلا وقتی یک المان رو توصفحه یا داخل یک المان دیگه اضافه می کنید برای اینکه مجبور کنید مرورگر رو بعد از انجام این عملیات و قبل از انجام عملیات بعدی، برای مدیریت دام reflow این مواردی که اضافه کرده اید رو بصورت دسته ای تا اینجای کار محاسبه کنه استفاده از یکی از همین دو مورد offsetWidth یا offsetHeight یک المانه و بعد از اون عملیات های دیگه رو ادامه بدیدتعاملات کاربر:مورد آخر هم تعاملات کاربر هستش مثل هاور کردن روی یک المان یا حتی افزودن متن یا تکست درون یک فیلد، هر نوع تغییر در فونت و تغییر اندازه windowمیزان تاثیراتی که هر کدوم از این موارد روی reflow میذاره متفاوته و به عوامل مختلفی بستگی دارن مثلا برخی از مرورگرها نسبت به باقی مرورگر ها توی برخی از عملیات بهتر عمل می کنن یا یک سری از المان ها نسبت به بقیه رندر سنگین تری دارن. اما بطور کل یکسری نکته هست که شما میتونید برای بهبود عملکرد ها در این زمینه استفاده کنید.چطور تغییرات reflow رو به حداقل برسونیمالآن که فهمیدیم چه مواردی باعث reflow میشن پس وقتشه که ببینیم چطوری میتونیم بهبودش بدیم. بخصوص زمان هایی که ابزار هایی برای دسته بندی کردن تغییرات هنگام رندر کردن استفاده نمی کنید یا اینکه ابزار یا فریم ورک مد نظر شما الگوریتم خاصی و مناسبی برای این موارد مورد بحث ما استفاده نمیکنه.عدم استفاده از css های inline : بد نیست بدونیم که یکی از اصلی ترین معایب استفاده از این روش اینه که منجر به reflow اضافی میشه.کاهش قواعد و قوانین cssهرچقدر قواعد سی اس اس کمتری داشته باشیم تا بخوایم به المان مدنظر برسیم، تغییرات سریعتر هم اعمال میشن چون مراحل کمتری باید طی بشه تا سلکتور انتخاب بشه.پس برای پرفورمنس بهتر، اول تا اونجا که میشه کد های پیچیده و ناخواسته سی اس اس رو باید به حداقل رسوند.کم کردن عمق دامتغییر در یک لول از دام میتونه منجر به تغییر در هر لول دام بشه. هرچقدر تعداد المان ها در یک برنچ درخت دام کمتر باشه ریفلو سریع تر اتفاق میفته. نکته ای که می تونه برای این موضوع کمک کنه اینه که المان های wrapper غیر ضروری رو تا اونجا که میشه باید کم کنیم.تغییر کلاس ها بر روی المان های مناسب ترتا اونجا که میشه تغییر کلاس ها رو روی المان هایی انجام بدیم که عمق کمتری توی فرزندان خودشون دارن. این موضوع باعث میشه در محدوده کوچک تری ریفلو انجام بشه و در نتیجه سرعت ریفلو ما سریع تر رخ میدهحذف انیمیشن های سنگین از جریان های دامهمونطور که گفتیم هر فریم یک انیمیشن منجر به ریفلو میشه. پس بهتره تا جایی که ممکنه المان هایی که انیمیشن های سنگین داره رو از جریان درخت دام با position های absolute و fixed جدا کنیم که محدوده اعمال ریفلو فقط مخصوص به خودشون بشه.تغییرات برای المان های مخفیالمان هایی که display اونها none هستش تغییراتی که در اونها اعمال میشه منجر به reflow و repaint نمیشه. درصورت امکان میشه تغییرات اونها رو قبل از اینکه به نمایش در بیان اعمال کنیم.دسته ای کردن تغییرات ظاهریبهتره بجای اینکه تغییرات مربوط به ظاهر رو در چند عملیات جداگانه انجام بدیم به صورت یکجا اعمال کنیم، مثلا برای تغییرات ظاهر یک المان بجای چند تغییر استایل و ابعاد و در چند خط جدا، با یک تغییر کلاس اونا رو اعمال کنیم. یا بجای اینکه در حلقه ها و در هر حلقه یک المان به صفحه اضافه کنیم، با ذخیره المان ها در یک متغیر یا آرایه بصورت یکجا بعد از اتمام حلقه این تغییرات رو اعمال کنیم.ریکت چطور این موضوع را مدیریت میکنهالان که فهمیدیم که reflow چی هست خیلی راحت تر میشه درک کنیم این موضوع رو که پایه استراتژی ریکت، کاهش و دسته ای کردن تغییرات reflow برای virtual DOM هست. یعنی ریکت کار جدیدی انجام نمیده فقط یک حرکت استراتژیک میزنه.به این صورت که ریکت یک کپی از دام واقعی در مموری ذخیره داره، وقتی ما یک تغییر یا یک اصلاحی رو انجام میدیم، این تغییرات ابتدا در DOM داخل مموری اعمال میشه. بعد از اون ریکت با استفاده از یک الگوریتم بنام diffing algorithm میفهمه که دقیقا چه چیز تغییر کرده. با فهمیدن این موضوع ریکت تغییرات رو بصورت دسته ای شده (batching) و بهینه بر روی DOM واقعی انجام میده.پس به اینصورت ریکت تغییرات reflow و repaint انجام شده رو به حداقل ممکن کاهش میده.سخن پایانیخب تبریک میگم بهتون اگه تا اینجا کار با من بودید تقریبا مبحث reflow و repaint رو تونستید بطور کل ببندید و یه درک کلی از تغییرات زمان رندر داشته باشید.مواردی که باید برای پرفورمنس مدنظر داشته باشید مختلف هستش و یکسری از اونها هم به موارد دیگه ای مثل ابزار و فریم ورکی که استفاده میکنید، مخاطب هدف، پروژه و هدف مدنظر و … داره.توی این مقاله سعی کردیم یک موضوع کلی و عمومی مربوط به رندر تغییرات ظاهری رو باهم صحبت کنید که درک بهتری ازش توی پروژه هامون داشته باشیم و دونستن این مباحث میتونه تفاوت هایی توی دیدگاهتون نسبت به کسی که نمیدونه ایجاد کنه.در آخر یادمون نره یادگیری متوقف نشه.اگر موردی بود که جا انداختم یا اشتباهی انجام دادم خوشحال میشم برام بنویسید و اینکه میتونی از طریق لینکدین با من در ارتباط باشید.منابعhttps://developers.google.com/speed/docs/insights/browser-reflowhttps://medium.com/swlh/what-the-heck-is-repaint-and-reflow-in-the-browser-b2d0fb980c08https://www.sitepoint.com/10-ways-minimize-reflows-improve-performance/