یک معماری خارج از موضوع اصلی می تواند به طور قابل توجهی قابلیت اطمینان و تجربه کاربری برنامه شما را بهبود بخشد.
در 20 سال گذشته، وب به طور چشمگیری از اسناد ثابت با چند سبک و تصاویر به برنامه های پیچیده و پویا تکامل یافته است. با این حال، یک چیز تا حد زیادی بدون تغییر باقی مانده است: ما فقط یک رشته در هر برگه مرورگر (به استثنای برخی موارد) برای انجام کار رندر کردن سایتها و اجرای جاوا اسکریپت خود داریم.
در نتیجه، موضوع اصلی به طرز باورنکردنی بیش از حد کار شده است. و با افزایش پیچیدگی برنامه های وب، موضوع اصلی به یک گلوگاه مهم برای عملکرد تبدیل می شود. بدتر از همه، مدت زمانی که برای اجرای کد در رشته اصلی برای یک کاربر خاص نیاز است تقریباً کاملاً غیرقابل پیش بینی است زیرا قابلیت های دستگاه تأثیر زیادی بر عملکرد دارد. این غیرقابلپیشبینیبودن تنها زمانی افزایش مییابد که کاربران از مجموعهای از دستگاههای متنوع، از تلفنهای دارای ویژگیهای بیش از حد محدود گرفته تا دستگاههای پرچمدار پرقدرت و با نرخ تازهسازی بالا، به وب دسترسی پیدا کنند.
اگر میخواهیم برنامههای وب پیچیده بهطور قابل اعتمادی دستورالعملهای عملکردی مانند Core Web Vitals را برآورده کنند - که مبتنی بر دادههای تجربی در مورد ادراک و روانشناسی انسان است - به راههایی برای اجرای کد خود از رشته اصلی (OMT) نیاز داریم.
چرا کارمندان وب؟
جاوا اسکریپت به طور پیشفرض، یک زبان تک رشتهای است که وظایف را روی رشته اصلی اجرا میکند. با این حال، کارگران وب نوعی دریچه فرار از رشته اصلی را با اجازه دادن به توسعه دهندگان برای چرخش رشته های جداگانه برای رسیدگی به کار خارج از رشته اصلی ارائه می کنند. در حالی که دامنه کارمندان وب محدود است و دسترسی مستقیم به DOM را ارائه نمی دهد، اگر کارهای قابل توجهی انجام شود که در غیر این صورت موضوع اصلی را تحت تأثیر قرار می دهد، می توانند بسیار سودمند باشند.
در مورد Core Web Vitals ، اجرای کار خارج از موضوع اصلی می تواند مفید باشد. به طور خاص، بارگذاری کار از موضوع اصلی به کارمندان وب میتواند مشاجره برای موضوع اصلی را کاهش دهد، که میتواند معیارهای مهم پاسخگویی مانند تعامل با رنگ بعدی (INP) و تاخیر ورودی اول (FID) را بهبود بخشد. وقتی موضوع اصلی کار کمتری برای پردازش داشته باشد، می تواند سریعتر به تعاملات کاربر پاسخ دهد.
کار کمتر با نخ اصلی - به ویژه در هنگام راه اندازی - همچنین با کاهش کارهای طولانی، مزایای بالقوه ای برای بزرگترین رنگ محتوایی (LCP) دارد. رندر کردن یک عنصر LCP به زمان رشته اصلی نیاز دارد - چه برای رندر کردن متن یا تصاویر، که عناصر متداول و رایج LCP هستند - و با کاهش کلی کار رشته اصلی، میتوانید اطمینان حاصل کنید که عنصر LCP صفحه شما با کارهای گران قیمت مسدود میشود. یک کارگر وب می تواند به جای آن از عهده این کار برآید.
گفتگو با کارگران وب
پلتفرمهای دیگر معمولاً از کار موازی پشتیبانی میکنند و به شما اجازه میدهند به یک رشته تابعی بدهید که به موازات بقیه برنامههای شما اجرا میشود. شما می توانید از هر دو رشته به متغیرهای یکسانی دسترسی داشته باشید و دسترسی به این منابع مشترک را می توان با mutexes و semaphores همگام کرد تا از شرایط مسابقه جلوگیری شود.
در جاوا اسکریپت، ما میتوانیم عملکردهای تقریباً مشابهی را از وبکارگران دریافت کنیم، که از سال 2007 وجود داشته و از سال 2012 در تمام مرورگرهای اصلی پشتیبانی میشود. کارگران وب بهطور موازی با رشته اصلی کار میکنند، اما برخلاف رشتههای سیستمعامل، نمیتوانند متغیرها را به اشتراک بگذارند.
برای ایجاد یک وب کارگر، یک فایل را به سازنده worker ارسال کنید، که شروع به اجرای آن فایل در یک رشته جداگانه می کند:
const worker = new Worker("./worker.js");
با ارسال پیام از طریق postMessage
API با وب کارگر ارتباط برقرار کنید. مقدار پیام را به عنوان یک پارامتر در تماس postMessage
ارسال کنید و سپس یک شنونده رویداد پیام را به کارگر اضافه کنید:
main.js
const worker = new Worker('./worker.js');
worker.postMessage([40, 2]);
worker.js
addEventListener('message', event => {
const [a, b] = event.data;
// Do stuff with the message
// ...
});
برای ارسال پیام به رشته اصلی، از همان postMessage
API در web worker استفاده کنید و شنونده رویداد را در رشته اصلی تنظیم کنید:
main.js
const worker = new Worker('./worker.js');
worker.postMessage([40, 2]);
worker.addEventListener('message', event => {
console.log(event.data);
});
worker.js
addEventListener('message', event => {
const [a, b] = event.data;
// Do stuff with the message
postMessage(a + b);
});
مسلماً این رویکرد تا حدودی محدود است. از لحاظ تاریخی، کارگران وب عمدتاً برای جابجایی یک تکه کار سنگین از رشته اصلی استفاده میشدهاند. تلاش برای انجام چندین عملیات با یک وبکارگر به سرعت دشوار میشود: شما باید نه تنها پارامترها، بلکه عملیات موجود در پیام را نیز رمزگذاری کنید، و باید حسابداری را انجام دهید تا پاسخها به درخواستها مطابقت داشته باشد. این پیچیدگی احتمالاً به همین دلیل است که کارگران وب به طور گستردهتری مورد پذیرش قرار نگرفتهاند.
اما اگر بتوانیم برخی از مشکلات برقراری ارتباط بین thread اصلی و کارگران وب را حذف کنیم، این مدل می تواند برای بسیاری از موارد استفاده مناسب باشد. و خوشبختانه، کتابخانه ای وجود دارد که این کار را انجام می دهد!
کاملینک: کاهش کار کارگران وب
Comlink کتابخانه ای است که هدف آن این است که به شما اجازه دهد بدون فکر کردن به جزئیات postMessage
از وب کارمندان استفاده کنید. Comlink به شما امکان می دهد تا متغیرها را بین کارگران وب و رشته اصلی تقریباً مانند سایر زبان های برنامه نویسی که از thread پشتیبانی می کنند به اشتراک بگذارید.
Comlink را با وارد کردن آن در وبکارگر و تعریف مجموعهای از توابع برای نمایش در رشته اصلی راهاندازی میکنید. سپس Comlink را روی رشته اصلی وارد کرده، worker را بپیچید و به توابع در معرض دید دسترسی پیدا کنید:
worker.js
import {expose} from 'comlink';
const api = {
someMethod() {
// ...
}
}
expose(api);
main.js
import {wrap} from 'comlink';
const worker = new Worker('./worker.js');
const api = wrap(worker);
متغیر api
در رشته اصلی مانند آنچه در وب ورکر است عمل می کند، با این تفاوت که هر تابع به جای خود مقدار، یک وعده برای یک مقدار برمی گرداند.
چه کدی را باید به وبکارگر منتقل کنید؟
کارمندان وب به DOM و بسیاری از APIها مانند WebUSB ، WebRTC یا Web Audio دسترسی ندارند، بنابراین نمیتوانید قطعاتی از برنامه خود را که به چنین دسترسیهایی متکی هستند در یک کارگر قرار دهید. با این حال، هر کد کوچکی که به یک کارگر منتقل میشود، فضای بیشتری را در رشته اصلی برای چیزهایی که باید وجود داشته باشد، خریداری میکند - مانند بهروزرسانی رابط کاربری.
یک مشکل برای توسعه دهندگان وب این است که اکثر برنامه های وب برای هماهنگ کردن همه چیز در برنامه به یک چارچوب رابط کاربری مانند Vue یا React متکی هستند. همه چیز جزئی از چارچوب است و بنابراین ذاتاً به DOM گره خورده است. به نظر می رسد که مهاجرت به معماری OMT دشوار است.
با این حال، اگر به مدلی برویم که در آن نگرانیهای UI از سایر نگرانیها جدا شوند، مانند مدیریت دولتی، وبکارگران حتی با برنامههای مبتنی بر چارچوب میتوانند کاملاً مفید باشند. این دقیقاً همان رویکردی است که با PROXX اتخاذ شده است.
PROXX: مطالعه موردی OMT
تیم Google Chrome PROXX را بهعنوان یک کلون Minesweeper توسعه داده است که الزامات برنامه وب پیشرو را برآورده میکند، از جمله کار آفلاین و داشتن تجربه کاربری جذاب. متأسفانه، نسخههای اولیه بازی روی دستگاههای محدودی مانند تلفنهای ویژه عملکرد ضعیفی داشتند، که باعث شد تیم متوجه شود که موضوع اصلی یک گلوگاه است.
تیم تصمیم گرفت از وبکارگرها استفاده کند تا حالت بصری بازی را از منطق آن جدا کند:
- موضوع اصلی رندر انیمیشن ها و انتقال ها را انجام می دهد.
- یک وبکار منطق بازی را مدیریت میکند، که صرفاً محاسباتی است.
OMT تأثیرات جالبی بر عملکرد تلفن ویژگی PROXX داشت. در نسخه غیر OMT، رابط کاربری پس از تعامل کاربر با آن به مدت شش ثانیه ثابت می شود. هیچ بازخوردی وجود ندارد و کاربر باید شش ثانیه کامل صبر کند تا بتواند کار دیگری انجام دهد.
با این حال، در نسخه OMT، بازی دوازده ثانیه طول می کشد تا یک آپدیت UI کامل شود. در حالی که به نظر می رسد این کاهش عملکرد است، اما در واقع منجر به افزایش بازخورد به کاربر می شود. کاهش سرعت به این دلیل رخ می دهد که برنامه فریم های بیشتری را نسبت به نسخه غیر OMT ارسال می کند که اصلاً فریمی ارسال نمی کند. بنابراین کاربر میداند که اتفاقی در حال رخ دادن است و میتواند با بهروزرسانی رابط کاربری به بازی ادامه دهد و باعث میشود بازی به طور قابل توجهی بهتر شود.
این یک مبادله آگاهانه است: ما به کاربران دستگاههای محدود تجربهای میدهیم که بدون جریمه کردن کاربران دستگاههای پیشرفته، احساس بهتری داشته باشند.
مفاهیم معماری OMT
همانطور که مثال PROXX نشان می دهد، OMT باعث می شود برنامه شما به طور قابل اعتماد در طیف وسیع تری از دستگاه ها اجرا شود، اما برنامه شما را سریعتر نمی کند:
- شما فقط کار را از موضوع اصلی حرکت می دهید، نه اینکه کار را کاهش دهید.
- هزینه ارتباط اضافی بین وب کارگر و رشته اصلی گاهی اوقات می تواند کارها را تا حدی کندتر کند.
با در نظر گرفتن معاوضه ها
از آنجایی که رشته اصلی برای پردازش تعاملات کاربر مانند اسکرول در حین اجرای جاوا اسکریپت رایگان است، فریم های افت شده کمتری وجود دارد، حتی اگر کل زمان انتظار ممکن است کمی بیشتر باشد. کمی صبر کردن کاربر به رها کردن یک فریم ترجیح داده می شود زیرا حاشیه خطا برای فریم های افت شده کمتر است: رها کردن یک فریم در میلی ثانیه اتفاق می افتد، در حالی که قبل از اینکه کاربر زمان انتظار را درک کند صدها میلی ثانیه فرصت دارید.
به دلیل غیرقابل پیشبینی بودن عملکرد دستگاهها، هدف معماری OMT در واقع کاهش ریسک است - قویتر کردن برنامه شما در مواجهه با شرایط زمان اجرا بسیار متغیر - نه در مورد مزایای عملکرد موازیسازی. افزایش انعطاف پذیری و پیشرفت در UX بیش از هر معاوضه کوچکی در سرعت ارزش دارد.
نکته ای در مورد ابزارسازی
کارگران وب هنوز جریان اصلی نیستند، بنابراین اکثر ابزارهای ماژول - مانند webpack و Rollup - از آنها پشتیبانی نمی کنند. (هر چند بسته این کار را انجام می دهد!) خوشبختانه، افزونه هایی برای ایجاد وب کارمندان وجود دارد که با وب پک و جمع بندی کار کنند :
- پلاگین کارگر برای وب پک
- rollup-plugin-off-main-thread برای Rollup
جمع بندی
برای اطمینان از اینکه برنامههای ما تا حد امکان قابل اعتماد و در دسترس هستند، بهویژه در بازاری که بهطور فزایندهای جهانی شده است، باید از دستگاههای محدود پشتیبانی کنیم—اینگونه است که اکثر کاربران در سطح جهانی به وب دسترسی دارند. OMT روشی امیدوارکننده برای افزایش عملکرد در چنین دستگاه هایی بدون تأثیر نامطلوب بر کاربران دستگاه های پیشرفته ارائه می دهد.
همچنین، OMT مزایای ثانویه دارد:
- هزینه های اجرای جاوا اسکریپت را به یک رشته جداگانه منتقل می کند.
- هزینه های تجزیه را کاهش می دهد، به این معنی که UI ممکن است سریعتر راه اندازی شود. این ممکن است First Contentful Paint یا حتی Time to Interactive را کاهش دهد که به نوبه خود می تواند امتیاز Lighthouse شما را افزایش دهد.
کارگران وب نباید ترسناک باشند. ابزارهایی مانند Comlink کار کارگران را از بین می برند و آنها را به انتخابی مناسب برای طیف وسیعی از برنامه های کاربردی وب تبدیل می کنند.
تصویر قهرمان از Unsplash اثر جیمز پیکوک .