خمس طرق تحسين ميزة AirSHIFT في وقت تشغيل تطبيق React الخاص بها

دراسة حالة واقعية حول تحسين أداء React SPA

Kento Tsuji
Kento Tsuji
Satoshi Arai
Satoshi Arai
Yosuke Furukawa
Yosuke Furukawa

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

استجابة بطيئة وإنتاجية أقل

‫AirSHIFT هو تطبيق ويب مخصّص للكمبيوتر المكتبي يساعد مالكي المتاجر، مثل المطاعم والمقاهي، في إدارة نوبات عمل موظفيهم. تم إنشاء تطبيق الصفحة الواحدة باستخدام React، وهو يقدّم ميزات غنية للعملاء، بما في ذلك جداول شبكية مختلفة لجداول النوبات مرتبة حسب اليوم والأسبوع والشهر وغير ذلك.

لقطة شاشة لتطبيق AirSHIFT على الويب

عندما أضاف فريق هندسة Recruit Technologies ميزات جديدة إلى تطبيق AirSHIFT، بدأ الفريق في تلقّي المزيد من الملاحظات حول الأداء البطيء. قال مدير الهندسة في AirSHIFT، يوسوكه فوروكاوا:

في إحدى دراسات أبحاث المستخدمين، فوجئنا عندما قالت إحدى صاحبات المتاجر إنّها تغادر مقعدها لتحضير القهوة بعد النقر على زر، وذلك لقتل الوقت أثناء انتظار loading لجدول النوبات.

بعد مراجعة البحث، لاحظ الفريق الهندسي أنّ العديد من المستخدمين كانوا يحاولون تحميل جداول نوبات عمل ضخمة على أجهزة كمبيوتر ذات مواصفات منخفضة، مثل كمبيوتر محمول من فئة Celeron M بسرعة 1 غيغاهرتز يعود إلى 10 سنوات.

أداة الدوران بلا نهاية على الأجهزة المنخفضة الأداء

كان تطبيق AirSHIFT يحظر سلسلة المهام الرئيسية باستخدام نصوص برمجية باهظة التكلفة، ولكن لم يدرك الفريق الهندسي مدى ارتفاع تكلفة النصوص البرمجية لأنّه كان يجري التطوير والاختبار على أجهزة كمبيوتر ذات مواصفات قوية واتصالات Wi-Fi سريعة.

رسم بياني يعرض نشاط وقت تشغيل التطبيق
عند تحميل جدول النوبات، تم استخدام حوالي% 80 من وقت التحميل من خلال تشغيل النصوص البرمجية.

بعد تحليل الأداء في "أدوات مطوّري البرامج في Chrome" مع تفعيل ميزة الحدّ من معدّل نقل البيانات في وحدة المعالجة المركزية والشبكة، أصبح من الواضح أنّه يجب تحسين الأداء. شكّلت AirSHIFT فريق عمل لمعالجة هذه المشكلة. في ما يلي 5 أمور ركّز عليها الفريق لجعل تطبيقه أكثر استجابةً لطلبات المستخدمين.

1. إنشاء جداول كبيرة افتراضية

يتطلّب عرض جدول النوبات عدّة خطوات باهظة التكلفة: إنشاء DOM الافتراضي وعرضه على الشاشة بما يتناسب مع عدد الموظفين والمناوبات الزمنية. على سبيل المثال، إذا كان لدى مطعم 50 موظفًا وأراد الاطّلاع على جدول النوبات الشهري، سيكون جدولاً يتضمّن 50 (موظفًا) مضروبًا في 30 (يومًا)، ما سيؤدي إلى عرض 1,500 عنصر خلية. هذه عملية باهظة التكلفة، خاصةً للأجهزة ذات المواصفات المنخفضة. في الواقع، كانت الأمور أسوأ. من خلال البحث، تعرّف الفريق على أنّ هناك متاجر تدير 200 موظف، ما يتطلّب حوالي 6,000 عنصر خلية في جدول شهري واحد.

