Page Lifecycle API

پشتیبانی مرورگر

  • کروم: 68.
  • لبه: 79.
  • فایرفاکس: پشتیبانی نمی شود.
  • سافاری: پشتیبانی نمی شود.

مرورگرهای مدرن امروزی گاهی اوقات صفحات را به حالت تعلیق در می آورند یا در صورت محدود بودن منابع سیستم به طور کامل آنها را کنار می گذارند. در آینده، مرورگرها می خواهند این کار را فعالانه انجام دهند، بنابراین انرژی و حافظه کمتری مصرف می کنند. Page Lifecycle API قلاب‌های چرخه حیات را فراهم می‌کند تا صفحات شما بتوانند با خیال راحت این مداخلات مرورگر را بدون تأثیر بر تجربه کاربر انجام دهند. نگاهی به API بیندازید تا ببینید آیا باید این ویژگی ها را در برنامه خود پیاده سازی کنید یا خیر.

پس زمینه

چرخه عمر برنامه یک راه کلیدی برای مدیریت منابع سیستم عامل های مدرن است. در اندروید، iOS و نسخه‌های اخیر ویندوز، برنامه‌ها را می‌توان در هر زمانی که بخواهید توسط سیستم عامل راه‌اندازی و متوقف کرد. این به این پلتفرم‌ها اجازه می‌دهد منابع را در جایی که به بهترین وجه برای کاربر مفید هستند، ساده‌سازی و تخصیص مجدد دهند.

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

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

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

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

Page Lifecycle API سعی می کند این مشکل را با استفاده از موارد زیر حل کند:

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

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

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

مروری بر وضعیت ها و رویدادهای چرخه عمر صفحه

همه حالت‌های چرخه عمر صفحه گسسته و متقابلاً انحصاری هستند، به این معنی که یک صفحه در یک زمان فقط می‌تواند در یک حالت باشد. و اکثر تغییرات در حالت چرخه عمر صفحه عموماً از طریق رویدادهای DOM قابل مشاهده است (به توصیه های توسعه دهنده برای هر حالت برای موارد استثنا مراجعه کنید).

شاید ساده‌ترین راه برای توضیح حالت‌های چرخه عمر صفحه - و همچنین رویدادهایی که سیگنال انتقال بین آنها را نشان می‌دهند - با یک نمودار باشد:

یک نمایش تصویری از وضعیت و جریان رویداد که در سراسر این سند توضیح داده شده است.
وضعیت API چرخه زندگی صفحه و جریان رویداد.

ایالات

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

ایالت توضیحات
فعال

اگر صفحه قابل مشاهده باشد و فوکوس ورودی داشته باشد در حالت فعال است.

حالات احتمالی قبلی:
منفعل (از طریق رویداد focus )
ثابت (از طریق رویداد resume ، سپس رویداد pageshow )

حالات بعدی احتمالی:
منفعل (از طریق رویداد blur )

منفعل

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

حالات احتمالی قبلی:
فعال (از طریق رویداد blur )
پنهان (از طریق رویداد visibilitychange )
ثابت (از طریق رویداد resume ، سپس رویداد pageshow )

حالات بعدی احتمالی:
فعال (از طریق رویداد focus )
پنهان (از طریق رویداد visibilitychange )

پنهان شده است

اگر صفحه ای قابل مشاهده نباشد (و ثابت، دور انداخته یا خاتمه نیافته باشد) در حالت پنهان است.

حالات احتمالی قبلی:
غیرفعال (از طریق رویداد visibilitychange )
ثابت (از طریق رویداد resume ، سپس رویداد pageshow )

حالات بعدی احتمالی:
غیرفعال (از طریق رویداد visibilitychange )
منجمد (از طریق رویداد freeze )
دور انداخته شد (هیچ رویدادی اجرا نشد)
خاتمه یافت (هیچ رویدادی اجرا نشد)

منجمد

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

