التبديل إلى Next.js والإسراع في تحميل الصفحة الرئيسية لـ manifold.co 7.5 مرة

ننشر اليوم ترجمة للقصة حول كيفية تسريع الانتقال من React Boilerplate إلى Next.js ، وهو إطار لتطوير تطبيقات الويب التقدمية القائمة على React ، في تحميل الصفحة الرئيسية لمشروع manifold.co 7.5 مرة. لم يتم إجراء أي تغييرات أخرى على المشروع ، وقد تبين أن هذا الانتقال ، بشكل عام ، غير مرئي تمامًا لأجزاء أخرى من النظام. ما تبين في النهاية تبين أنه أفضل من المتوقع.



نظرة عامة على النتائج


في الواقع ، يمكننا أن نقول أن الانتقال إلى Next.js أعطانا شيئًا مثل "زيادة إنتاجية المشروع التي خرجت من العدم". إليك ما يبدو عليه وقت تحميل المشروع عند استخدام موارد الأجهزة المختلفة واتصالات الشبكة.
اتصال
وحدة المعالجة المركزية
إلى ثواني
بعد ثواني
تحسين ٪
سريع (200 ميغابت في الثانية)
سريع
1.5
0.2
750
متوسطة (3G)
سريع
5.6
1.1
500
متوسطة (3G)
متوسطة
7.5
1.3
570
بطيء (اتصال 3G بطيء)
متوسطة
22
4
550

عند استخدام اتصال سريع وجهاز مع معالج سريع ، انخفض وقت تحميل الموقع من 1.5 ثانية. يصل إلى 0.2 ثانية ، وهذا هو ، تحسن هذا المؤشر 7.5 مرات. على اتصال متوسط ​​الجودة وعلى جهاز متوسط ​​الأداء ، انخفض وقت تحميل الموقع من 7.5 ثانية. حتى 1.3 ثانية

ماذا يحدث بعد قيام المستخدم بالنقر فوق عنوان URL؟


لفهم ميزات عمل تطبيقات الويب التقدمية (تطبيق التقدم على شبكة الإنترنت ، PWA) ، تحتاج أولاً إلى فهم ما يحدث بين اللحظة التي ينقر فيها المستخدم على عنوان URL (على عنوان موقع الويب الخاص بنا) واللحظة التي يرى فيها شيئًا ما في نافذة متصفح (في هذه الحالة ، تطبيق React لدينا).


مراحل التطبيق

النظر في المراحل 5 من العمل مع التطبيق ، ويرد الرسم البياني أعلاه.

  1. يذهب المستخدم إلى عنوان URL ، ويقوم النظام باكتشاف عنوان الخادم باستخدام DNS والوصول إلى الخادم. كل هذا يتم بسرعة كبيرة ، وعادة ما يستغرق أقل من 100 مللي ثانية ، ولكن هذه الخطوة تستغرق بعض الوقت ، وهذا هو سبب ذكرها هنا.
  2. يعرض الخادم الآن رمز HTML للصفحة ، ولكن تظل الصفحة في المتصفح فارغة حتى يتم تحميل الموارد اللازمة لعرضه (ما لم يتم تحميل الموارد بشكل غير متزامن ). في الواقع ، يتم اتخاذ المزيد من الإجراءات في هذه المرحلة مما هو موضح في الرسم التخطيطي ، ولكن المراجعة المشتركة لجميع هذه العمليات ستناسبنا.
  3. بعد تحميل رمز HTML وأهم الموارد ، يبدأ المتصفح في عرض ما يمكنه عرضه ، مع الاستمرار في تحميل كل شيء آخر (الصور ، على سبيل المثال) في الخلفية. هل سبق لك أن تساءلت عن سبب "فجأة" الصور في بعض الأحيان بشكل مفاجئ على صفحة بشكل أسرع من اللازم ، وأحيانًا يتم تحميلها لفترة طويلة؟ هذا هو بالضبط سبب حدوث ذلك. يتيح لك هذا النهج إنشاء صفحة منتهية بسرعة.
  4. لا يمكن تحليل شفرة JavaScript وتنفيذها إلا بعد تحميلها. اعتمادًا على حجم رمز JS المستخدم في الصفحة (وقد يكون هذا ، بالنسبة لتطبيق React نموذجي ، كبير جدًا إذا تم حزم الكود في ملف واحد) فقد يستغرق الأمر عدة ثوانٍ أو أكثر (لاحظ أن JS لا يحتاج الرمز ، من أجل البدء في التنفيذ ، انتظر تحميل جميع الموارد الأخرى ، على الرغم من حقيقة أنه في الرسم البياني يبدو تمامًا مثل ذلك).
  5. في حالة تطبيق React ، تأتي اللحظة الآن عندما يعدل الرمز DOM ، مما يجعل المتصفح يعيد رسم الصفحة المعروضة بالفعل. ثم تبدأ دورة تحميل مورد آخر. يعتمد الوقت الذي تستغرقه هذه الخطوة على مدى تعقيد الصفحة.