للحد من تكلفة هذه العملية، فعّلت AirSHIFT جدول النوبات الافتراضية. يُثبِّت التطبيق الآن المكوّنات داخل إطار العرض فقط ويُزيل تثبيت المكوّنات التي لا تظهر على الشاشة.

لقطة شاشة توضيحية تُظهر أنّ AirSHIFT كان يُستخدَم لعرض المحتوى خارج إطار العرض
قبل إجراء التغيير: يتم عرض جميع خلايا جدول التحويل.
لقطة شاشة توضيحية توضّح أنّ AirSHIFT لا يعرض الآن سوى المحتوى المرئي في إطار العرض.
بعد إجراء التغيير: يتم عرض الخلايا داخل إطار العرض فقط.

في هذه الحالة، استخدمت AirSHIFT react-virtualized لأنّ هناك متطلبات حول تفعيل جداول الشبكة ثنائية الأبعاد المعقدة. ويبحث الفريق أيضًا عن طرق لتحويل عملية التنفيذ لاستخدام react-window الخفيفة في المستقبل.

النتائج

أدّى استخدام تقنية المحاكاة الافتراضية للجدول وحده إلى تقليل وقت تحليل النصوص البرمجية بمقدار 6 ثوانٍ (في بيئة Macbook Pro التي تبطئ وحدة المعالجة المركزية (CPU) بمقدار 4 أضعاف وشبكة الجيل الثالث السريعة). كان هذا هو التحسين الأكثر تأثيرًا في الأداء في مشروع إعادة التنظيم.

لقطة شاشة توضيحية لتسجيل لوحة الأداء في "أدوات مطوّري البرامج في Chrome"
في السابق: 10 ثوانٍ تقريبًا من النصوص البرمجية بعد إدخال المستخدم.
لقطة شاشة أخرى مُشارَك فيها تعليقات توضيحية لتسجيل لوحة الأداء في "أدوات مطوّري البرامج في Chrome"
بعد: 4 ثوانٍ من تنفيذ النصوص البرمجية بعد إدخال المستخدم

2. التدقيق باستخدام User Timing API

بعد ذلك، أعاد فريق AirSHIFT صياغة النصوص البرمجية التي يتم تشغيلها استنادًا إلى إدخال المستخدم. يتيح مخطّط المخطّطات البيانية في أدوات مطوّري البرامج في Chrome تحليل ما يحدث فعليًا في سلسلة المهام الرئيسية. لكنّ فريق AirSHIFT وجد أنّه من الأسهل تحليل نشاط التطبيق استنادًا إلى دورة حياة React.

يقدّم React 16 تتبُّع الأداء من خلال User Timing API، والذي يمكنك عرضه بشكل مرئي من قسم "المُدد الزمنية" في "أدوات مطوّري البرامج في Chrome". استخدَمت AirSHIFT قسم "المواعيد الزمنية" للعثور على منطق غير ضروري يتم تنفيذه في أحداث دورة حياة React.

قسم "المُدد الزمنية" في لوحة "الأداء" ضمن "أدوات مطوّري البرامج في Chrome"
أحداث User Timing في React

النتائج

اكتشف فريق AirSHIFT أنّه قبل كل عملية انتقال إلى مسار، كان يتم تنفيذ عملية React Tree Reconciliation غير ضرورية. وهذا يعني أنّ React كان يعدّل جدول التبديل بدون داعٍ قبل عمليات التنقّل. كان تحديث حالة Redux غير الضروري يتسبب في حدوث هذه المشكلة. وقد أدّى إصلاحها إلى توفير حوالي 750 ملي ثانية من وقت التشغيل. أجرت شركة AirSHIFT تحسينات دقيقة أخرى أيضًا أدّت في النهاية إلى تقليل إجمالي وقت التنفيذ بمقدار ثانية واحدة.

3- تحميل المكوّنات بشكل كسول ونقل المنطق المكلف إلى عمال الويب

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