مرورگرها صفحات را به عنوان راهی برای حفظ مصرف CPU/باتری/داده مسدود می کنند. آنها همچنین این کار را به عنوان راهی برای فعال کردن ناوبری سریعتر به عقب و جلو انجام می دهند - اجتناب از نیاز به بارگذاری مجدد کامل صفحه.

حالات احتمالی قبلی:
پنهان (از طریق رویداد freeze )

حالات بعدی احتمالی:
فعال (از طریق رویداد resume ، سپس رویداد pageshow )
غیرفعال (از طریق رویداد resume ، سپس رویداد pageshow )
پنهان (از طریق رویداد resume )
دور انداخته شد (هیچ رویدادی اجرا نشد)

خاتمه یافت

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

حالات احتمالی قبلی:
پنهان (از طریق رویداد pagehide )

حالات بعدی احتمالی:
هیچ کدام

دور انداخته شد

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

در حالت حذف ، خود برگه (شامل عنوان برگه و فاویکون) معمولاً برای کاربر قابل مشاهده است حتی اگر صفحه از بین رفته باشد.

حالات احتمالی قبلی:
پنهان (هیچ رویدادی اجرا نشد)
منجمد (هیچ رویدادی پخش نشد)

حالات بعدی احتمالی:
هیچ کدام

رویدادها

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

نام جزئیات
focus

یک عنصر DOM فوکوس دریافت کرده است.

توجه: یک رویداد focus لزوماً نشان دهنده تغییر وضعیت نیست. این فقط در صورتی تغییر حالت را نشان می دهد که صفحه قبلاً فوکوس ورودی نداشته باشد.

حالات احتمالی قبلی:
منفعل

حالات فعلی احتمالی:
فعال

blur

یک عنصر DOM تمرکز خود را از دست داده است.

توجه: یک رویداد blur لزوماً نشان دهنده تغییر وضعیت نیست. فقط در صورتی که صفحه دیگر فوکوس ورودی نداشته باشد، تغییر حالت را نشان می دهد (یعنی صفحه فقط فوکوس را از یک عنصر به عنصر دیگر تغییر نداده است).

حالات احتمالی قبلی:
فعال

حالات فعلی احتمالی:
منفعل

visibilitychange

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

حالات احتمالی قبلی:
منفعل
پنهان شده است

حالات فعلی احتمالی:
منفعل
پنهان شده است

freeze *

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

حالات احتمالی قبلی:
پنهان شده است

حالات فعلی احتمالی:
منجمد شده

resume *

مرورگر یک صفحه ثابت را از سر گرفته است.

حالات احتمالی قبلی:
منجمد شده

حالات فعلی احتمالی:
فعال (اگر رویداد pageshow دنبال شود)
منفعل (اگر به دنبال رویداد pageshow باشد)
پنهان شده است

pageshow

یک ورودی سابقه جلسه در حال عبور است.

این می‌تواند یک بارگذاری صفحه کاملاً جدید باشد یا صفحه‌ای که از کش عقب/ جلو گرفته شده است. اگر صفحه از حافظه پنهان عقب/ جلو گرفته شده باشد، ویژگی persisted رویداد true است، در غیر این صورت false است.

حالات احتمالی قبلی:
یخ زده (یک رویداد resume نیز اخراج می شد)

حالات فعلی احتمالی:
فعال
منفعل
پنهان شده است

pagehide

یک ورودی سابقه جلسه از آن در حال عبور است.

اگر کاربر در حال پیمایش به صفحه دیگری باشد و مرورگر بتواند صفحه فعلی را به حافظه پنهان عقب/ جلو اضافه کند تا بعداً دوباره از آن استفاده شود، ویژگی persisted رویداد true است. وقتی true ، صفحه وارد حالت ثابت می شود، در غیر این صورت وارد حالت پایان یافته می شود.

حالات احتمالی قبلی:
پنهان شده است

حالات فعلی احتمالی:
ثابت ( event.persisted درست است، رویداد freeze به دنبال دارد)
خاتمه یافت ( event.persisted نادرست است، رویداد unload بارگیری می شود)

beforeunload

