دعم المتصفح
قد تعلّق المتصفحات الحديثة في بعض الأحيان الصفحات أو تتجاهلها تمامًا عند محدودية موارد النظام. في المستقبل، تريد المتصفّحات إجراء ذلك بشكل استباقي، حتى تستهلك طاقة وذاكرة أقل. توفّر Page Lifecycle API عناصر الجذب خلال مراحل النشاط، ما يتيح لصفحاتك معالجة هذه التدخلات في المتصفّح بأمان بدون التأثير في تجربة المستخدم. اطّلِع على واجهة برمجة التطبيقات لتحديد ما إذا كان عليك تنفيذ هذه الميزات في تطبيقك.
الخلفية
تمثل دورة حياة التطبيق إحدى الطرق الرئيسية التي تدير بها أنظمة التشغيل الحديثة مواردها. على أجهزة Android وiOS وأحدث إصدارات Windows، يمكن للنظام التشغيل بدء التطبيقات و stopped في أي وقت. ويسمح ذلك لهذه المنصات بتبسيط الموارد وإعادة تخصيصها حيث تعود بالفائدة على المستخدم على أفضل وجه.
لم تكن هناك دورة حياة كهذه على الويب في السابق، ويمكن الاحتفاظ بالتطبيقات إلى أجل غير مسمى. مع تشغيل أعداد كبيرة من صفحات الويب، يمكن أن يتم الاشتراك بشكل مفرط في موارد النظام العميقة الأهمية، مثل الذاكرة ووحدة المعالجة المركزية (CPU) والبطارية والشبكة، مما يؤدي إلى تجربة سيئة للمستخدم النهائي.
على الرغم من أنّ منصة الويب كانت تتضمّن منذ فترة طويلة أحداثًا مرتبطة بحالات دورة النشاط
، مثل load
،
unload
،
visibilitychange
، إلا أنّ هذه الأحداث لا تسمح للمطوّرين سوى بالاستجابة لتغييرات حالة دورة النشاط التي يبدأها المستخدم. لكي تعمل الويب
بطريقة موثوقة على الأجهزة ذات الطاقة المنخفضة (وتكون أكثر حرصًا على الموارد بشكل عام على
جميع المنصات)، تحتاج المتصفّحات إلى طريقة لاسترداد موارد النظام
بشكل استباقي وإعادة تخصيصها.
في الواقع، تتّخذ المتصفّحات حاليًا إجراءات نشطة للحفاظ على الموارد للصفحات في علامات التبويب التي تعمل في الخلفية، وتريد العديد من المتصفّحات (خاصةً Chrome) اتخاذ المزيد من الإجراءات لخفض إجمالي استهلاكها للموارد.
تكمن المشكلة في أنّه لا تتوفّر للمطوّرين طريقة للاستعداد لهذه الأنواع من التدخلات التي يبدأها النظام أو حتى معرفة أنّها تحدث. وهذا يعني أنّه يجب أن تكون المتصفّحات حذرة في التعامل مع هذه العناصر، وإلا قد يؤدي ذلك إلى تعطُّل صفحات الويب.
تحاول Page Lifecycle API حلّ هذه المشكلة من خلال:
- تقديم مفهوم حالات مراحل النشاط على الويب وتوحيده.
- تحديد حالات جديدة يبدأها النظام تسمح للمتصفّحات بتقييد الموارد التي يمكن أن تستهلكها علامات التبويب المخفية أو غير النشطة
- إنشاء واجهات برمجة تطبيقات وأحداث جديدة تتيح لمطوّري الويب الاستجابة للانتقالات إلى هذه الحالات الجديدة التي يبدأها النظام والخروج منها
يوفّر هذا الحلّ إمكانية التوقّع التي يحتاجها مطوِّرو البرامج على الويب لإنشاء تطبيقات مرنة مع التدخلات في النظام، كما يتيح للمتصفّحات تحسين موارد النظام بشكل مكثف، ما يعود بالفائدة في النهاية على جميع مستخدمي الويب.
ستتناول بقية هذه المشاركة ميزات "دورة حياة الصفحة" الجديدة وتستكشف كيفية ارتباطها بجميع حالات وأحداث منصة الويب الحالية. وسيقدّم أيضًا اقتراحات وأفضل الممارسات بشأن أنواع العمل الذي يجب أن ينفّذه المطوّرون (وما لا يجب أن ينفّذوه) في كل ولاية.
نظرة عامة على حالات وأحداث دورة حياة الصفحة
جميع حالات مسار المستخدِم للصفحة منفصلة ومتعارضة مع بعضها، ما يعني أنّ الصفحة لا يمكن أن تكون في أكثر من حالة واحدة في الوقت نفسه. ويمكن رصد معظم التغييرات في حالة دورة حياة الصفحة بشكل عام من خلال أحداث DOM (اطّلِع على اقتراحات المطوّرين لكل حالة للاطّلاع على الاستثناءات).
ربما تكون الطريقة الأسهل لشرح حالات مسار المستخدِم على الصفحة، بالإضافة إلى الأحداث التي تشير إلى الانتقالات بينها، هي من خلال مخطّط بياني:
الولايات
يوضّح الجدول التالي كل حالة بالتفصيل. ويسرد أيضًا الحالات المحتمَلة التي يمكن أن تأتي قبلها وبعدها، بالإضافة إلى الأحداث التي يمكن للمطوّرين استخدامها لرصد التغييرات.
الحالة | الوصف |
---|---|
نشِط |
تكون الصفحة في الحالة نشطة إذا كانت مرئية وكانت محطّ تركيز الإدخال.
الحالات السابقة المحتملة: |
غير نشِطة |
تكون الصفحة في الحالة سلبية إذا كانت مرئية ولم يكن عليها تركيز الإدخال.
الحالات السابقة المحتمَلة:
الحالات التالية المحتملة: |
مخفية |
تكون الصفحة في الحالة مخفية إذا لم تكن مرئية (وإذا لم يتم تجميدها أو تجاهلها أو إنهائها).
الحالات السابقة المحتملة:
الحالات التالية المحتمَلة: |
مجمّدة |
في الحالة مجمّدة، يوقف المتصفّح تنفيذ
المهام
القابلة للتجميد في
قوائم المهام للصفحة إلى أن يتم إلغاء تجميد الصفحة. وهذا يعني أنّه لا يتم تنفيذ الإجراءات، مثل موقتات JavaScript واسترجاع عمليات معاودة الاتصال. يمكن إنهاء المهام قيد التشغيل حاليًا (الأهم من ذلك، استدعاء
تجمِّد المتصفّحات الصفحات كطريقة للحفاظ على استخدام وحدة المعالجة المركزية (CPU) أو البطارية أو البيانات، ويهدف ذلك أيضًا إلى إتاحة استخدام التنقّل للخلف أو للأمام بشكل أسرع، ما يتجنّب الحاجة إلى إعادة تحميل الصفحة بالكامل.
الحالات السابقة المحتمَلة:
الحالات التالية المحتمَلة: |
تم إنهاء |
تكون الصفحة في حالة متوقفة بعد أن يبدأ المتصفّح في إلغاء تحميلها وتنظيفها من الذاكرة. لا يمكن بدء أي مهام جديدة في هذه الحالة، وقد يتم إيقاف المهام قيد التنفيذ إذا استغرقت وقتًا طويلاً جدًا.
الحالات السابقة المحتمَلة:
الحالات التالية المحتملة: |
تم التجاهل |
تكون الصفحة في الحالة تم تجاهلها عندما يُلغي المتصفّح تحميلها للحفاظ على الموارد. لا يمكن تنفيذ أي مهام أو عمليات استدعاء للأحداث أو رموز JavaScript من أي نوع في هذه الحالة، لأنّ عمليات الحذف تتم عادةً في ظل قيود على الموارد، حيث يكون بدء عمليات جديدة مستحيلاً. في الحالة مُهمَلة، تكون علامة التبويب نفسها (بما في ذلك عنوان علامة التبويب ورمز الشارة) مرئية للمستخدم عادةً على الرغم من اختفاء الصفحة.
الحالات السابقة المحتمَلة:
الحالات التالية المحتمَلة: |
الفعاليات
تُرسِل المتصفّحات الكثير من الأحداث، ولكن نسبة صغيرة فقط منها تشير إلى احتمال حدوث تغيير في حالة مسار المستخدِم على الصفحة. يوضّح الجدول التالي جميع الأحداث التي تتعلّق بالرحلة ويسرد الحالات التي يمكن أن تنتقل إليها أو منها.
الاسم | التفاصيل |
---|---|
focus
|
عنصر DOM تم التركيز عليه.
ملاحظة: لا يشير حدث
الحالات السابقة المحتمَلة:
الحالات الحالية المحتمَلة: |
blur
|
فقد عنصر DOM التركيز.
ملاحظة: لا يشير حدث
الحالات السابقة المحتمَلة:
الحالات الحالية المحتمَلة: |
visibilitychange
|
تم تغيير قيمة
|
freeze
*
|
تم تجميد الصفحة للتو. ولن يبدأ أي مهمة قابلة للتجميد في قوائم انتظار المهام الخاصة بالصفحة.
الحالات السابقة المحتمَلة:
الحالات الحالية المحتمَلة: |
resume
*
|
استأنف المتصفّح عرض صفحة مجمّدة.
الحالات السابقة المحتمَلة:
الحالات الحالية المحتمَلة: |
pageshow
|
يتم الانتقال إلى إدخال في سجلّ الجلسات. يمكن أن تكون عملية تحميل صفحة جديدة تمامًا أو صفحة مأخوذة من التخزين المؤقت للصفحات. إذا تم أخذ الصفحة
من ذاكرة التخزين المؤقت للصفحات، تكون قيمة سمة
الحالات السابقة المحتمَلة: |
pagehide
|
يتمّ التنقّل من إدخال في سجلّ الجلسات. إذا كان المستخدم ينتقل إلى صفحة أخرى وكان المتصفّح قادرًا على إضافة
الصفحة الحالية إلى ذاكرة التخزين المؤقت للرجوع/الانتقال إلى الأمام
لإعادة استخدامها لاحقًا، تكون قيمة السمة
الحالات السابقة المحتمَلة:
الحالات الحالية المحتمَلة: |
beforeunload
|
سيتم قريبًا إلغاء تحميل النافذة والمستند وموارده. سيظل المستند مرئيًا وسيظل بإمكانك إلغاء الحدث في هذه المرحلة.
ملاحظة مهمة: يجب استخدام حدث
الحالات السابقة المحتمَلة:
الحالات الحالية المحتمَلة: |
unload
|
يتم تفريغ الصفحة.
تحذير:
لا يُنصح أبدًا باستخدام الحدث
الحالات السابقة المحتمَلة:
الحالات الحالية المحتمَلة: |
* يشير إلى حدث جديد محدّد من خلال Page Lifecycle API
الميزات الجديدة التي تمت إضافتها في الإصدار 68 من Chrome
يعرض الرسم البياني السابق حالتَين يبدأهما النظام بدلاً من المستخدم: مجمّد ومُهمَل. كما ذكرنا سابقًا، تعمل المتصفّحات حاليًا على تجميد علامات التبويب المخفية وأحيانًا تجاهلها (وفقًا لتقديرها)، ولكن لا تتوفّر للمطوّرين طريقة لمعرفة متى يحدث ذلك.
في الإصدار 68 من Chrome، يمكن للمطوّرين الآن رصد حالات تجميد علامة تبويب مخفية وتفعيلها مجددًا من خلال الاستماع إلى حدثَي freeze
وresume
في document
.
document.addEventListener('freeze', (event) => {
// The page is now frozen.
});
document.addEventListener('resume', (event) => {
// The page has been unfrozen.
});
اعتبارًا من الإصدار 68 من Chrome، يتضمّن عنصر document
الآن سمة
wasDiscarded
في متصفّح Chrome للكمبيوتر المكتبي (يتم تتبُّع توافق Android في هذه المشكلة). لتحديد ما إذا تم تجاهل صفحة أثناء الانتقال إلى علامة تبويب مخفية، يمكنك فحص قيمة هذه السمة في وقت تحميل الصفحة (ملاحظة: يجب إعادة تحميل الصفحات المرفوضة لاستخدامها مرة أخرى).
if (document.wasDiscarded) {
// Page was previously discarded by the browser while in a hidden tab.
}
للحصول على نصائح حول الإجراءات المهمة التي يجب اتّخاذها في حدثَي freeze
وresume
، بالإضافة إلى كيفية التعامل مع الصفحات التي يتم تجاهلها والاستعداد لذلك، يمكنك الاطّلاع على
اقتراحات المطوّرين لكل حالة.
تقدّم الأقسام المتعدّدة التالية نظرة عامة حول كيفية ملاءمة هذه الميزات الجديدة للحالات والأحداث الحالية لمنصّات الويب.
كيفية مراقبة حالات مراحل نشاط الصفحة في الرمز
في الحالات النشطة والسلبية والمخفية، من الممكن تشغيل رمز JavaScript الذي يحدّد حالة مراحل نشاط الصفحة الحالية من واجهات برمجة التطبيقات الحالية الخاصة بالنظام الأساسي للويب.
const getState = () => {
if (document.visibilityState === 'hidden') {
return 'hidden';
}
if (document.hasFocus()) {
return 'active';
}
return 'passive';
};
على صعيدٍ آخر، لا يمكن رصد حالتَي توقُّف وإنهاء إلا في أداة معالجة الحدث المعنيّة
(freeze
وpagehide
) أثناء تغيُّر الحالة.
كيفية رصد تغييرات الحالة
استنادًا إلى دالة getState()
المحدّدة سابقًا، يمكنك رصد جميع التغييرات في حالة Page
Lifecycle باستخدام الرمز البرمجي التالي.
// 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 |
إنّ الحالة نشط هي الوقت الأكثر أهمية بالنسبة إلى المستخدم، وبالتالي هو الوقت الأكثر أهمية لصفحتك لتكون سريعة الاستجابة لإدخالات المستخدم. يجب إزالة الأولوية من أي عمل غير واجهة المستخدم قد يؤدي إلى حظر السلسلة الرئيسية وتحديد وقت الخمول له أو نقله إلى عامل ويب. |
Passive |
في الحالة السلبية، لا يتفاعل المستخدم مع الصفحة، ولكن يظل بإمكانه رؤيتها. وهذا يعني أنّ تعديلات واجهة المستخدم والرسومات المتحركة يجب أن تظل سلسة، ولكن توقيت حدوث هذه التعديلات أقل أهمية. عند تغيير الصفحة من نشطة إلى سلبية، حان الوقت للاحتفاظ بحالة التطبيق غير المحفوظة. |
عندما تتغيّر الحالة من سلبية إلى مخفية، من المحتمل ألا يتفاعل المستخدم معها مرة أخرى إلى أن تتم إعادة تحميلها. غالبًا ما يكون الانتقال إلى الحالة مخفي هو أيضًا آخر تغيير في الحالة
يمكن للمطوّرين رصده بشكل موثوق (ينطبق ذلك بشكل خاص على
الأجهزة الجوّالة، لأنّه يمكن للمستخدمين إغلاق علامات التبويب أو تطبيق المتصفّح نفسه، ولا يتم بدء أحداث
وهذا يعني أنّه عليك التعامل مع الحالة مخفي على أنّها نهاية محتمله لجلسة المستخدم. بعبارة أخرى، يجب الاحتفاظ بأي حالة تطبيق لم يتم حفظها وإرسال أي بيانات تحليلية لم يتم إرسالها. يجب أيضًا إيقاف إجراء تعديلات على واجهة المستخدم (لأنّ المستخدم لن يراها)، ويجب إيقاف أي مهام لا يريد المستخدم تنفيذها في الخلفية. |
|
Frozen |
في الحالة مجمّدة، يتم تعليق المهام القابلة للتجميد في قوائم انتظار المهام إلى أن يتم إلغاء تجميد الصفحة، وهو ما قد لا يحدث أبدًا (مثلاً إذا تم تجاهل الصفحة). وهذا يعني أنّه عند تغيير حالة الصفحة من مخفية إلى مجمّدة، من الضروري إيقاف أيّ أدوات توقيت أو إلغاء أيّ عمليات ربط، لأنّها في حال تجميدها قد تؤثّر في علامات التبويب الأخرى المفتوحة في المصدر نفسه، أو قد تؤثّر في قدرة المتصفّح على وضع الصفحة في ذاكرة التخزين المؤقت للرجوع/التقديم. وعلى وجه الخصوص، من المهم أن:
يجب أيضًا الاحتفاظ بأي حالة عرض ديناميكية (مثل موضع التمرير
في عرض قائمة لا نهائية) في
إذا عادت الصفحة من الحالة مجمّدة إلى الحالة مخفية، يمكنك إعادة فتح أيّ اتصالات مغلقة أو إعادة بدء أيّ عمليات استطلاع stopped عند تجميد الصفحة في البداية. |
Terminated |
بشكل عام، ليس عليك اتّخاذ أي إجراء عند انتقال صفحة إلى الحالة انتهت. بما أنّ الصفحات التي يتمّ إلغاء تحميلها نتيجةً لفعل المستخدم تمرّ دائمًا بالحالة مخفية قبل الدخول إلى الحالة منتهية، فإنّ الحالة مخفية هي الحالة التي يجب فيها تنفيذ منطق إنهاء الجلسة (مثل الحفاظ على حالة التطبيق وإعداد التقارير في "إحصاءات Google"). بالإضافة إلى ذلك، (كما ذكرنا في اقتراحات الحالة مخفية)، من المهم جدًا أن يدرك المطوّرون أنّه لا يمكن رصد عملية الانتقال إلى الحالة تم الإنهاء بشكل موثوق في العديد من الحالات (لا سيما على الأجهزة الجوّالة)، لذا من المرجَّح أن يفقد المطوّرون الذين يعتمدون على أحداث الإنهاء (مثل |
Discarded |
لا يمكن للمطوّرين ملاحظة الحالة مُهمَلة في أثناء تجاهل الصفحة. ويعود السبب في ذلك إلى أنّه يتم عادةً تجاهل الصفحات بسبب قيود الموارد، ولا يمكن ببساطة في معظم الحالات إلغاء تجميد صفحة للسماح بتشغيل النص البرمجي استجابةً لحدث تجاهل. نتيجةً لذلك، يجب الاستعداد لاحتمالية تجاهل التغييرات في
التغيير من مخفي إلى مجمّد، وبعد ذلك يمكنك
التفاعل مع استعادة صفحة تم تجاهلها في وقت تحميل الصفحة من خلال
وضع علامة في المربّع |
مرة أخرى، بما أنّ موثوقية أحداث دورة الحياة وترتيبها لا يتم تنفيذهما باستمرار في جميع المتصفّحات، فإنّ أسهل طريقة لاتّباع النصيحة الواردة في الجدول هي استخدام PageLifecycle.js.
واجهات برمجة التطبيقات القديمة لمراحل النشاط التي يجب تجنُّبها
يجب تجنب الأحداث التالية متى أمكن ذلك.
حدث Unload
يتعامل العديد من مطوّري البرامج مع حدث unload
على أنّه استدعاء مضمون ويستخدمه كإشارة نهاية الجلسة لحفظ الحالة وإرسال بيانات الإحصاءات، إلا أنّ إجراء ذلك غير موثوق للغاية، لا سيما على الأجهزة الجوّالة. لا يتم تنشيط حدث unload
في العديد من حالات إلغاء التحميل المعتادة، بما في ذلك إغلاق علامة تبويب من مبدِّل علامات التبويب على الأجهزة الجوّالة أو إغلاق تطبيق المتصفّح من مبدّل التطبيقات.
لهذا السبب، من الأفضل دائمًا الاعتماد على حدث
visibilitychange
لتحديد وقت انتهاء جلسة، والنظر في الحالة المخفية على أنّها
آخر وقت موثوق به لحفظ بيانات التطبيق والمستخدم.
بالإضافة إلى ذلك، يمكن أن يؤدي مجرد توفّر معالِج حدث unload
مسجَّل (من خلال
onunload
أو addEventListener()
) إلى منع المتصفّحات من
وضع الصفحات في التخزين المؤقت للصفحات لتحميل الصفحات بشكلٍ أسرع
للرجوع والتقديم.
في جميع المتصفّحات الحديثة، ننصحك باستخدام الحدث
pagehide
دائمًا لرصد عمليات إزالة التحميل المحتمَلة للصفحات (المعروفة أيضًا باسم الحالة
terminated) بدلاً من الحدث unload
. إذا
كنت بحاجة إلى إتاحة الإصدار 10 من Internet Explorer والإصدارات الأقدم، عليك رصد ميزة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
يواجه حدث 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
يحذّر عند إلغاء تحميل الصفحة.
في الوقت الحالي، سيتّخذ Chrome إجراءات متحفظة عند تجاهل الصفحات، ولن يفعل ذلك إلا عندما يكون متأكدًا من أنّ ذلك لن يؤثر في المستخدمين. على سبيل المثال، لن يتم تجاهل الصفحات التي تم رصدها وهي تُجري أيًا مما يلي أثناء حالتها المخفية، ما لم تكن تواجه قيودًا شديدة على الموارد:
- يتم تشغيل الصوت
- استخدام WebRTC
- تعديل عنوان الجدول أو رمزه المفضّل
- عرض التنبيهات
- إرسال إشعارات فورية
للاطّلاع على ميزات القائمة الحالية المستخدَمة لتحديد ما إذا كان يمكن تجميد علامة تبويب أو تجاهلها بأمان، يُرجى الاطّلاع على المقالة الإجراءات الاستكشافية للتجميد والتجاهل في Chrome.
ما هي ميزة "التخزين المؤقت للصفحات"؟
التخزين المؤقت للصفحات هو مصطلح يُستخدَم لوصف ميزة تحسين التنقل التي تطبّقها بعض المتصفّحات، ما يجعل استخدام زرَّي الرجوع والانتقال إلى الخطوة التالية أسرع.
عندما ينتقل المستخدم بعيدًا عن صفحة، تجمِّد هذه المتصفّحات نسخة من تلك
الصفحة حتى يمكن استئنافها بسرعة في حال تنقّل المستخدم للخلف باستخدام زرَّي
الرجوع أو التقديم. تذكَّر أنّ إضافة unload
معالج أحداث يمنع إمكانية إجراء هذا التحسين.
من جميع النواحي، يشبه هذا التجميد من الناحية الوظيفية الإجراء الذي تتّخذه المتصفّحات لتجميد التطبيقات بهدف الحفاظ على وحدة المعالجة المركزية أو البطارية، ولهذا السبب، يُعدّ جزءًا من حالة مجمّد في دورة الحياة.
إذا لم أتمكّن من تشغيل واجهات برمجة التطبيقات غير المتزامنة في الحالات "مجمّد" أو "منتهي"، كيف يمكنني حفظ البيانات في IndexedDB؟
في الحالتَين "مجمّد" و"منتهي"، يتم تعليقالمهام القابلة للتجميد في قوائم المهام للصفحة، ما يعني أنّه لا يمكن استخدام واجهات برمجة التطبيقات غير المتزامنة والمستندة إلى طلبات إعادة الاتصال، مثل IndexedDB بشكل موثوق.
في المستقبل، سنضيف طريقة commit()
إلى عناصر IDBTransaction
، ما سيمنح المطوّرين طريقة لإجراء معاملات للكتابة فقط بشكل فعّال
لا تتطلّب عمليات استدعاء. بعبارة أخرى، إذا كان المطوِّر يكتب
البيانات فقط في IndexedDB ولا يُجري معاملة معقّدة تتألف من عمليات قراءة
وكتابة، سيتمكّن الأسلوب commit()
من الانتهاء قبل تعليق قوائم المهام
(على افتراض أنّ قاعدة بيانات IndexedDB مفتوحة).
بالنسبة إلى الرموز البرمجية التي يجب أن تعمل اليوم، يتوفّر للمطوّرين خياران:
- استخدام ميزة "تخزين الجلسة": تخزين الجلسة هو إجراء متزامن ويتم الاحتفاظ به عند تجاهل الصفحات.
- استخدام IndexedDB من خدمة Worker: يمكن لخدمة Worker تخزين البيانات في IndexedDB بعد إغلاق الصفحة أو تجاهلها. في مستمع أحداث
freeze
أوpagehide
، يمكنك إرسال البيانات إلى عامل الخدمة من خلالpostMessage()
، ويمكن لعامل الخدمة التعامل مع حفظ البيانات.
اختبار تطبيقك في حالتَي التجميد والتجاهل
لاختبار أداء تطبيقك في حالتَي التجميد والتجاهل، يمكنك الانتقال إلى
chrome://discards
لتجميد أيّ من علامات التبويب المفتوحة أو تجاهلها.
يتيح لك ذلك التأكّد من أنّ صفحتك تعالج بشكلٍ صحيح حدثَي freeze
وresume
بالإضافة إلى علامة document.wasDiscarded
عند إعادة تحميل الصفحات بعد
التخلص منها.
ملخّص
على مطوّري التطبيقات الذين يريدون احترام موارد النظام الخاصة بأجهزة المستخدمين إنشاء تطبيقاتهم مع وضع حالات "دورة حياة الصفحة" في الاعتبار. من المهم أن لا تستهلك صفحات الويب موارد النظام بشكل مفرط في حالات لا يتوقعها المستخدم.
وكلما زاد عدد المطوّرين الذين بدأوا في تنفيذ واجهات برمجة التطبيقات الجديدة لمراحل نشاط الصفحة، زادت أمان عملية تجميد المتصفّحات للصفحات غير المستخدَمة ورفضها. ويعني ذلك أنّ المتصفّحات ستستهلك موارد أقل من الذاكرة ووحدة المعالجة المركزية (CPU) والبطارية والشبكة، وهو ما يصبّ في مصلحة المستخدمين.