لتحسين هذه التجربة، تستخدم AirSHIFT الآن React.lazy وSuspense لعرض عناصر نائبة لمحتوى الجدول أثناء تحميل المكوّنات الفعلية بشكلٍ بطيء.

نقل فريق AirSHIFT أيضًا بعض منطق العمل المكلف ضمن المكوّنات التي يتم تحميلها بشكلٍ كسول إلى Web Workers. وقد أدّى ذلك إلى حلّ مشكلة التقطُّع في البيانات التي يُدخلها المستخدم من خلال تحرير سلسلة المهام الرئيسية كي تتمكّن من التركيز على الاستجابة لبيانات المستخدم.

يواجه المطوّرون عادةً تعقيدًا في استخدام "العمال"، ولكن هذه المرة تمكّنت شركة Comlink من إنجاز المهمة الشاقة نيابةً عنهم. في ما يلي الرمز البرمجي الاصطناعي لكيفية استخدام AirSHIFT للعمال في إحدى العمليات الأكثر تكلفة التي أجروها: احتساب إجمالي تكاليف العمالة.

في App.js، استخدِم React.lazy وSuspense لعرض المحتوى الاحتياطي أثناء التحميل

/** App.js */
import React, { lazy, Suspense } from 'react'

// Lazily loading the Cost component with React.lazy
const Hello = lazy(() => import('./Cost'))

const Loading = () => (
 
<div>Some fallback content to show while loading</div>
)

/
/ Showing the fallback content while loading the Cost component by Suspense
export default function App({ userInfo }) {
   
return (
   
<div>
     
<Suspense fallback={<Loading />}>
       
<Cost />
     
</Suspense>
    </
div>
 
)
}

في مكوّن التكلفة، استخدِم comlink لتنفيذ منطق الحساب

/** Cost.js */
import React from 'react';
import { proxy } from 'comlink';

// import the workerlized calc function with comlink
const WorkerlizedCostCalc = proxy(new Worker('./WorkerlizedCostCalc.js'));
export default async function Cost({ userInfo }) {
 
// execute the calculation in the worker
 
const instance = await new WorkerlizedCostCalc();
 
const cost = await instance.calc(userInfo);
 
return <p>{cost}</p>;
}

تنفيذ منطق الحساب الذي يتم تشغيله في Worker وعرضه باستخدام comlink

// WorkerlizedCostCalc.js
import { expose } from 'comlink'
import { someExpensiveCalculation } from './CostCalc.js'

// Expose the new workerlized calc function with comlink
expose
({
  calc
(userInfo) {
   
// run existing (expensive) function in the worker
   
return someExpensiveCalculation(userInfo);
 
}
}, self);

النتائج

على الرغم من العدد المحدود من عمليات المنطق التي تم تحويلها إلى سلسلة مهام فرعية في إطار تجربة، نقلت AirSHIFT حوالي 100 ملي ثانية من رمز JavaScript إلى السلسلة الفرعية (تم اختبار ذلك من خلال خفض معدّل استخدام وحدة المعالجة المركزية بمقدار 4 مرّات).

لقطة شاشة لتسجيل في لوحة &quot;الأداء&quot; في &quot;أدوات مطوّري البرامج في Chrome&quot; يُظهر أنّ كتابة النصوص البرمجية تحدث الآن في Web Worker بدلاً من سلسلة المهام الرئيسية

تبحث شركة AirSHIFT حاليًا في إمكانية تحميل المكوّنات الأخرى بشكلٍ بطيء وإلغاء تحميل المزيد من المنطق إلى عمال الويب لتقليل الارتباك بشكلٍ أكبر.

4. ضبط ميزانية الأداء