پنجره، سند و منابع آن در شرف تخلیه هستند. سند هنوز قابل مشاهده است و رویداد هنوز در این مرحله قابل لغو است.

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

حالات احتمالی قبلی:
پنهان شده است

حالات فعلی احتمالی:
خاتمه یافت

unload

صفحه در حال تخلیه است.

هشدار: استفاده از رویداد unload هرگز توصیه نمی شود زیرا غیرقابل اعتماد است و در برخی موارد می تواند به عملکرد آسیب برساند. برای جزئیات بیشتر به بخش APIهای قدیمی مراجعه کنید.

حالات احتمالی قبلی:
پنهان شده است

حالات فعلی احتمالی:
خاتمه یافت

* یک رویداد جدید را نشان می دهد که توسط صفحه Lifecycle API تعریف شده است

ویژگی های جدید اضافه شده در کروم 68

نمودار قبلی دو حالت را نشان می‌دهد که به جای شروع توسط کاربر، توسط سیستم راه‌اندازی شده‌اند: منجمد و دور انداخته شده . همانطور که قبلاً ذکر شد، امروزه مرورگرها گاهی اوقات برگه های مخفی را (به صلاحدید خود) مسدود می کنند و دور می اندازند، اما توسعه دهندگان راهی برای اطلاع از زمان وقوع این اتفاق ندارند.

در Chrome 68، توسعه‌دهندگان اکنون می‌توانند با گوش دادن به freeze و resume رویدادها در document ، مشاهده کنند که یک برگه مخفی ثابت و غیر منجمد می‌شود.

document.addEventListener('freeze', (event) => {
  // The page is now frozen.
});

document.addEventListener('resume', (event) => {
  // The page has been unfrozen.
});

از Chrome 68 شی document اکنون شامل یک ویژگی wasDiscarded در کروم دسکتاپ است ( پشتیبانی از Android در این شماره ردیابی می شود ). برای تعیین اینکه آیا یک صفحه زمانی که در یک برگه مخفی است کنار گذاشته شده است یا خیر، می توانید مقدار این ویژگی را در زمان بارگذاری صفحه بررسی کنید (توجه داشته باشید: برای استفاده مجدد، صفحات حذف شده باید دوباره بارگیری شوند).

if (document.wasDiscarded) {
  // Page was previously discarded by the browser while in a hidden tab.
}

برای مشاوره در مورد کارهایی که در freeze و resume رویدادها مهم هستند، و همچنین نحوه مدیریت و آماده شدن برای صفحاتی که دور ریخته می شوند، توصیه های برنامه نویس برای هر ایالت را ببینید.

چندین بخش بعدی یک نمای کلی از نحوه تناسب این ویژگی های جدید با وضعیت ها و رویدادهای پلت فرم وب موجود ارائه می دهد.

نحوه مشاهده حالتهای چرخه عمر صفحه در کد

در حالت‌های فعال ، غیرفعال و پنهان ، می‌توان کد جاوا اسکریپت را اجرا کرد که وضعیت چرخه حیات صفحه را از APIهای پلتفرم وب موجود تعیین می‌کند.

const getState = () => {
  if (document.visibilityState === 'hidden') {
    return 'hidden';
  }
  if (document.hasFocus()) {
    return 'active';
  }
  return 'passive';
};

از طرف دیگر، حالت‌های یخ‌زده و خاتمه‌یافته ، تنها در شنونده رویداد مربوطه ( freeze و pagehide ) با تغییر وضعیت قابل شناسایی هستند.

نحوه مشاهده تغییرات حالت

با استفاده از تابع getState() که قبلا تعریف شده بود، می توانید تمام تغییرات حالت چرخه عمر صفحه را با کد زیر مشاهده کنید.

// Stores the initial state using the `getState()` function (defined above).
let state = getState();

// Accepts a next state and, if there's been a state change, logs the
// change to the console. It also updates the `state` value defined above.
const logStateChange = (nextState) => {
  const prevState = state;
  if (nextState !== prevState) {
    console.log(`State change: ${prevState} >>> ${nextState}`);
    state = nextState;
  }
};

