نگاهی به مرورگر وب مدرن (قسمت 4)

Mariko Kosaka

ورودی به Compositor می آید

این آخرین مورد از سری 4 قسمتی وبلاگ است که به داخل کروم نگاه می کند. بررسی نحوه مدیریت کد ما برای نمایش یک وب سایت. در پست قبلی به فرآیند رندر نگاه کردیم و با کامپوزیتور آشنا شدیم . در این پست، ما به این خواهیم پرداخت که چگونه کامپوزیتور هنگام ورود ورودی کاربر، تعامل صاف را ممکن می‌سازد.

رویدادها را از دیدگاه مرورگر وارد کنید

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

هنگامی که ژست کاربر مانند لمس روی صفحه رخ می دهد، فرآیند مرورگر همان چیزی است که در ابتدا ژست را دریافت می کند. با این حال، فرآیند مرورگر فقط از جایی که آن حرکت رخ داده است آگاه است زیرا محتوای داخل یک برگه توسط فرآیند رندر کنترل می شود. بنابراین فرآیند مرورگر نوع رویداد (مانند touchstart ) و مختصات آن را به فرآیند رندر می‌فرستد. فرآیند رندر با یافتن هدف رویداد و اجرای شنوندگان رویداد که پیوست شده اند، رویداد را به طور مناسب مدیریت می کند.

رویداد ورودی
شکل 1: رویداد ورودی که از طریق فرآیند مرورگر به فرآیند رندر هدایت می شود

Compositor رویدادهای ورودی را دریافت می کند

شکل 2: Viewport که روی لایه های صفحه قرار دارد

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

درک منطقه غیر سریع اسکرول

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

محدود منطقه غیر سریع اسکرول
شکل 3: نمودار ورودی توصیف شده به ناحیه غیرسریع اسکرول

هنگام نوشتن کنترل کننده رویداد، مراقب باشید

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

document.body.addEventListener('touchstart', event => {
    if (event.target === area) {
        event.preventDefault();
    }
});

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

منطقه تمام صفحه غیر قابل پیمایش سریع
شکل 4: نمودار ورودی توصیف شده به ناحیه غیرسریع پیمایشی که کل صفحه را پوشش می دهد.

برای کاهش این اتفاق، می‌توانید گزینه‌های passive: true را در شنونده رویداد خود پاس کنید. این به مرورگر اشاره می کند که هنوز می خواهید به رویداد در رشته اصلی گوش دهید، اما compositor می تواند ادامه دهد و فریم جدید را نیز ترکیب کند.

document.body.addEventListener('touchstart', event => {
    if (event.target === area) {
        event.preventDefault()
    }
 }, {passive: true});

بررسی کنید آیا رویداد قابل لغو است یا خیر

اسکرول صفحه
شکل 5: یک صفحه وب با قسمتی از صفحه که روی اسکرول افقی ثابت شده است

تصور کنید کادری در صفحه ای دارید که می خواهید جهت اسکرول را فقط به اسکرول افقی محدود کنید.

استفاده از گزینه passive: true در رویداد اشاره گر شما به این معنی است که پیمایش صفحه می تواند صاف باشد، اما ممکن است پیمایش عمودی تا زمانی که می خواهید از preventDefault تا جهت پیمایش را محدود کنید، شروع شده باشد. با استفاده از روش event.cancelable می توانید این موضوع را بررسی کنید.

document.body.addEventListener('pointermove', event => {
    if (event.cancelable) {
        event.preventDefault(); // block the native scroll
        /*
        *  do what you want the application to do here
        */
    }
}, {passive: true});

از طرف دیگر، می‌توانید از قانون CSS مانند touch-action برای حذف کامل کنترل‌کننده رویداد استفاده کنید.

#area {
  touch-action: pan-x;
}

یافتن هدف رویداد

تست ضربه بزنید
شکل 6: نخ اصلی که به رکوردهای رنگ نگاه می کند و می پرسد چه چیزی روی نقطه xy کشیده شده است.

هنگامی که thread کامپوزیتور یک رویداد ورودی را به رشته اصلی ارسال می کند، اولین چیزی که اجرا می شود یک تست ضربه برای یافتن هدف رویداد است. تست ضربه از داده‌های ثبت رنگ استفاده می‌کند که در فرآیند رندر تولید شده‌اند تا بفهمند که زیر مختصات نقطه‌ای که رویداد در آن رخ داده است، چه چیزی وجود دارد.

به حداقل رساندن ارسال رویداد به موضوع اصلی

در پست قبلی، در مورد اینکه چگونه صفحه نمایش معمولی ما صفحه نمایش را 60 بار در ثانیه تازه می کند و چگونه باید با آهنگ حرکت برای انیمیشن روان همگام شویم، بحث کردیم. برای ورودی، یک دستگاه صفحه لمسی معمولی رویدادهای لمسی را 60 تا 120 بار در ثانیه ارائه می دهد و یک ماوس معمولی رویدادها را 100 بار در ثانیه ارائه می دهد. رویداد ورودی از وفاداری بالاتری نسبت به صفحه نمایش ما برخوردار است.

اگر یک رویداد پیوسته مانند touchmove 120 بار در ثانیه به رشته اصلی ارسال شود، ممکن است در مقایسه با میزان کندی که صفحه نمایش می‌تواند بازخوانی کند، تعداد زیادی تست ضربه و اجرای جاوا اسکریپت را آغاز کند.

رویدادهای فیلتر نشده
شکل 7: رویدادهایی که خط زمانی فریم را پر می کنند و باعث jank صفحه می شوند

