عرض المسارات مسبقًا باستخدام ميزة "تفاعل الانطباق"

هل العرض من جهة الخادم ليس من جهة الخادم ولكنّك تريد تسريع أداء موقعك الإلكتروني في React؟ تجربة العرض المسبق

react-snap هي مكتبة تابعة لجهة خارجية تعرض مسبقًا صفحات على موقعك الإلكتروني في شكل ملفات HTML ثابتة. ويمكن أن يؤدي هذا إلى تحسين مرات "الطلاء الأول" في تطبيقك.

في ما يلي مقارنة بين التطبيق نفسه مع ميزة العرض المُسبَق والتي تم تحميلها على اتصال 3G وجهاز جوّال تمت محاكاته أو بدونه:

مقارنة بين عمليات التحميل جنبًا إلى جنب ويتم تحميل الإصدار الذي يستخدم العرض المسبق بسرعة 4.2 ثوانٍ.

لماذا يُعدّ ذلك مفيدًا؟

تكمن مشكلة الأداء الرئيسية للتطبيقات الكبيرة المكوّنة من صفحة واحدة في أنّ المستخدم يحتاج إلى انتظار انتهاء تنزيل حِزم JavaScript التي تشكِّل الموقع الإلكتروني ليتمكن من رؤية أي محتوى حقيقي. كلما زاد حجم الحزم، طالت مدة انتظار المستخدم.

لحل هذه المشكلة، يتبع العديد من المطوّرين أسلوب عرض التطبيق على الخادم بدلاً من تشغيله في المتصفّح فقط. ومع كل عملية انتقال للصفحة أو المسار، يتم إنشاء رمز HTML كاملاً على الخادم وإرساله إلى المتصفّح، ما يقلّل من سرعة "سرعة عرض أول" ولكن سيكون أقل من وقت تحميل "أول بايت".

والعرض المُسبَق هو أسلوب منفصل أقل تعقيدًا من العرض على الخادم، ولكنه يوفّر أيضًا طريقة لتحسين مرات "سرعة عرض أول مرة" في تطبيقك. يتم استخدام متصفِّح بلا واجهة مستخدم رسومية، أو متصفِّح بدون واجهة مستخدم، لإنشاء ملفات HTML ثابتة لكل مسار خلال وقت الإصدار. ويمكن بعد ذلك شحن هذه الملفات مع حِزم JavaScript اللازمة للتطبيق.

وصلة تفاعلية

يستخدم react-snap أداة Puppeteer لإنشاء ملفات HTML معروضة مسبقًا لمسارات مختلفة في تطبيقك. للبدء، قم بتثبيتها كتبعية للتطوير:

npm install --save-dev react-snap

أضِف بعد ذلك النص البرمجي postbuild في package.json:

"scripts": {
  //...
  "postbuild": "react-snap"
}

سيؤدي ذلك إلى تشغيل الأمر react-snap تلقائيًا في كل مرة يتم فيها إنشاء إصدار جديد من التطبيقات التي تم إنشاؤها (npm build).

آخر شيء سيتعين عليك القيام به هو تغيير طريقة تشغيل التطبيق. غيِّر ملف src/index.js إلى ما يلي:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';

ReactDOM.render(<App />, document.getElementById('root'));
const rootElement = document.getElementById("root");

if (rootElement.hasChildNodes()) {
  ReactDOM.hydrate(<App />, rootElement);
} else {
  ReactDOM.render(<App />, rootElement);
}

بدلاً من استخدام ReactDOM.render فقط لعرض عنصر React الجذري مباشرةً في DOM، يتحقّق هذا الإجراء لمعرفة ما إذا كانت هناك عُقد فرعية متوفّرة لتحديد ما إذا كان قد تم عرض محتوى HTML مسبقًا (أو تم عرضه على الخادم). وفي هذه الحالة، يتم استخدام ReactDOM.hydrate بدلاً من ذلك لإرفاق أدوات استماع الأحداث إلى ملف HTML الذي سبق إنشاؤه بدلاً من إنشائه من جديد.

سيؤدي إنشاء التطبيق الآن إلى إنشاء ملفات HTML ثابتة كحمولات لكل مسار يتم الزحف إليه. يمكنك الاطّلاع على شكل حمولة HTML من خلال النقر على عنوان URL لطلب HTML ثم النقر على علامة التبويب المعاينات في "أدوات مطوري البرامج في Chrome".

المقارنة قبل وبعد. تُظهر اللقطة اللاحقة المحتوى لعرضه.

وميض من محتوى غير نمطي

على الرغم من عرض HTML الثابت الآن بشكل فوري تقريبًا، إلا أنه لا يزال بدون نمط بشكل افتراضي مما قد يتسبب في ظهور "وميض لمحتوى غير ذي نمط" (FOUC). ويمكن ملاحظة ذلك بشكل خاص إذا كنت تستخدم مكتبة CSS-in-JS لإنشاء أدوات اختيار، حيث يجب أن تنتهي حزمة JavaScript من التنفيذ قبل تطبيق أي أنماط.

للمساعدة في منع حدوث ذلك، يمكن تضمين محتوى CSS المهم أو الحدّ الأدنى من حجم CSS اللازم لعرض الصفحة الأولية مباشرةً إلى <head> في مستند HTML. يستخدم react-snap مكتبة أخرى تابعة لجهة خارجية، minimalcss، لاستخراج أي ملفات CSS مهمة للمسارات المختلفة. يمكنك تفعيل هذا الخيار من خلال تحديد ما يلي في ملف package.json:

"reactSnap": {
  "inlineCss": true
}

بعد الاطّلاع على معاينة الردّ في "أدوات مطوري البرامج في Chrome"، سيتم الآن عرض صفحة ذات نمط معيّن يتضمّن محتوى CSS مهمًّا.

المقارنة قبل وبعد. تعرض اللقطة اللاحقة المحتوى وقد تم تصميمه باستخدام CSS الأساسي المضمّن.

الخلاصة

إذا لم تكن مسارات العرض من جهة الخادم في تطبيقك، يمكنك استخدام react-snap لعرض محتوى HTML ثابت مسبقًا للمستخدمين.

  1. قم بتثبيتها كتبعية للتطوير والبدء بالإعدادات الافتراضية فقط.
  2. ويمكنك استخدام خيار inlineCss التجريبي لتضمين محتوى CSS المهم إذا كان مناسبًا لموقعك الإلكتروني.
  3. إذا كنت تستخدم تقسيم الرمز على مستوى المكونات في أي مسارات، احرص على عدم عرض حالة تحميل مسبقًا للمستخدمين. يتناول react-snap الملف التمهيدي هذا الموضوع بمزيد من التفصيل.