// Options used for all event listeners.
const opts = {capture: true};

// These lifecycle events can all use the same listener to observe state
// changes (they call the `getState()` function to determine the next state).
['pageshow', 'focus', 'blur', 'visibilitychange', 'resume'].forEach((type) => {
  window.addEventListener(type, () => logStateChange(getState()), opts);
});

// The next two listeners, on the other hand, can determine the next
// state from the event itself.
window.addEventListener('freeze', () => {
  // In the freeze event, the next state is always frozen.
  logStateChange('frozen');
}, opts);

window.addEventListener('pagehide', (event) => {
  // If the event's persisted property is `true` the page is about
  // to enter the back/forward cache, which is also in the frozen state.
  // If the event's persisted property is not `true` the page is
  // about to be unloaded.
  logStateChange(event.persisted ? 'frozen' : 'terminated');
}, opts);

این کد سه کار را انجام می دهد:

  • با استفاده از تابع getState() حالت اولیه را تنظیم می کند.
  • تابعی را تعریف می کند که حالت بعدی را می پذیرد و در صورت تغییر، تغییرات وضعیت را به کنسول ثبت می کند.
  • شنوندگان رویداد را برای همه رویدادهای چرخه حیات ضروری اضافه می کند، که به نوبه خود logStateChange() فراخوانی می کند و در حالت بعدی ارسال می شود.

یک نکته در مورد کد این است که تمام شنوندگان رویداد به window اضافه می شوند و همه آنها {capture: true} را پاس می کنند. چند دلیل برای این وجود دارد:

  • همه رویدادهای چرخه عمر صفحه هدف یکسانی ندارند. pagehide ، و pageshow بر روی window شلیک می شود. visibilitychange ، freeze و resume روی document فعال می‌شوند و focus و blur روی عناصر DOM مربوطه فعال می‌شوند.
  • بیشتر این رویدادها حباب نمی‌شوند، به این معنی که اضافه کردن شنوندگان رویداد غیرمجاز به یک عنصر جد مشترک و مشاهده همه آنها غیرممکن است.
  • مرحله ضبط قبل از مرحله هدف یا حباب اجرا می شود، بنابراین افزودن شنوندگان به آنجا کمک می کند تا اطمینان حاصل شود که قبل از اینکه سایر کدها بتوانند آنها را لغو کنند، اجرا می شوند.

توصیه های توسعه دهنده برای هر ایالت

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

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

ایالت توصیه های توسعه دهنده
Active

حالت فعال بحرانی ترین زمان برای کاربر است و بنابراین مهم ترین زمان برای پاسخگویی صفحه شما به ورودی کاربر است.

هر کار غیر UI که ممکن است رشته اصلی را مسدود کند باید به دوره‌های بی‌حرکت اولویت داده شود یا برای یک وب‌کارگر بارگذاری شود .

Passive

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

هنگامی که صفحه از فعال به غیرفعال تغییر می کند، زمان خوبی برای تداوم وضعیت ذخیره نشده برنامه است.

Hidden

وقتی صفحه از حالت غیرفعال به مخفی تغییر می کند، این امکان وجود دارد که کاربر تا زمانی که مجدداً بارگیری نشود، با آن ارتباط برقرار نکند.

انتقال به حالت پنهان اغلب آخرین تغییر حالت است که به طور قابل اعتماد توسط توسعه دهندگان قابل مشاهده است (این امر به ویژه در تلفن همراه صادق است، زیرا کاربران می توانند برگه ها یا خود برنامه مرورگر را ببندند، و رویدادهای beforeunload ، pagehide و unload در آن موارد فعال نمی شوند. ).

این بدان معنی است که شما باید حالت پنهان را به عنوان پایان احتمالی جلسه کاربر در نظر بگیرید. به عبارت دیگر، هر حالت برنامه ذخیره نشده را حفظ کنید و داده های تحلیلی ارسال نشده را ارسال کنید.