أسرع ، كان ذلك أفضل.


نظرًا لأن تطبيق الويب التدريجي يأخذ كود React وينتج كود HTML و CSS ثابتًا ، فهذا يعني أن المستخدم يرى تطبيق React بالفعل في الخطوة 3 من المخطط أعلاه ، وليس في الخطوة 5. في اختباراتنا ، يستغرق هذا من 0.2 إلى 4 ثوانٍ ، وهذا يعتمد على سرعة اتصال المستخدم بالإنترنت وجهازه. هذا أفضل بكثير من السابق 1.5-22 ثانية. تطبيقات الويب المتقدمة هي طريقة موثوقة لتقديم تطبيقات React أسرع للمستخدم.

السبب في أن تطبيقات الويب التقدمية والأطر ذات الصلة مثل Next.js لا تزال غير شائعة للغاية ، لأن أطر JS التقليدية غير ناجحة بشكل خاص في إنشاء كود HTML ثابت. اليوم ، تغير كل شيء كثيرًا بسبب حقيقة أن الأطر مثل React و Vue و Angular ، وغيرها ، تتمتع بدعم ممتاز للتقديم من جانب الخادم. ومع ذلك ، من أجل استخدام هذه الأدوات ، لا تزال بحاجة إلى فهم عميق لميزات عمل أدوات التجميع وأدوات بناء المشاريع. العمل مع كل هذا لا يخلو من المشاكل.

كان ظهور أطر عمل PWA مؤخرًا مثل Next.js و Gatsby (ظهر كلاهما في أواخر عام 2016 - أوائل عام 2017) خطوة جادة نحو اعتماد واسع النطاق لـ PWA نظرًا لانخفاض حواجز الدخول وبسبب حقيقة أنه جعل استخدام هذه الأطر مهمة بسيطة وممتعة.

على الرغم من أنه لا يمكن نقل كل تطبيق إلى Next.js ، فبالنسبة للعديد من تطبيقات React ، يعني هذا الانتقال نفس "الأداء من أي مكان" الذي نتحدث عنه هنا ، مع استكمال استخدام أكثر كفاءة لموارد الشبكة.

ما مدى صعوبة الانتقال إلى Next.js؟


بشكل عام ، يمكن الإشارة إلى أن ترجمة صفحتنا الرئيسية إلى Next.js لم تكن صعبة للغاية. ومع ذلك ، واجهنا بعض الصعوبات التي نتجت عن ميزات بنية تطبيقنا.

▍ رفض جهاز التوجيه التفاعلي


اضطررنا إلى التخلي عن جهاز التوجيه React لأن Next.js لديه جهاز التوجيه المدمج به ، والذي يتم دمجه بشكل أفضل مع التحسينات المتعلقة بفصل الشفرة التي يتم إجراؤها أعلى بنية PWA. يسمح هذا لجهاز التوجيه بتوفير تحميل صفحة أسرع بكثير مما تتوقعه من أي جهاز توجيه من جانب العميل.

جهاز توجيه Next.js عبارة عن جهاز توجيه React عالي السرعة ، لكنه لا يزال غير جهاز توجيه React.

في الممارسة العملية ، نظرًا لأننا لم نستفيد من الميزات المتقدمة بشكل خاص التي يوفرها جهاز توجيه React ، فإن الانتقال إلى جهاز توجيه Next.js بالنسبة لنا كان ببساطة استبدال مكون جهاز توجيه React القياسي بمكون Next.js المقابل:

/*   ( React) */ <Link to="/my/page">  A link </Link> /*   ( Next.js) */ <Link href="/my/page" passHref>  <a>    A link  </a> </Link> 

بشكل عام ، تبين أن كل شيء ليس سيئًا للغاية. كان علينا إعادة تسمية العقار وإضافة علامة لأغراض عرض الخادم. نظرًا لأننا استخدمنا أيضًا مكتبة styled-components ، فقد اتضح أننا في معظم الحالات نحتاج إلى إضافة خاصية passHref لضمان سلوك النظام بطريقة تشير href دائمًا إلى العلامة المنشأة.


طلبات الشبكة لـ manifold.co