بعد تنفيذ كل هذه التحسينات، كان من المهم التأكّد من الحفاظ على أداء التطبيق على مدار الوقت. تستخدِم AirSHIFT الآن bundlesize لتجنُّب تجاوز حجم ملفَي JavaScript وCSS الحاليَين. بالإضافة إلى ضبط هذه الميزانيات الأساسية، أنشأوا لوحة بيانات لعرض النسب المئوية المختلفة لوقت تحميل جدول النوبات للتحقّق مما إذا كان التطبيق يحقّق أداءً جيدًا حتى في الظروف غير المثالية.

  • يتم الآن قياس وقت اكتمال النص البرمجي لكل حدث Redux.
  • يتم جمع بيانات الأداء في Elasticsearch.
  • يتمّ عرض أداء الشريحة المئوية العاشرة والخامسة والعشرين والخمسين والسابعة والخمسين لكلّ حدث باستخدام Kibana.

تتتبّع AirSHIFT الآن حدث تحميل جدول النوبات للتأكّد من اكتماله في 3 ثوانٍ لمستخدمي الربع المئوي الخمسين. هذه ميزانية غير مفروضة في الوقت الحالي، ولكنّهم يفكرون في إرسال إشعارات تلقائية عبر Elasticsearch عند تجاوز ميزانيتهم.

رسم بياني يُظهر أنّ الشريحة المئوية الخامسة والسبعين تكتمل في غضون 2500 مللي ثانية تقريبًا، وأنّ الشريحة المئوية الخمسين تكتمل في غضون 1250 مللي ثانية تقريبًا، وأنّ الشريحة المئوية الخامسة والعشرين تكتمل في غضون 750 مللي ثانية تقريبًا، وأنّ الشريحة المئوية العاشرة تكتمل في غضون 500 مللي ثانية تقريبًا
لوحة بيانات Kibana تعرض بيانات الأداء اليومية حسب الشرائح المئوية.

النتائج

من الرسم البياني أعلاه، يمكنك معرفة أنّ AirSHIFT تحقّق الآن في الغالب الحدّ الأقصى لمدة 3 ثوانٍ لمستخدمي الشريحة المئوية التسعون، كما تحمّل جدول التحويلات خلال ثانية واحدة لمستخدمي الشريحة المئوية الخامسة والعشرين. من خلال تسجيل بيانات أداء مراقبة المستخدمين في الوقت الفعلي من ظروف وأجهزة مختلفة، يمكن لشركة AirSHIFT الآن التحقّق مما إذا كان إصدار ميزة جديدة يؤثر فعليًا في أداء التطبيق أم لا.

5- هاكثونات الأداء

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

تُجري شركة AirSHIFT حاليًا فعاليات داخلية لتحسين الأداء تستمر لمدة يوم واحد للسماح للمهندسين بالتركيز على العمل المرتبط بالأداء فقط. وفي هذه الفعاليات، تُزال جميع القيود ويُحتفَظ بإبداع المهندسين، ما يعني أنّ أيّ تنفيذ يساهم في السرعة يستحقّ الاعتبار. لتسريع ورشة الهackathon، تقسم AirSHIFT المجموعة إلى فِرق صغيرة ويتنافس كل فريق لمعرفة من يمكنه تحقيق أكبر تحسين في نتيجة أداء Lighthouse. تصبح الفرق تنافسية جدًا. 🔥

صور من الهاكاثون

النتائج

يحقّق نهج "هاكاثون" نجاحًا جيدًا لهم.

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

ومن الآثار الجانبية الإيجابية لهذا النهج العملي أنّ العديد من الفِرق الهندسية الأخرى في Recruit أصبحت مهتمة به، ويسهّل فريق AirSHIFT الآن العديد من ورش الهackathon السريعة داخل الشركة.

ملخّص

لم تكن هذه الرحلة هي الأسهل على AirSHIFT، ولكنّها بالتأكيد كانت مثمرة. الآن، تحمّل AirSHIFT جدول النوبات خلال 1.5 ثانية في المتوسط، ما يمثّل تحسُّنًا بمقدار 6 مرّات مقارنةً بأداء الشركة قبل المشروع.

بعد إطلاق تحسينات الأداء، قال أحد المستخدمين:

نشكرك على سرعة تحميل جدول النوبات. أصبح من الأسهل الآن تنظيم العمل في نوبات.