برای به حداقل رساندن تماس‌های بیش از حد به رشته اصلی، Chrome رویدادهای پیوسته (مانند wheel ، mousewheel ، mousemove ، pointermove ، touchmove ) را یکپارچه می‌کند و ارسال را درست قبل از requestAnimationFrame بعدی AnimationFrame به تأخیر می‌اندازد.

رویدادهای ادغام شده
شکل 8: همان جدول زمانی قبلی، اما رویداد ادغام شده و به تاخیر افتاده است

هر گونه رویداد مجزا مانند keydown ، keyup ، mouseup ، mousedown ، touchstart ، و touchend فورا ارسال می شود.

از getCoalescedEvents برای دریافت رویدادهای درون فریم استفاده کنید

برای اکثر برنامه های کاربردی وب، رویدادهای ادغام شده باید برای ارائه یک تجربه کاربری خوب کافی باشد. با این حال، اگر در حال ساختن چیزهایی مانند ترسیم برنامه و قرار دادن مسیری بر اساس مختصات touchmove هستید، ممکن است مختصات بین‌المللی را برای کشیدن یک خط صاف از دست بدهید. در این صورت، می‌توانید از متد getCoalescedEvents در رویداد اشاره‌گر استفاده کنید تا اطلاعاتی درباره آن رویدادهای ادغام شده به دست آورید.

getCoalescedEvents
شکل 9: مسیر حرکت لمسی صاف در سمت چپ، مسیر محدود یکپارچه در سمت راست
window.addEventListener('pointermove', event => {
    const events = event.getCoalescedEvents();
    for (let event of events) {
        const x = event.pageX;
        const y = event.pageY;
        // draw a line using x and y coordinates.
    }
});

مراحل بعدی

در این مجموعه، عملکردهای داخلی یک مرورگر وب را پوشش داده ایم. اگر هرگز به این فکر نکرده‌اید که چرا DevTools اضافه کردن {passive: true} را در کنترل‌کننده رویداد خود توصیه می‌کند یا اینکه چرا ممکن است ویژگی async در تگ اسکریپت خود بنویسید، امیدوارم این مجموعه روشن کند که چرا مرورگر به این اطلاعات برای ارائه سریع‌تر و روان‌تر نیاز دارد. تجربه وب

از فانوس دریایی استفاده کنید

اگر می‌خواهید کد خود را برای مرورگر خوب کنید اما نمی‌دانید از کجا شروع کنید، Lighthouse ابزاری است که ممیزی هر وب‌سایتی را اجرا می‌کند و گزارشی از آنچه درست انجام می‌شود و آنچه نیاز به بهبود دارد به شما می‌دهد. خواندن لیست ممیزی ها همچنین به شما ایده می دهد که یک مرورگر به چه چیزهایی اهمیت می دهد.

یاد بگیرید چگونه عملکرد را اندازه گیری کنید

ترفندهای عملکرد ممکن است برای سایت های مختلف متفاوت باشد، بنابراین بسیار مهم است که عملکرد سایت خود را بسنجید و تصمیم بگیرید که چه چیزی برای سایت شما مناسب است. تیم Chrome DevTools آموزش های کمی در مورد نحوه اندازه گیری عملکرد سایت شما دارد.

سیاست ویژگی را به سایت خود اضافه کنید

اگر می خواهید یک قدم اضافی بردارید، سیاست ویژگی یک ویژگی جدید پلتفرم وب است که می تواند حفاظی برای شما در هنگام ساخت پروژه باشد. روشن کردن خط مشی ویژگی رفتار خاص برنامه شما را تضمین می کند و از اشتباه کردن شما جلوگیری می کند. به عنوان مثال، اگر می‌خواهید مطمئن شوید که برنامه شما هرگز تجزیه را مسدود نمی‌کند، می‌توانید برنامه خود را روی خط‌مشی اسکریپت‌های همزمان اجرا کنید. وقتی sync-script: 'none' فعال باشد، جاوا اسکریپت مسدودکننده تجزیه کننده از اجرای آن جلوگیری خواهد شد. این مانع از مسدود کردن هر کد شما در تجزیه کننده می شود و مرورگر نیازی به نگرانی در مورد توقف تجزیه کننده ندارد.

جمع کنید

متشکرم

وقتی شروع به ساختن وب‌سایت کردم، تقریباً فقط به این اهمیت می‌دادم که چگونه کدم را بنویسم و ​​چه چیزی به من کمک می‌کند تا کارایی بیشتری داشته باشم. این چیزها مهم هستند، اما باید به این فکر کنیم که مرورگر چگونه کدهایی را که می نویسیم می گیرد. مرورگرهای مدرن روی روش هایی سرمایه گذاری می کنند که تجربه وب بهتری را برای کاربران فراهم کنند. خوب بودن با مرورگر با سازماندهی کد ما، به نوبه خود، تجربه کاربری شما را بهبود می بخشد. امیدوارم در تلاش برای رفتار خوب با مرورگرها به من بپیوندید!

از همه کسانی که پیش نویس های اولیه این مجموعه را بررسی کردند، از جمله (اما نه محدود به): الکس راسل ، پل آیریش ، مگین کرنی ، اریک بیدلمن ، ماتیاس باینس ، آدی عثمانی ، کینوکو یاسودا ، ناسکو اسکوف و چارلی ریس بسیار سپاسگزاریم.

آیا از این سریال لذت بردید؟ اگر سؤال یا پیشنهادی برای پست آینده دارید، مایلم در بخش نظرات زیر یا @kosamari در توییتر از شما بشنوم.