لكي ترى بأم عينيك تحسين جهاز توجيه Next.js أثناء العمل ، افتح علامة تبويب الشبكة الخاصة بأدوات مطور المتصفح من خلال عرض صفحة manifold.co وانقر فوق بعض الارتباطات. يوضح الشكل السابق نتيجة النقر فوق ارتباط /services . كما ترى ، فإنه يؤدي إلى تنفيذ طلب لتحميل services.js بدلاً من تنفيذ طلب منتظم.

أنا لا أتحدث فقط عن التوجيه من جانب العميل ؛ جهاز التوجيه React مناسب أيضًا لحل هذه المشكلة. أنا أتحدث عن جزء حقيقي من شفرة جافا سكريبت التي تم استخراجها من بقية الشفرة وتحميلها عند الطلب. يتم ذلك باستخدام معيار Next.js. وهذا أفضل بكثير مما كان لدينا من قبل. وهي نتحدث عن حزمة كبيرة من JS-code بحجم 1.7 ميغابايت ، والتي كان على العميل ، قبل أن يتمكن من رؤية شيء ما ، تنزيلها ومعالجتها.

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

of ميزات استخدام Redux


متابعة موضوع الصعوبات المرتبطة بالانتقال إلى Next.js ، يمكن الإشارة إلى أن جميع التحسينات المثيرة للاهتمام التي يعرضها Next.js للتطبيق لها تأثير معين على هذا التطبيق. وهي ، بما أن Next.js ينفذ الفصل بين الشفرة على مستوى الصفحة ، فإنه يمنع المطور من الوصول إلى مكون React root أو طريقة التقديم () الخاصة بمكتبة rea react-dom . إذا كنت مشتركًا بالفعل في تكوين Redux ، فيمكنك ملاحظة أن كل هذا يخبرنا أنه من أجل التشغيل المعتاد مع Redux ، نحتاج إلى حل المشكلة ، وهو أنه ليس من الواضح تمامًا مكان البحث عن Redux.

يوفر Next.js مكوّنًا خاصًا ذو ترتيب withRedux ، withRedux ، والذي withRedux بمثابة مجمّع لجميع مكونات المستوى الأعلى في كل صفحة:

 export default withRedux(HomePage); 

على الرغم من أن كل هذا ليس سيئًا للغاية ، ولكن إذا كنت بحاجة إلى أساليب createStore() ، مثل عند استخدام محاقن تخفيض الإعادة ، تتوقع أنك ستحتاج إلى وقت إضافي لتصحيح المغلف (وبالمناسبة ، لا تحاول أبدًا استخدام شيء مثل redux-reducer-injectors ).

بالإضافة إلى ذلك ، نظرًا لأن Redux أصبح الآن "مربعًا أسودًا" ، فإن استخدام المكتبة غير القابلة للتغيير يصبح مشكلة. على الرغم من حقيقة أن Immutable ستعمل مع Redux تبدو واضحة تمامًا ، إلا أنني واجهت مشكلة. لذلك ، إما أن حالة المستوى الأعلى لم تكن ثابتة ( get is not a function خطأ في get is not a function ) ، أو حاول المكون المجمع استخدام تدوين النقاط للعمل مع كائنات JS بدلاً من طريقة .get() ( Can't get catalog of undefined لخطأ Can't get catalog of undefined ). لتصحيح هذه المشكلة ، اضطررت إلى الرجوع إلى التعليمات البرمجية المصدر. بعد كل شيء ، Next.js يفرض على المطور استخدام آلياته الخاصة لسبب ما.

بشكل عام ، يمكن الإشارة إلى أن المشكلة الرئيسية المرتبطة بـ Next.js هي أن القليل جدًا في هذا الإطار تم توثيقه جيدًا. هناك العديد من الأمثلة في الوثائق التي يمكنك على أساسها إنشاء شيء خاص بك ، ولكن إذا لم يكن بينها مثال يعكس ميزات مشروعك ، فيمكنك فقط التوفيق.

et إحضار الرفض


استخدمنا مكتبة react-inlinesvg ، التي توفر خيارات التصميم لصور SVG المدمجة والتخزين المؤقت للاستعلام. ولكن هنا واجهنا مشكلة واحدة: عند إجراء تقديم الخادم ، لا يوجد شيء مثل طلبات XHR (على الأقل ليس بمعنى عناوين URL التي تم إنشاؤها بواسطة Webpack ، كما قد تتوقع). محاولات لتنفيذ مثل هذه الطلبات تتداخل مع تقديم الخادم.