همچنین باید به‌روزرسانی‌های رابط کاربری را متوقف کنید (زیرا کاربر آنها را نمی‌بیند)، و باید کارهایی را که کاربر نمی‌خواهد در پس‌زمینه اجرا شود، متوقف کنید.

Frozen

در حالت یخ‌زده ، وظایف قابل انجماد در صف‌های وظیفه تا زمانی که صفحه از حالت ثابت خارج شود، به حالت تعلیق در می‌آیند - که ممکن است هرگز اتفاق نیفتد (مثلاً اگر صفحه دور ریخته شود).

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

به طور خاص ، مهم است که شما:

  • تمام اتصالات IndexedDB باز را ببندید.
  • اتصالات باز BroadcastChannel را ببندید.
  • اتصالات فعال WEBRTC را ببندید.
  • هرگونه نظرسنجی شبکه را متوقف کنید یا اتصالات باز سوکت وب را ببندید.
  • قفل های وب نگه داشته شده را آزاد کنید.

همچنین باید هر وضعیت نمای پویا (مثلاً موقعیت اسکرول در یک نمای لیست نامحدود) را در sessionStorage (یا IndexedDB از طریق commit() ) که می‌خواهید در صورت حذف و بارگذاری مجدد صفحه بعداً بازیابی شود، ادامه دهید.

اگر صفحه از حالت ثابت به حالت مخفی تبدیل شد، می‌توانید اتصالات بسته را دوباره باز کنید یا هر نظرسنجی را که زمانی که صفحه در ابتدا ثابت بود متوقف کرده‌اید، دوباره راه‌اندازی کنید.

Terminated

معمولاً هنگام انتقال صفحه به حالت پایان یافته ، نیازی به انجام هیچ اقدامی نیست.

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

همچنین (همانطور که در توصیه‌های مربوط به حالت پنهان ذکر شد)، برای توسعه‌دهندگان بسیار مهم است که بدانند انتقال به حالت پایان‌یافته در بسیاری از موارد (به‌ویژه در تلفن همراه) به‌طور قابل اعتماد قابل تشخیص نیست، بنابراین توسعه‌دهندگانی که به رویدادهای پایان (مثلاً beforeunload ) وابسته هستند , pagehide و unload ) به احتمال زیاد داده ها را از دست می دهند.

Discarded

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

در نتیجه، باید برای احتمال نادیده گرفتن تغییر از حالت مخفی به ثابت آماده شوید و سپس می توانید با بررسی document.wasDiscarded به بازیابی صفحه حذف شده در زمان بارگذاری صفحه واکنش نشان دهید.

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

APIهای چرخه عمر قدیمی که باید اجتناب کنید

تا جایی که ممکن است باید از اتفاقات زیر اجتناب شود.

رویداد تخلیه

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

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

علاوه بر این، صرف حضور یک کنترل‌کننده unload ثبت‌شده (از طریق onunload یا addEventListener() ) می‌تواند مانع از این می‌شود که مرورگرها بتوانند صفحات را در حافظه پنهان عقب و جلو برای بارگیری سریع‌تر به عقب و جلو قرار دهند.

در همه مرورگرهای مدرن، توصیه می‌شود که همیشه از رویداد pagehide برای شناسایی بارگیری‌های احتمالی صفحه (معروف به حالت پایان یافته ) به جای رویداد unload استفاده شود. اگر نیاز به پشتیبانی از اینترنت اکسپلورر نسخه 10 و پایین‌تر دارید، باید قابلیت تشخیص رویداد pagehide را داشته باشید و فقط در صورتی unload استفاده کنید که مرورگر از pagehide پشتیبانی نمی‌کند:

const terminationEvent = 'onpagehide' in self ? 'pagehide' : 'unload';

window.addEventListener(terminationEvent, (event) => {
  // Note: if the browser is able to cache the page, `event.persisted`
  // is `true`, and the state is frozen rather than terminated.
});

رویداد قبل از بارگیری

