مرحبا يا هبر! أنا مشترك في تطوير نظام ECM. وفي سلسلة قصيرة من المقالات أريد أن أشارك تجربتنا وتاريخ تطوير شبكة بيانات React الخاصة بي (المشار إليها فيما يلي بكل بساطة شبكة) ، وهي:
- لماذا تركنا المكونات النهائية
- ما هي المشاكل والمهام التي واجهناها عند تطوير شبكتنا
- ما الربح لا تطوير الشبكة الخاصة بك تعطي
قبل التاريخ
يحتوي نظامنا على تطبيق ويب يعمل فيه المستخدمون مع قوائم المستندات ونتائج البحث والأدلة. علاوة على ذلك ، يمكن أن تكون القوائم إما صغيرة (10 موظفين) أو كبيرة جدًا (50000 مقاول). لعرض هذه القوائم ، قمنا بتطوير شبكتنا الخاصة:

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

أو التمرير اللانهاية في نفس Google على الصور ، حيث يتم تحميل الجزء التالي من الصور عند التمرير خلال الجزء الأول إلى النهاية:

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

لتطبيق الويب الخاص بنا ، أردت استخدام التمرير الظاهري. أوافق على أن التمرير إلى أي مكان في قائمة 10،000 الإدخالات هو حالة اخترع إلى حد ما. ومع ذلك ، فإن التمرير العشوائي في سجلات 500-1000 يمثل حالة حية.
عند قيامهم بتطبيق التمرير الظاهري ، غالبًا ما يقومون بتطبيق واجهة برمجة التطبيقات البرمجية لإدارة هذا التمرير. هذه هي ميزة مهمة جدا. يتم استخدام التمرير البرامجي ، على سبيل المثال ، لوضع السجل المحدد في منتصف الشاشة عند فتح الدليل:

العودة إلى المتطلبات. ماذا نحتاج؟
- إدارة التمرير الظاهري API
- تخصيص مظهر الشبكة (الصفوف والأعمدة وقائمة السياق) بحيث لا تبدو الشبكة غريبة في تطبيقنا
- دعم التقنيات التي نستخدمها: رد فعل ، واسترجاع و flexbox
- أن الشبكة عملت في ie11
بشكل عام ، كان هناك العديد من المتطلبات.
المحاولة الأولى (2016). جافا سكريبت DevExtreme شبكة البيانات
لم يمض وقت طويل على استكشاف المكتبات الموجودة ، فقد عثرنا على شبكة بيانات جافا سكريبت DevExtreme. من خلال المتطلبات الوظيفية ، غطت هذه الشبكة جميع احتياجاتنا وكان لها مظهر أنيق للغاية. ومع ذلك ، لم تكن مناسبة للمتطلبات التكنولوجية (لا تتفاعل ، لا تسترجع ، وليس فليكس بوكس). في ذلك الوقت ، لم يكن لدى DevExtreme شبكة تفاعل.
حسنًا ، دعنا لا نتفاعل ، لأننا شبكة جميلة وعملية ، سوف نستخدمها. وأضافوا المكتبة لمشروعهم. اتضح أننا أضفنا 3 ميغابايت من النصوص.
لبضعة أسابيع ، قمنا بدمج الشبكة في تطبيق الويب الخاص بنا ورفعنا الوظائف الأساسية:
- لف شبكة لتكوين صداقات مع رد فعل واستعادة
- رفع التمرير الظاهري وتحميل جزء من البيانات من خادم الويب لدينا
- تنفيذ الفرز والاختيار
في عملية شد الشبكة ، أصبحت مشكلتان خطيرتان واضحتين ومجموعة كاملة من المشاكل الأقل خطورة.
أول مشكلة خطيرة
لجعل DevExtreme JavaScript Data Grid مع عملية الإرجاع صعبة للغاية. لقد نجحنا في التحكم في إعدادات الأعمدة وإبراز السجلات من خلال الإرجاع ، ولكن لتخزين البيانات المحملة على جزء في الإعادة ، وتنفيذ عمليات CRUD عليها من خلال الإعادة - هذا غير واقعي. اضطررت إلى عمل عكاز ، بتجاوز عملية الإعادة ، تعاملت مع بيانات الشبكة. تبين أن العكاز معقد وهش. كان هذا أول جرس إنذار لا تلائمنا الشبكة ، لكننا واصلنا تثبيته.
المشكلة الخطيرة الثانية
لا يوجد واجهة برمجة تطبيقات لإدارة التمرير الظاهري. لم نتمكن من رفض التحكم في برنامج التمرير ، كان علينا إعادة مصادر DevExtreme والعثور على واجهة برمجة التطبيقات للتحكم في التمرير الداخلي. بالطبع ، كان لواجهة برمجة التطبيقات هذه مجموعة من القيود ، لأنه تم تصميمها للاستخدام الداخلي. ونتيجة لذلك ، تمكنا من جعل واجهة برمجة التطبيقات الداخلية تعمل بشكل أو بآخر على حالاتنا ، ولكن مرة أخرى ، تجاوز التكرار ، ومرة أخرى مجموعة من العكازات.
مشاكل أقل خطورة
ظهرت مشاكل أقل خطورة طوال الوقت ، لأن وظيفة DevExtreme JavaScript Data Grid القياسية لم تكن مناسبة تمامًا لنا ، وحاولنا تصحيحها:
- تمديد شبكة DevExtreme في الارتفاع لا يعمل. اضطررت إلى كتابة اختراق لتعليم DevExtreme كيفية القيام بذلك (ربما لا توجد مشكلة في هذا في الإصدارات الحديثة).
- عندما لا يكون التركيز في الشبكة ، فإنه من المستحيل التحكم في اختيار الخطوط من خلال لوحة المفاتيح (وقد احتجنا إليها). كان علي أن أكتب التحكم في لوحة المفاتيح.
- عند تغيير تكوين الأعمدة وتغيير البيانات ، واجهنا مشكلة وميض البيانات (مع التمرير الظاهري ممكّن).
- مشكلة عدد كبير من الطلبات عند عرض الشبكة لأول مرة. كان ملحوظًا بشكل خاص عند التحكم في التمرير عبر واجهة برمجة التطبيقات الداخلية.
- من الصعب تخصيص بعض أجزاء شبكة واجهة المستخدم. على سبيل المثال ، كانت هناك رغبة أعلى خط الشبكة المحدد لرسم إجراءات إدارة الخط (حذف سطر ، نسخ ، فتح بطاقة). ولكن كيف تم ربط هذا بـ DevExtreme لم تكن واضحة ، وحتى باستخدام الرد:

- يصعب تصنيف الفرز (أردنا الفرز حسب البيانات التي لا يتم عرضها في الشبكة ولا يتم تعيينها في أعمدة).
- العكازات مطلوبة لربط مكون التفاعل في خلايا الشبكة (بعد كل شيء ، الشبكة ليست على رد الفعل).
- لا كتابة التعليمات البرمجية DevExtreme (تدفق / typescript).
- مشكلة السرعة مع التمرير الظاهري طويلة.
- مشكلة السرعة عند تمديد / إعادة ترتيب الأعمدة (بعد التمرير الافتراضي المطول).
- حجم البرامج النصية الشبكة 3 ميغابايت.
على الرغم من أن شبكة وظائف DevExtreme تحتوي على كل ما نحتاجه ، إلا أنني أردت إعادة كتابة جميع الوظائف القياسية تقريبًا. أثناء استخدامه ، تمت إضافة مئات أسطر التعليمات البرمجية التي كان من الصعب فهمها والتي حاولت حل مشاكل التفاعل مع الإعادة والتفاعل ، وكان من الصعب استخدام شبكة عدم التفاعل في تطبيق التفاعل.
رفض DevExtreme. البحث عن البدائل
بعد مرور بعض الوقت باستخدام DevExtreme ، تقرر التخلي عنه. تخلص من جميع الاختراقات والتعليمات البرمجية المعقدة بالإضافة إلى 3 ميغابايت من البرامج النصية DevExtreme. والعثور على أو كتابة شبكة جديدة.
هذه المرة ، نحن أكثر اهتمامًا بدراسة الشبكات الحالية. تمت دراسة MS Fabric DetailsList و ReactVirtualized Grid و DevExtreme React Grid و Telerik Grid و KendoUI Grid.
ظلت المتطلبات على حالها ، لكنها بدأت بالفعل في قائمة فهمناها.
متطلبات التكنولوجيا:
المتطلبات الوظيفية:
- التمرير الظاهري (مع إمكانية عرض عشرات الآلاف من السجلات)
- التمرير إدارة API
- تخزين البيانات وإعدادات الشبكة في التكرار
- دفعة تحميل البيانات من خادم الويب
- إدارة العمود (التمدد / إعادة الترتيب / التحكم في الرؤية)
- فرز العمود + التصفية
- اختيار متعددة
- مثل البحث مع الخلفية
- التمرير الأفقي
- العمل مع لوحة المفاتيح
- قائمة السياق (على خط ، في منطقة فارغة ، على الأعمدة)
- دعم ie11 ، الحافة ، الكروم ، وما يليها ، رحلات السفاري
في هذه المرحلة ، ظهرت النسخة الأولى من DevExtreme React Grid بالفعل ، لكننا أسقطناها على الفور للأسباب التالية:
- التمرير الظاهري غير معتمد في ie11
- لا يعمل التمرير الظاهري مع بيانات تنزيل الدُفعات من الخادم (رغم أنه يبدو أن هناك بعض الحلول).
- والأهم من ذلك ، لم أكن أرغب في أن أخطو على نفس أشعل النار عندما أردت إعادة كتابة نصف الوظائف القياسية لشبكة خارجية.
أظهر تحليل الحلول الحالية عدم وجود "رصاصة فضية". شبكة من شأنها أن تغطي جميع متطلباتنا غير موجودة. تقرر كتابة شبكتنا الخاصة ، والتي من حيث وظائفنا سوف نطور في الاتجاه الذي نحتاج إليه ، ونكون أصدقاء مع التقنيات التي يحتاجها منتجاتنا.
تطوير شبكة بيانات التفاعل الخاصة بك
بدأ تطوير الشبكة بنماذج أولية ، حيث اختبروا أصعب الموضوعات بالنسبة لنا:
- التمرير الظاهري
- تخزين جميع بيانات الشبكة في Redux
الأكثر صعوبة تبين أن التمرير الظاهري. بالنسبة للجزء الأكبر ، يتم تصنيعه بإحدى الطرق الثلاث التالية:
1. الصفحة الافتراضية
يتم رسم البيانات في أجزاء - صفحات. عند التمرير ، تتم إضافة الصفحات المرئية ، ويتم حذف الصفحات غير المرئية. تتكون الصفحة من 20 إلى 60 سطرًا (يكون الحجم عادةً قابل للتخصيص). هذا هو المكان الذي ذهبت إليه المنتجات: DevExtreme JavaScript Data Grid ، MS Fabric DetailsList.

2. الافتراضية سطرا
يتم رسم الخطوط المرئية فقط. بمجرد أن يترك سطر الشاشة ، يتم حذفه على الفور. ذهبت المنتجات بهذه الطريقة: ReactVirtualized Grid و DevExtreme React Grid و Telerik Grid.

3. قماش
يتم رسم جميع الخطوط ومحتوياتها باستخدام Canvas. هذا ما فعله محرّر مستندات Google.

عند تطوير الشبكة ، قمنا بعمل نماذج أولية لجميع خيارات المحاكاة الافتراضية الثلاثة (حتى بالنسبة لـ Canvas). واختاروا صفحة افتراضية صفحة.
لماذا التخلي عن الخيارات الأخرى؟
واجهت المحاكاة الافتراضية للسطر مشاكل في تقديم السرعة في النموذج الأولي. بمجرد أن أصبحت محتويات الأسطر أكثر تعقيدًا (الكثير من النصوص ، التمييز ، التشذيب ، الأيقونات ، عدد كبير من الأعمدة ، وفي كل مكان flexbox) ، أصبحت إضافة / إزالة الخطوط عدة مرات في الثانية. بالطبع ، تعتمد النتائج أيضًا على المتصفح (قدمنا الدعم ، بما في ذلك بالنسبة إلى ie11 ، الحافة):