على الرغم من وجود مكتبات أخرى للعمل مع بيانات SVG المضمنة التي تدعم SSR ، فقد قررت التخلي عن هذه الميزة ، حيث أن ملفات SVG كانت نادراً ما تستخدم. إما أن استبدلتهم بصور عادية وعلامات <img> ، إذا لم أكن بحاجة إلى التصميم عند عرض الصور المطابقة ، أو قمت بدمجها في الكود في شكل React JSX. ربما ، أصبح كل شيء على ما يرام ، نظرًا لأن الرسوم التوضيحية JSX تضغط الآن على المتصفح عندما تم تحميل الصفحة لأول مرة ، وكانت حزمة JS المرسلة إلى العميل بها مكتبة واحدة أقل.

إذا كنت بحاجة إلى استخدام آليات تحميل البيانات (كنت بحاجة لهذه الميزة لمكتبة أخرى) ، فيمكنك تكوين هذا باستخدام next.config.js ، باستخدام whatwg-fetch و node-fetch :

 module.exports = { webpack: (config, options) =>   Object.assign(config, {     plugins: config.plugins.concat([       new webpack.ProvidePlugin(         config.isServer           ? {}           : { fetch: 'imports-loader?this=>global!exports-loader?global.fetch!whatwg-fetch' }       ),     ]),   resolve: Object.assign(config.resolve, {     alias: Object.assign(       config.resolve.alias,       config.isServer ? {} : { fetch: 'node-fetch' }     ),   }), }), }; 

▍ العميل والخادم JS


الميزة الأخيرة لـ Next.js ، والتي أود أن أذكرها هنا ، هي أن هذا الإطار تم إطلاقه مرتين - مرة واحدة للخادم ، ومرة ​​أخرى للعميل. هذا يطمس قليلاً بين كود JavaScript من جانب العميل ورمز Node.js في نفس قاعدة الشفرة ، مما تسبب في حدوث أخطاء غير عادية مثل fs is undefined عند محاولة الاستفادة من ميزات Node.js على العميل.

نتيجة لذلك ، يتعين علينا إنشاء مثل هذه الإنشاءات في next.js.config :

 module.exports = { webpack: (config, options) =>   Object.assign(config, {     node: config.isServer ? undefined : { fs: 'empty' },   }), }; 

ستكون علامة config.isServer في Webpack أفضل صديق لك إذا كنت بحاجة إلى تشغيل نفس الرمز في بيئات مختلفة.

بالإضافة إلى ذلك ، يدعم Next.js ، بالإضافة إلى الطرق القياسية لدورة حياة مكونات getInitialProps() ، طريقة getInitialProps() ، والتي يتم استدعاؤها فقط عند getInitialProps() التعليمات البرمجية في وضع الخادم:

 class HomePage extends React.Component { static getInitialProps() {   //         } componentDidMount() {   //     ,    } … } 

نعم ، ودعونا لا ننسى أن صديقنا العزيز ، كائن window ، ضروري لتنظيم الاستماع إلى الأحداث ، لتحديد حجم نافذة المتصفح وإتاحة الوصول إلى العديد من الوظائف المفيدة ، غير متوفر في Node.js:

 if (typeof window !== 'undefined') { // ,     `window`      } 

تجدر الإشارة إلى أنه حتى Next.js غير قادر على حفظ المطور من الحاجة إلى حل المشاكل المرتبطة بتنفيذ نفس الرمز على الخادم وعلى العميل. ولكن عند حل هذه المشكلات ، تكون config.isServer و getInitialProps() مفيدة جدًا.

النتائج: ماذا سيحدث بعد Next.js؟


في المدى القصير ، يتطابق إطار Next.js تمامًا ، من حيث الأداء ، مع متطلباتنا الخاصة بتقديم الخادم والقدرة على عرض موقعنا على الأجهزة التي تم تعطيل JavaScript عليها. بالإضافة إلى ذلك ، يتيح لك الآن استخدام العلامات الوصفية المتقدمة (الغنية).

ربما سننظر في المستقبل في خيارات أخرى في حالة احتياج تطبيقنا إلى كل من تقديم الخادم ومنطق الخادم الأكثر تعقيدًا (على سبيل المثال ، ننظر في إمكانية تطبيق تقنية تسجيل الدخول الأحادي على manifold.co و dashboard.manifold.co ) ولكن حتى ذلك الحين سوف نستخدم Next.js ، لأن هذا الإطار ، بتكاليف زمنية صغيرة ، جلب لنا فوائد ضخمة.

أعزائي القراء! هل تستخدم Next.js في مشاريعك؟

Source: https://habr.com/ru/post/ar442654/


All Articles