رویداد beforeunload مشکلی مشابه با رویداد unload دارد، زیرا از نظر تاریخی، وجود یک رویداد beforeunload می‌تواند از واجد شرایط بودن صفحات برای کش عقب/ جلو جلوگیری کند. مرورگرهای مدرن این محدودیت را ندارند. اگرچه برخی از مرورگرها، به عنوان یک اقدام احتیاطی، هنگام تلاش برای قرار دادن یک صفحه در حافظه پنهان عقب/ جلو، رویداد beforeunload فعال نمی‌کنند، به این معنی که رویداد به عنوان سیگنال پایان جلسه قابل اعتماد نیست. علاوه بر این، برخی از مرورگرها (از جمله Chrome ) قبل از اینکه اجازه دهند رویداد beforeunload فعال شود، نیاز به تعامل با کاربر در صفحه دارند که بر قابلیت اطمینان آن تأثیر می گذارد.

یک تفاوت بین beforeunload و unload این است که استفاده های قانونی از beforeunload وجود دارد. به عنوان مثال ، هنگامی که می خواهید به کاربر هشدار دهید که در صورت ادامه بارگیری صفحه ، تغییراتی غیرقانونی از دست می دهند.

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

به عبارت دیگر، این کار را انجام ندهید (زیرا یک شنونده beforeunload بدون قید و شرط اضافه می کند):

addEventListener('beforeunload', (event) => {
  // A function that returns `true` if the page has unsaved changes.
  if (pageHasUnsavedChanges()) {
    event.preventDefault();

    // Legacy support for older browsers.
    return (event.returnValue = true);
  }
});

در عوض این کار را انجام دهید (زیرا فقط در صورت نیاز شنونده beforeunload را اضافه می کند و در صورت عدم نیاز آن را حذف می کند):

const beforeUnloadListener = (event) => {
  event.preventDefault();
  
  // Legacy support for older browsers.
  return (event.returnValue = true);
};

// A function that invokes a callback when the page has unsaved changes.
onPageHasUnsavedChanges(() => {
  addEventListener('beforeunload', beforeUnloadListener);
});

// A function that invokes a callback when the page's unsaved changes are resolved.
onAllChangesSaved(() => {
  removeEventListener('beforeunload', beforeUnloadListener);
});

سوالات متداول

چرا حالت "بارگیری" وجود ندارد؟

Page Lifecycle API حالتها را مجزا و انحصاری متقابل تعریف می کند. از آنجایی که یک صفحه می‌تواند در حالت فعال، غیرفعال یا پنهان بارگیری شود، و از آنجایی که می‌تواند حالت‌ها را تغییر دهد - یا حتی خاتمه یابد - قبل از اتمام بارگذاری، یک حالت بارگذاری جداگانه در این پارادایم معنا ندارد.

صفحه من وقتی پنهان است کارهای مهمی انجام می دهد، چگونه می توانم جلوی فریز یا دور انداختن آن را بگیرم؟

دلایل قانونی زیادی وجود دارد که صفحات وب نباید هنگام اجرا در حالت مخفی مسدود شوند. بارزترین مثال اپلیکیشنی است که موسیقی پخش می کند.

همچنین شرایطی وجود دارد که دور انداختن صفحه برای Chrome خطرناک است، مثلاً اگر حاوی فرمی با ورودی کاربر ارسال‌نشده باشد، یا اگر دارای یک کنترل کننده beforeunload بارگیری باشد که هنگام بارگیری صفحه هشدار می‌دهد.

در حال حاضر، کروم در حذف صفحات محافظه کارانه عمل می کند و تنها زمانی این کار را انجام می دهد که مطمئن باشد بر کاربران تأثیری نخواهد گذاشت. برای مثال، صفحاتی که مشاهده شده‌اند هر یک از کارهای زیر را در حالت مخفی انجام می‌دهند، کنار گذاشته نمی‌شوند، مگر اینکه تحت محدودیت شدید منابع باشند:

  • پخش صدا
  • با استفاده از WebRTC
  • به روز رسانی عنوان جدول یا فاویکون
  • نمایش هشدارها
  • ارسال اعلان های فشار