كان خيار Canvas مغرًا جدًا في تقديم السرعة ، ولكنه كان شاقًا. تم اقتراح رسم كل شيء: النص ، التفاف النص ، تشذيب النص ، التمييز ، الأيقونات ، تقسيم الخطوط ، التمييز ، المسافة البادئة. قم برد فعل على النقر فوق زر الماوس على لوحة الرسم القماشية ، مع إبراز الخطوط عند تحريك الماوس. في الوقت نفسه ، يجب تطبيق بعض عناصر Dom (تظهر تلميحات ، "الإجراءات المنبثقة" على الخط) على Canvas. كان لا يزال من الضروري حل مشكلة عدم وضوح النص والرموز في Canvas. كل هذا طويل ومن الصعب القيام به. على الرغم من أننا أتقن النموذج الأولي. في الوقت نفسه ، فإن أي تخصيص للصفوف والخلايا في المستقبل سيؤدي إلى إجهاد كبير بالنسبة لنا.
فوائد الترحيل
تمتاز المحاكاة الافتراضية لكل صفحة بمزايا مقارنةً بسطر تلو الآخر ، والتي حددت اختيارها:
- إذا كانت الصفحة معروضة بالفعل ، فسيكون التمرير داخل الصفحة رخيصًا (لا تتغير شجرة DOM عند التمرير). تتطلب المحاكاة الافتراضية سطراً لأي تمرير ثانوي تغيير شجرة DOM ، وهذا أمر مكلف عندما تكون شجرة DOM معقدة ويتم استخدام flexbox في كل مكان.
- للقوائم الصغيرة (<200 مدخل) لا يمكن حذف الصفحات ، فقط أضف. عاجلاً أم آجلاً ، سيتم بناء جميع الصفحات ، وسيكون التنقل مجانيًا بالكامل (من حيث وقت العرض).
اختيار حجم الصفحة
مشكلة منفصلة هي اختيار حجم الصفحة. كتبت أعلاه أن الحجم قابل للتخصيص وعادة ما يكون 20-60 سطرًا. يتم رسم صفحة كبيرة لفترة طويلة ، تؤدي صفحة صغيرة إلى عرض متكرر لـ "شاشة بيضاء" عند التمرير. من الناحية التجريبية ، تم تحديد حجم صفحة من 25 سطرًا. ومع ذلك ، بالنسبة لـ ie11 ، تم تقليل الحجم إلى 5 خطوط. يشعر وكأن الواجهة في IE أكثر استجابة إذا قمت برسم الكثير من الصفحات الصغيرة مع تأخير صغير من صفحة واحدة كبيرة مع تأخير كبير.
يجب تنفيذ المحاكاة الافتراضية للصفحة باستخدام رد الفعل. للقيام بذلك ، يجب حل العديد من المهام:
المهمة 1. كيفية إضافة / إزالة الصفحات من خلال رد فعل عند التمرير؟
لحل هذه المشكلة ، تم تقديم المفاهيم التالية:
النموذج هو معلومات بناء عليها. طريقة العرض هي مكون React.

في الواقع ، فإن مهمة المحاكاة الافتراضية بعد ذلك جاءت لمعالجة نماذج الصفحة: قم بتخزين قائمة نماذج الصفحة ، قم بإضافة النماذج وإزالتها عند التمرير. وبالفعل من قائمة النماذج من خلال رد فعل بناء / إعادة إنشاء العرض:

أثناء التنفيذ ، تم تشكيل قواعد العمل مع نماذج الصفحات:
- يجب أن تضاف الصفحات واحدة في وقت واحد. بعد كل إضافة ، امنح الوقت للرسم. من المقبول إضافة صفحة واحدة كل 300 إلى 500 مللي ثانية - هذا وضع تمرير سريع. إذا أضفت ، على سبيل المثال ، 5 صفحات في وقت واحد ، فسيتم تعليق واجهة المستخدم عند بنائها.
- الصفحات لا تحتاج إلى أن تعقد في عشرات. مثال لحالة مشكلة: يتم عرض 20 صفحة ، ينتقل المستخدم إلى قائمة أخرى ويجب حذف جميع الصفحات العشرين مرة واحدة. تعتبر عملية إزالة عدد كبير من الصفحات عملية مكلفة ؛ حيث يستغرق تنظيف شجرة DOM ثانية واحدة. لتجنب ذلك ، من الأفضل عدم الاحتفاظ بأكثر من 10 صفحات في كل مرة.
- لأي معالجة أعمدة (إعادة الترتيب ، الإضافة ، الحذف ، التمديد) ، من الأفضل حذف الصفحات غير المرئية للمستخدم مقدمًا. هذا سوف تجنب إعادة بناء مكلفة لجميع الصفحات المقدمة.
المهمة 2. كيفية عرض scollbar؟
يفترض التمرير الظاهري توفر شريط التمرير ، والذي يأخذ في الاعتبار حجم القائمة ويسمح لك بالانتقال إلى أي مكان:

كيفية عرض مثل scollbar؟ أبسط الحلول هو رسم div غير مرئي من الحجم المطلوب بدلاً من البيانات الحقيقية. وبالفعل في الجزء العلوي من هذا div ، نعرض الصفحات المرئية:

المهمة 3. كيفية مراقبة حجم إطار العرض؟
Viewport هو منطقة البيانات المرئية في الشبكة. لماذا تراقب حجمها؟ لحساب عدد الصفحات التي تحتاج إلى عرضها للمستخدم. افترض أن لدينا حجم صفحة صغير (5 أسطر) ودقة شاشة كبيرة (1920 × 1080). كم عدد الصفحات التي يحتاج المستخدم إلى عرضها لإغلاق إطار العرض بالكامل؟

يمكنك حل هذه المشكلة إذا كنت تعرف ارتفاع إطار العرض وارتفاع صفحة واحدة. الآن دعنا نعقد المهمة ، لنفترض أن المستخدم يغير المقياس بحدة في المتصفح - يحدد 50٪:

يوضح الموقف مع المقياس أنه لا يكفي معرفة حجم منفذ العرض مرة واحدة ، فأنت بحاجة إلى مراقبة الحجم. والآن سنقوم بتعقيد المهمة تمامًا: لا تحتوي عناصر html على حدث تغيير الحجم ، حيث يمكنك الاشتراك في الحجم ومراقبته. فقط كائن النافذة له حجم.
أول ما يتبادر إلى الذهن هو استخدام جهاز ضبط الوقت واستقصاء ارتفاع عنصر html باستمرار. ولكن هناك حل أفضل رأيناه في DevExtreme JavaScript Data Grid: قم بإنشاء إطار غير مرئي ، وتمديده إلى حجم الشبكة والاشتراك في حدث تغيير حجم iframe.contentWindow:


ملخص
PS هذه ليست النهاية. في المقال التالي ، سوف أخبرك عن كيفية تكوين صداقات مع الإعادة.
للحصول على التمرير الظاهري الكامل ، يجب حل العديد من المهام الأخرى. ولكن تلك المذكورة أعلاه كانت الأكثر إثارة للاهتمام. فيما يلي بعض المهام الأخرى المنبثقة أيضًا:
- ضع في الاعتبار اتجاه وسرعة التمرير عند إضافة / إزالة الصفحات.
- ضع في الاعتبار تغييرات البيانات لتقليل إعادة إنشاء نماذج الصفحة. على سبيل المثال ، حذف سطر واحد أو إضافة سطر ، ماذا تفعل مع الصفحات المقدمة بالفعل؟ رمي كل شيء بعيدا ، أو ترك بعض؟ هناك مجال للتحسين.
- عند تغيير التحديد ، أعد ترتيب الحد الأدنى لعدد الصفحات.
إذا كانت لديك أسئلة حول التنفيذ ، فيمكنك كتابتها في التعليقات.