برای ویژگی‌های فهرست فعلی که برای تعیین اینکه آیا یک برگه را می‌توان به‌طور ایمن ثابت کرد یا دور انداخت، ببینید: Heuristics for Freezing & Discarding در Chrome.

کش عقب/ جلو چیست؟

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

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

برای همه مقاصد، این انجماد از نظر عملکردی همانند مرورگرهای انجماد برای حفظ CPU/باتری است. به همین دلیل بخشی از حالت چرخه حیات منجمد در نظر گرفته می شود.

اگر نمی‌توانم APIهای ناهمزمان را در حالت‌های ثابت یا خاتمه اجرا کنم، چگونه می‌توانم داده‌ها را در IndexedDB ذخیره کنم؟

در حالت‌های ثابت و خاتمه‌یافته، وظایف قابل انجماد در صف‌های وظیفه صفحه به حالت تعلیق در می‌آیند، به این معنی که APIهای ناهمزمان و مبتنی بر تماس مانند IndexedDB قابل استفاده نیستند.

در آینده، یک متد commit() به اشیاء IDBTransaction اضافه خواهیم کرد، که به توسعه دهندگان راهی می دهد تا تراکنش هایی را که به طور موثر فقط نوشتن هستند و نیازی به callback ندارند، انجام دهند. به عبارت دیگر، اگر توسعه‌دهنده فقط داده‌ها را در IndexedDB می‌نویسد و یک تراکنش پیچیده شامل خواندن و نوشتن را انجام نمی‌دهد، متد commit() می‌تواند قبل از تعلیق صف‌های وظیفه به پایان برسد (با فرض اینکه پایگاه داده IndexedDB از قبل باز باشد).

با این حال، برای کدهایی که باید امروز کار کنند، توسعه دهندگان دو گزینه دارند:

  • از فضای ذخیره‌سازی جلسه استفاده کنید: ذخیره‌سازی جلسه همزمان است و در بین صفحات نادیده گرفته می‌شود.
  • از IndexedDB از سرویس‌کار خود استفاده کنید: یک سرویس‌گر می‌تواند داده‌ها را پس از پایان یا دور انداختن صفحه در IndexedDB ذخیره کند. در شنونده رویداد freeze یا pagehide می‌توانید داده‌ها را از طریق postMessage() به سرویس‌کار خود ارسال کنید و سرویس‌کار می‌تواند ذخیره داده‌ها را انجام دهد.

آزمایش برنامه شما در حالت یخ زده و دور انداخته شده

برای آزمایش نحوه عملکرد برنامه‌تان در حالت‌های ثابت و دور انداخته‌شده، می‌توانید از chrome://discards دیدن کنید تا در واقع هر یک از برگه‌های باز خود را ثابت یا نادیده بگیرید.

Chrome UI را کنار می‌گذارد
Chrome UI را کنار می‌گذارد

این به شما این امکان را می دهد که مطمئن شوید صفحه شما به درستی رویدادهای freeze و resume و همچنین پرچم document.wasDiscarded را هنگام بارگیری مجدد صفحات پس از رد کردن، مدیریت می کند.

خلاصه

توسعه‌دهندگانی که می‌خواهند به منابع سیستمی دستگاه‌های کاربر خود احترام بگذارند، باید برنامه‌های خود را با در نظر گرفتن حالت‌های چرخه عمر صفحه بسازند. بسیار مهم است که صفحات وب منابع بیش از حد سیستم را در شرایطی که کاربر انتظار ندارد مصرف نکنند.

هرچه توسعه‌دهندگان بیشتر شروع به پیاده‌سازی APIهای جدید Page Lifecycle کنند، برای مرورگرها ایمن‌تر می‌شوند که صفحاتی را که استفاده نمی‌شوند مسدود کرده و دور بیندازند. این بدان معناست که مرورگرها حافظه، CPU، باتری و منابع شبکه کمتری مصرف می کنند که برای کاربران یک پیروزی محسوب می شود.