في حديثي في Ya. Subbotnik Pro ، تذكرت ماذا وكيف انتهينا من تجميع وهندسة "المشروع الحديث القياسي" وما النتائج التي حصلنا عليها.
- منذ عام ونصف كنت أعمل في فريق هندسة Serp. نقوم بتطوير وقت التشغيل وتجميع التعليمات البرمجية الجديدة في React و TypeScript هناك.
لنتحدث عن ألمنا المشترك الذي سيتناوله هذا الحديث. عندما تريد إنشاء مشروع صغير في React ، تحتاج فقط إلى استخدام مجموعة قياسية من الأدوات تسمى ثلاثة أحرف - CRA. يتضمن ذلك إنشاء البرامج النصية والبرامج النصية لإجراء الاختبارات وإعداد بيئة التطوير وكل شيء تم بالفعل للإنتاج. كل شيء يتم بكل بساطة من خلال نصوص NPM ، وربما يعرف الجميع عنها من لديه خبرة في React.
لكن لنفترض أن المشروع أصبح كبيرًا ، فلديه الكثير من الأكواد ، والكثير من المطورين ، تظهر ميزات الإنتاج ، مثل الترجمات ، التي لا يعرف عنها تطبيق Create React شيئًا. أو لديك نوع من خطوط أنابيب CI / CD المعقدة. ثم تبدأ الأفكار في إخراج لاستخدام تطبيق Create React كأساس وتخصيصه لمشروعك الخاص. لكن ليس من الواضح على الإطلاق ما الذي ينتظر هناك ، وراء هذا القذف. لأنه عندما تقوم بالقذف ، فإنها تقول أن هذه عملية خطيرة للغاية ، ولن يكون من الممكن إعادتها مرة أخرى وما إلى ذلك ، وهذا أمر مخيف للغاية. أولئك الذين ضغطوا على الإخراج يعلمون أنه يتم طرح الكثير من التكوينات هناك ، والتي تحتاج إلى فهمها. بشكل عام ، هناك الكثير من المخاطر ، وليس من الواضح ما يجب القيام به.
سأخبرك كيف كان الأمر معنا. أولا ، عن مشروعنا. مشروعنا الأمامي هو صفحات نتائج بحث Serp و Search Engine و Yandex التي شاهدها الجميع. منذ 2018 نحن لا ننقل React و TypeScript. تمت كتابة حوالي 12 ميغا بايت من التعليمات البرمجية على Serpa العام الماضي. هناك عدد قليل من الأنماط والكثير من أكواد TS و SCSS. كم في البداية ، في عام 2018 ، كان هناك ، لم أكتب ، هناك القليل جدًا ، كانت هناك قفزة حادة جدًا.
دعونا نرى ما إذا كان هذا الكثير من التعليمات البرمجية أم لا. بالمقارنة مع الكود المصدري webpack-4 ، يوجد كود أقل بكثير في webpack-4. حتى مستودع TypeScript يحتوي على كود أقل.
لكن vs-code يحتوي على المزيد من التعليمات البرمجية ، وهو مشروع جيد يحتوي على ما يصل إلى 30 ميغا بايت من رمز TypeScript. نعم ، هو مكتوب أيضًا بلغة TypeScript ، ويبدو المنجل أصغر حجمًا. بدأنا في عام 2018 ، في عام 2019 ، كان هناك 12 ميغا بايت ، وعمل 70 من مطورينا ، حيث نفذوا 100 طلب سحب في الأسبوع. في عام واحد ، ضاعفوا هذا الحجم ثلاث مرات ، وحصلوا على 30 ميغا بايت بالضبط. لقد أجريت قياسات هذا الشهر ، لدينا إجمالي 30 ميغا بايت من التعليمات البرمجية الآن ، وهذا بالفعل أكثر من في مقابل كود.
متساوية تقريبًا ، لكن أكثر قليلاً. هذا هو ترتيب مشروعنا.
وقد أخرجنا في البداية ، لأننا عرفنا على الفور أنه سيكون لدينا الكثير من الأكواد ، وعلى الأرجح ، لن تعمل التكوينات الأولية الموجودة في تطبيق Create React لدينا. لكننا بدأنا بنفس الطريقة ، مع Create React App.
هذا ما ستكون عليه القصة. نريد أن نشارك تجربتنا ، ونخبرك بما يجب علينا فعله باستخدام تطبيق Create React لجعل Yandex Serp يعمل بشكل صحيح عليه. هذا هو ، كيف حصلنا على سرعة التحميل والتهيئة في المتصفح ، وكيف حاولنا عدم إبطاء البناء ، والإعدادات ، والمكونات الإضافية والأشياء الأخرى التي استخدمناها لهذا الغرض. وبطبيعة الحال ، فإن النتائج التي حققناها ستأتي في النهاية.
كيف فكرنا؟ كانت الفكرة الأصلية هي أن المنجل الخاص بنا هو صفحة تحتاج إلى العرض بسرعة كبيرة ، لأنه ، في الأساس ، هناك نتائج نصية بسيطة للغاية ، لذلك نحتاج إلى قوالب من جانب الخادم ، لأن هذه هي الطريقة الوحيدة للحصول على عرض سريع. وهذا يعني أنه يتعين علينا رسم شيء ما حتى قبل أن يبدأ شيء ما في التهيئة على العميل.
في الوقت نفسه ، كنت أرغب في عمل الحد الأدنى لحجم الإحصائيات ، حتى لا يتم تحميل أي شيء غير ضروري وكانت التهيئة سريعة أيضًا. أي أننا نريد كلاً من التصيير الأول والتهيئة السريعة.
ماذا يقدم لنا تطبيق Create React؟ لسوء الحظ ، لا يقدم لنا أي شيء عن عرض الخادم.
تقول مباشرة أن عرض الخادم غير مدعوم في تطبيق Create React. بالإضافة إلى ذلك ، يحتوي تطبيق Create React على إدخال واحد فقط للتطبيق بأكمله. أي ، بشكل افتراضي ، يتم جمع حزمة كبيرة واحدة لجميع الصفحات المتنوعة الضخمة. هذا كثير. من الواضح أنه من أصل 30 ميغا بايت ، ما يقرب من نصفها من أنواع TS ، ولكن لا يزال هناك الكثير من التعليمات البرمجية ستنتقل مباشرة إلى المتصفح.
في الوقت نفسه ، يحتوي تطبيق Create React على بعض الإعدادات الجيدة ، على سبيل المثال ، يذهب وقت تشغيل webpack إلى هناك في جزء منفصل. يتم تحميله بشكل منفصل ، ويمكن تخزينه مؤقتًا لأنه لا يتغير بشكل طبيعي.
بالإضافة إلى ذلك ، يتم أيضًا تجميع الوحدات من وحدات العقدة في أجزاء منفصلة. نادرًا ما يتم تغييرها ، وبالتالي يتم تخزينها مؤقتًا أيضًا بواسطة المتصفح ، وهو أمر رائع ، يجب حفظه. ولكن في الوقت نفسه ، لا يوجد شيء يتعلق بالترجمات في Create React App.
دعونا نجمع قائمتنا لكيفية ظهور قائمة إمكانيات نظامنا الأساسي في حالتنا. أولاً ، نريد تصييرًا شماليًا ، كما قلت ، للقيام بتصيير سريع. بالإضافة إلى ذلك ، نود أن يكون لدينا ملف إدخال منفصل لكل نتيجة بحث.
إذا كان لدى Serpa ، على سبيل المثال ، آلة حاسبة ، فإننا نرغب في تسليم الحزمة مع الآلة الحاسبة ، ولا يلزم تسليم الحزمة مع المترجم بسرعة. إذا تم جمع كل هذا في حزمة واحدة كبيرة ، فسيظل كل شيء دائمًا ، حتى لو لم يكن نصف هذه الأشياء يتعلق بمسألة محددة.
علاوة على ذلك ، أود توفير وحدات مشتركة في أجزاء منفصلة حتى لا يتم تحميل ما تم تحميله بالفعل.
هنا مثال آخر مع المنجل. به آلة حاسبة ، وهناك حزمة آلة حاسبة. هناك مكونات مشتركة. تم تسليمها للعميل. ثم ظهرت ميزة أخرى - خريطة. قاد مجموعة من الخرائط ، وقاد المكونات المشتركة الأخرى ، باستثناء تلك التي تم تسليمها بالفعل.
إذا تم جمع المكونات المشتركة بشكل منفصل ، فهناك فرصة كبيرة للتحسين ويتم تسليم ما هو مطلوب فقط ، فقط فرق. والوحدات النمطية الأكثر شيوعًا الموجودة دائمًا على الصفحة ، على سبيل المثال ، وقت تشغيل حزمة الويب ، والتي تحتاجها دائمًا هذه البنية التحتية بأكملها ، يجب دائمًا تحميلها.
لذلك ، من المنطقي أن تجمع في قطعة منفصلة. بمعنى ، يمكن أيضًا تقسيم هذه المكونات الشائعة إلى تلك المكونات التي لا نحتاج إليها دائمًا ، والمكونات المطلوبة دائمًا. يمكن جمعها في ملف منفصل وتحميلها دائمًا وتخزينها مؤقتًا أيضًا ، لأن هذه المكونات الشائعة ، مثل الأزرار / الروابط ، لا تتغير كثيرًا ، بشكل عام ، تحصل على ربح من التخزين المؤقت.
وفي الوقت نفسه ، تحتاج إلى اتخاذ قرار بشأن تجميع الترجمات.
كل شيء واضح بما فيه الكفاية هنا. إذا ذهبنا إلى Turkish Serp ، فإننا نرغب في تنزيل الترجمات التركية فقط ، وليس تنزيل جميع الترجمات الأخرى ، لأن هذا رمز إضافي.
ماذا فعلنا؟ أولاً ، حول كود الخادم. فيما يتعلق بذلك ، سيكون لدينا اتجاهان - البناء للإنتاج والانطلاق للتطوير.
بشكل عام ، تحتاج إلى تقديم بيان منفصل حول TypeScript أولاً. عادة المشاريع ، كما سمعت ، تستخدم بابل. لكننا قررنا على الفور استخدام مترجم TypeScript القياسي ، لأننا نعتقد أن ميزات TypeScript الجديدة ستصل إليه بشكل أسرع. لذلك ، تخلينا على الفور عن بابل واستخدمنا tsc.
لذلك ، حجم الكود الحالي لدينا ، 30 ميغا بايت ، يتم تجميعها على كمبيوتر محمول في ثلاث دقائق. لا بأس به من. إذا تخلت عن فحص النوع واستخدمت tsc fork أثناء كل تجميع (لسوء الحظ ، لا يحتوي TSC على إعداد من شأنه تعطيل فحص النوع ، كان عليك أن تفرز) ، فيمكنك حينئذٍ توفير ضعف الوقت. سوف يستغرق تجميع الكود الخاص بنا دقيقة ونصف فقط.
لماذا لا يمكننا تجميع التحقق من نوع الوقت؟ لأننا ، على سبيل المثال ، يمكننا التحقق منها في خطافات الالتزام المسبق. قم بعمل linter يقوم بتشغيل فحص النوع فقط ، ويمكن إجراء التجميع نفسه دون فحص النوع. لقد اتخذنا هذا القرار.
كيف نجري في التطوير؟ عادةً ما يستخدم Dev أيضًا حزمة babel مع حزمة الويب ، لكننا نستخدم أداة مثل ts-node.
هذه أداة بسيطة للغاية. من أجل تشغيله ، يكفي كتابة هذا (ts-node) في ملف JavaScript للإدخال ، وسوف يتجاوز متطلبات كود TS بأكمله لاحقًا في هذه العملية. وإذا تم تحميل رمز TS في هذه العملية على طول الطريق ، فسيتم تجميعه سريعًا. شيء بسيط جدا.
بطبيعة الحال ، هناك حمل صغير مرتبط بحقيقة أنه إذا لم يتم تحميل الملف بعد في هذه العملية ، فيجب إعادة تجميعه. لكن في الواقع ، هذا الحمل ضئيل ومقبول بشكل عام.
بالإضافة إلى ذلك ، هناك بعض الأسطر الأكثر إثارة للاهتمام في هذه القائمة. الأول هو تجاهل الأنماط ، لأننا لا نحتاج إلى أنماط للقوالب من جانب الخادم. نحتاج فقط إلى الحصول على HTML. لذلك ، نستخدم أيضًا مثل هذه الوحدة - أنماط التجاهل. وإلى جانب ذلك ، نقوم بإيقاف نفس نوع التحقق (transpile-only) ، كما فعلنا في TSC من أجل تسريع عمل ts-node.
الانتقال إلى رمز العميل. كيف نجمع كود ts في webpack؟ نستخدم ts-loader وخيار transpileOnly ، أي نفس الحزمة تقريبًا. بدلاً من محمل babel ، هناك أدوات تحميل قياسي أكثر أو أقل و transpileOnly.
لسوء الحظ ، لا يعمل البناء الإضافي في محمل ts. وهذا يعني ، بعد كل شيء ، أن ts-loader ليس أداة قياسية تمامًا ، ولا يصنعها نفس الأشخاص الذين يستخدمون TypeScript. لذلك ، لا يتم دعم جميع خيارات المترجم هناك. على سبيل المثال ، الإنشاء التزايدي غير معتمد.
البناء التدريجي هو أحد الأشياء التي يمكن أن تكون مفيدة جدًا في التطوير. وبالمثل ، يمكنك إضافة هذه ذاكرات التخزين المؤقت إلى خط الأنابيب. بشكل عام ، عندما تكون تغييراتك صغيرة ، لا يمكنك إعادة ترجمة كل شيء بالكامل ، كل TypeScript ، ولكن فقط ما تغير. إنه يعمل بشكل فعال.
بشكل عام ، للاستغناء عن عمليات الإنشاء الإضافية ، نستخدم أداة تحميل ذاكرة التخزين المؤقت. هذا هو الحل القياسي من webpack. كل شيء واضح تماما. عندما يحاول رمز TypeScript الاتصال أثناء إنشاء webpack ، تتم معالجته بواسطة المترجم ، وإضافته إلى ذاكرة التخزين المؤقت ، وفي المرة التالية ، إذا لم تكن هناك تغييرات في ملفات المصدر ، فلن يقوم مُحمل ذاكرة التخزين المؤقت بتشغيل ts-loader وسيأخذها من ذاكرة التخزين المؤقت. وهذا يعني أن كل شيء بسيط للغاية هنا.
يمكن استخدامه لأي شيء ، ولكن خصيصًا لـ TypeScript ، هذا شيء مفيد ، لأن ts-loader هو محمل ثقيل إلى حد ما ، لذا فإن محمل ذاكرة التخزين المؤقت مناسب جدًا هنا.
لكن محمل ذاكرة التخزين المؤقت له عيب واحد - فهو يعمل مع وقت تعديل الملف. هنا مقتطف من شفرة المصدر. ولم تنجح معنا.
اضطررنا إلى تفرع وإعادة إجراء خوارزمية التخزين المؤقت بناءً على التجزئة من محتوى الملف ، لأنها لا تناسبنا لاستخدام أداة تحميل ذاكرة التخزين المؤقت في خط الأنابيب.
الحقيقة هي أنه عندما تريد إعادة استخدام نتائج البناء بين عدة طلبات سحب ، فإن هذه الآلية لن تعمل. لأنه إذا كان التجميع ، على سبيل المثال ، منذ وقت طويل. ثم تحاول تقديم طلب سحب جديد لا يغير الملفات التي تم جمعها في المرة السابقة.
لكن وقتهم أكثر حداثة. وفقًا لذلك ، سيعتقد مُحمل ذاكرة التخزين المؤقت أن الملفات قد تم تحديثها ، ولكن في الواقع ، لا ، لأن هذا ليس وقت تعديل ، بل وقت خروج. وإذا قمت بذلك على هذا النحو ، فستتم مقارنة التجزئات من المحتوى. لم يتغير المحتوى ، سيتم استخدام النتيجة القديمة.
وتجدر الإشارة هنا إلى أنه إذا كنا نستخدم babel ، فإن برنامج babel-loader لديه آلية تخزين مؤقت بالداخل افتراضيًا ، ويتم إجراؤه بالفعل على تجزئات من المحتوى ، وليس على mtime. لذلك ، ربما سنفكر أكثر قليلاً ونتطلع إلى بابل.
الآن حول تجميع القطع.
دعنا نتحدث قليلاً عما يفعله webpack افتراضيًا. إذا كان لدينا ملف فهرس إدخال ، يتم توصيل المكونات به. لديهم أيضًا مكونات ، إلخ. بالإضافة إلى ذلك ، ترتبط الوحدات النمطية الشائعة: React و React-dom و Lodash ، على سبيل المثال.
لذلك ، بشكل افتراضي ، حزمة الويب ، كما يعرف الجميع على الأرجح ، ولكن في حالة ، أكرر ، تجمع كل التبعيات في حزمة واحدة كبيرة.
في الوقت نفسه ، يمكن تجميع كل ما يتم توصيله عبر node_modules إما على هيئة عناصر خارجية ، أو محملة بنصوص منفصلة ، أو في جزء منفصل عن طريق إعداد إعداد Optization.splitChunks خاص في حزمة الويب. في رأيي ، حتى بشكل افتراضي ، يتم جمع وحدات البائعين هذه في قطعة منفصلة. يحتوي CRA على نسخة معدلة قليلاً من هذا الانقسام.
دعونا نتذكر ما هو وقت التشغيل. لقد ذكرته. هذا هو نوع الكود الذي يحتوي على "رأس" تحميل البرامج النصية والوظائف التي تضمن تشغيل النظام المعياري على العميل. ثم مصفوفة (أو ذاكرة تخزين مؤقت) ، والتي في الواقع تحتوي على الوحدات.
لماذا أخبرتك عن هذا؟ لأن إنشاء تطبيق React لا يزال يستخدم إعدادًا يجمع وقت التشغيل هذا في ملف منفصل. لن يتم وضع هذا الملف في الحزمة الصحية الأصلية ، ولكن في ملف منفصل. يمكن تخزينها مؤقتًا في المتصفح وكل ذلك.
إذن ما الذي لا يصلح لنا في Create React App؟
هذا splitChunks ، الذي يتم استخدامه هناك افتراضيًا ، يجمع فقط node_modules إلى أجزاء منفصلة. ولكن ، في الواقع ، هناك مكونات مشتركة ، مكتبات عامة ، على مستوى المشروع. أود أيضًا أن أجمعها في أجزاء منفصلة ، لأنها ، ربما ، نادرًا ما تتغير أيضًا. لماذا نقصر أنفسنا على ما هو موجود في وحدات العقدة فقط؟
بالإضافة إلى ذلك ، فيما يتعلق بـ runtimeChunks ، يمكننا أيضًا أن نقول أنه سيكون من الرائع ، كما ناقشنا في الأصل ، إلى جانب وقت التشغيل نفسه ، أيضًا جمع الوحدات النمطية هناك ، في نفس الجزء ، والتي تكون مطلوبة دائمًا. نفس الأزرار / الروابط. هناك دائما روابط على Serp. أود دائما أن أجمع الروابط. هذا ليس فقط وقت تشغيل webpack ، ولكن أيضًا بعض المكونات المشهورة جدًا.
هذا غير موجود في تطبيق Create React. كيف فعلنا ذلك معنا؟
لقد قمنا بتعديل SplitChunks بطريقة عطلنا بها جميع السلوك القياسي وطلبنا التجميع في كود مشترك ليس فقط ما هو موجود في node_modules ، ولكن أيضًا ما هي المكونات المشتركة لمشروعنا ورمز المكتبة الخاص بمشروعنا ، ما هو موجود في src / lib ، src / تحتوي على مكونات.
بالإضافة إلى ذلك ، نجمع في أجزاء منفصلة ما يتم توصيله عبر عمليات الاستيراد الديناميكية ، وما يسمى عادةً القطع غير المتزامنة.
هنا تحتاج إلى الانتباه إلى خيارين. أحدهما فرض والآخر أولي. بشكل عام ، يعد فرض إعدادًا مناسبًا بدرجة كافية لدرجة أنه يعطل أي أساليب إرشادية معقدة في splitChunks.
بشكل افتراضي ، تحاول SplitChunks فهم مقدار الوحدات المطلوبة وأخذ هذه الإحصائيات في الاعتبار عند التقسيم. لكن من الصعب اتباع ذلك ، ويمكن أن يتغير الطلب على الوحدة من وقت لآخر ، وستقفز الوحدة النمطية بين الأجزاء. من المجموعة العامة إلى حزمة الميزات والعكس. وهذا يعني أن هذا سلوك غير متوقع للغاية ، لذلك نقوم بتعطيله.
بمعنى ، نقول دائمًا كل ما يلبي الشروط في مجال الاختبار ، ندخل في الأجزاء المشتركة. نحن لا نريد أي استدلال.
لكن الأجزاء: الأولية هي أيضًا شيء جيد ، فهي تتعلق بحقيقة أن هذه الوحدات المتزامنة ، والوحدات التي يتم توصيلها عبر عمليات استيراد ديناميكية ، يمكن توصيلها في أماكن مختلفة بطرق مختلفة. بمعنى ، يمكنك توصيل نفس الوحدة إما عن طريق الاستيراد الديناميكي أو عن طريق الاستيراد المنتظم.
وتسمح القيمة الأولية ببناء نفس الوحدة بطريقتين. أي أنه تم تجميعه ، غير متزامن ومتزامن ، مما يسمح باستخدامه في كلا الاتجاهين. ملائم بما فيه الكفاية. يؤدي هذا إلى تضخيم حجم الإحصائيات المجمعة قليلاً ، لكنه يسمح لك باستخدام أي واردات.
بالمناسبة ، من الصعب فهم هذا من الوثائق. لقد أعدت مؤخرًا قراءة وثائق webpack ولم يتم كتابة أي شيء عادي عنه.
هذا ما فعلناه مع splitChunks. الآن ماذا فعلنا مع runtimeChunks. بدلاً من جمع وقت التشغيل فقط في وقت التشغيل ، نريد إضافة المزيد من المكونات الأكثر شيوعًا هناك.
لذلك كتبنا المكون الإضافي الخاص بنا والذي يسمى MainChunkPlugin. ولها بيئة تافهة للغاية. هناك فقط قائمة بالوحدات التي يجب جمعها هناك ، والتي اعتبرناها شائعة.
بمجرد استخدام أدوات اختبار A / B الخاصة بنا ، والعديد من الأدوات غير المتصلة بالإنترنت ، أدركنا المكونات الموجودة في أغلب الأحيان في نتائج البحث. هذا هو المكان الذي كتبوا فيه فقط في مثل هذه القائمة المسطحة. وفي النهاية ، يجمع المكون الإضافي الخاص بنا هذه المكونات من القائمة ، بالإضافة إلى المكتبات ، بالإضافة إلى وقت تشغيل webpack ، الذي يجمع هذا التحسين القياسي.
هنا ، بالمناسبة ، جزء من التعليمات البرمجية التي تلصق وقت التشغيل. كما أنه ليس من التافه إظهار أنه ليس من السهل كتابة المكونات الإضافية ، ولكن بعد ذلك دعونا نرى ما قدمته.
وتجدر الإشارة أيضًا إلى أنه بشكل عام ، فإن webpack لديه آلية قياسية للقيام بذلك ، تسمى DLLPlugin. كما يسمح لك بجمع جزء منفصل وفقًا لقائمة التبعيات. لكن لها عدد من العيوب. على سبيل المثال ، لا يتضمن runtimeChunks. أي ، وقت التشغيل ، سيكون لديك دائمًا قطعة منفصلة ، وستكون هناك قطعة مجمعة بواسطة DLLPlugin. هذه ليست مريحة للغاية.
يتطلب DLLPlugin أيضًا تجميعًا منفصلاً. بمعنى ، إذا أردنا إنشاء هذا الجزء المنفصل باستخدام أكثر المكونات إيقاعًا باستخدام DLLPlugin ، فسيتعين علينا تشغيل تجميعين.
أي ، قام المرء بتجميع هذه القطعة المنفصلة مع ملف البيان ، وستقوم بقية المجموعة بجمع كل شيء آخر ، ببساطة عن طريق طرح ملف البيان ، ولن يجمع ما تم إدخاله بالفعل في المقطع مع المكونات الشائعة. وهذا يؤدي إلى إبطاء الإنشاء ، لأن تنفيذ DLLPlug استغرق منا سبع ثوانٍ محليًا. هذا كثير. ولا يمكن تحسينه لأنه يحتوي على تنفيذ تسلسلي صارم.
بالإضافة إلى ذلك ، في لحظة معينة ، احتجنا إلى بناء هذا الجزء الرئيسي من مجموعتنا بمكونات شائعة بدون CSS ، فقط JS. DLLPlugin لا يفعل ذلك. يقوم دائمًا بجمع كل ما هو متاح من خلال الطلب من خلال الواردات. وهذا يعني أنه إذا قمت بتضمين CSS ، فستضرب دائمًا أيضًا. كان الأمر غير مريح لنا. ولكن إذا لم تكن هذه مشكلة بالنسبة لك ، ولا تريد كتابة مثل هذه التعليمات البرمجية الصعبة ، فإن DLLPlugin يعد حلاً عاديًا تمامًا. إنه يحل المشكلة الرئيسية. أي أنه يقدم المكونات الأكثر شيوعًا في ملف منفصل. يمكن استخدامه.
وذلك ما لم نحصل؟ يمكن أن تستخدم ميزتنا المكونات فائقة الشعبية من MainChunk ، والتي يتم تجميعها بواسطة مكون إضافي خاص يحمل نفس الاسم. بالإضافة إلى ذلك ، هناك أجزاء مشتركة ، والتي تشمل جميع أنواع المكونات المشتركة ، وهناك أجزاء غير متزامنة ، يتم تحميلها من خلال عمليات الاستيراد الديناميكية.
باقي الكود موجود في حزم الميزات. من حيث المبدأ ، هذا هو الهيكل الخاص بك.
حول تجميع الترجمات. ترجماتنا ليست سوى ملفات ts بجوار المكونات التي تحتاج إلى ترجمات. هنا لدينا تسع لغات ، وهنا تسعة ملفات.
تبدو الترجمات هكذا. إنه ببساطة كائن يحتوي على عبارة رئيسية ومعنى العبارة المترجمة.
هذه هي الطريقة التي ترتبط بها الترجمات بالمكون ، ومن ثم يتم استخدام مساعد خاص.
كيف يمكن جمع هذه الترجمات؟ نعتقد: نحن بحاجة إلى جمع الترجمات ، والبحث على الإنترنت ، وما يكتبونه ، وكيفية القيام بذلك.
يقولون على الإنترنت: استخدم الترجمة المتعددة. أي بدلاً من تشغيل حزمة ويب واحدة ، فقط قم بتشغيل webpack build لكل لغة. لكن ، كما يقولون ، سيكون كل شيء على ما يرام ، نظرًا لوجود مُحمل ذاكرة التخزين المؤقت ، فسيتم تخزين كل هذا العمل العام باستخدام TypeScript ، أو أي شيء لديك ، سيتم تخزينه مؤقتًا ، وبالتالي لن يمر وقت طويل.
لا تثبط عزيمتك ، لا تعتقد أن هذا سيكون تسعة حزم ويب حقيقية. لن يكون الأمر كذلك ، سيكون جيدًا.
الشيء الوحيد الذي يجب تصحيحه هو إضافة وحدة ReplacementPlugin ، والتي بدلاً من ملف الفهرس الذي يربط جميع اللغات ، سوف يستبدلها بلغة معينة. كل شيء تافه تمامًا ، ونعم ، يجب إصلاح الإخراج. اتضح الآن أننا بحاجة إلى جمع حزمة منفصلة لكل لغة.
الرسم البياني لهذه الوصفة على النحو التالي. كان هناك مترجم. قام بتوصيل ترجمات المترجم. لقد ربط اللغات ، وبدلاً من جمع هذه البنية ، قمنا بتكرارها لكل لغة ، وحصلنا على واحدة منفصلة ، وجمعنا كل منها كمجموعة منفصلة.
لسوء الحظ ، لا يعمل. حاولت تشغيل خيار الترجمة المتعددة هذا لكودنا الحالي 30 ميغابايت ، وانتظرت ساعة ونصف ، وحصلت على هذا الخطأ.
إنه طويل جدا ومستحيل. ماذا فعلنا بهذا؟ لقد قمنا بعمل مكون إضافي آخر. نأخذ نفس البنية ونعمل على دمج أنفسنا في عمل webpack عندما يكون على وشك حفظ ملفات الإخراج على القرص. نقوم بنسخ هذا الهيكل عدة مرات مثل اللغات ، ونلصق لغة واحدة بكل لغة. وعندها فقط نقوم بإنشاء الملفات.
في الوقت نفسه ، لا يتكرر العمل الرئيسي الذي يقوم به webpack لتجاوز تبعيات الترجمة. أي أننا نتدخل في المرحلة الأخيرة ، وبالتالي يمكننا أن نأمل أن تكون سريعة.
لكن تبين أن كود البرنامج المساعد معقد. هذا حرفيا هو ثمن المكون الإضافي الخاص بنا. أنا فقط أظهر مدى صعوبة الأمر. وهناك نجد بانتظام حشرات صغيرة غير سارة هناك. لكن تنفيذه لم يكن أسهل. لكنها تعمل بشكل جيد جدا.
أي ، بدلاً من ساعة ونصف مع وجود خطأ ، نحصل على خمس دقائق من التجميع مع هذا المكون الإضافي الخاص بنا.
الآن التسليم والتهيئة.
التسليم والتهيئة بسيط. ما نقوم بتحميله في موارد منفصلة ، نستخدم التحميل المسبق ، تمامًا مثل أي شخص آخر ، على ما أعتقد. ثم نقوم بتضمين CSS و JS و HTML فعليًا لمكوناتنا ، ثم نقوم بتحميل مواردنا هذه ، ولكن بدون تزامن.
جربنا. إذا كنت تستخدم غير متزامن ، فسيتم إزالة توقيت بداية التفاعل ، وهو ما لا نريده. لذلك فقط استخدم التحميل المسبق والتحميل في نهاية الصفحة. بشكل عام ، لا شيء خاص.
في نفس الوقت ، نضمّن كل شيء آخر. وهذا هو ، هذا هو MainChunk الخاص بنا ، ونحن نضمّن CSS الخاص به. المكونات العامة والأنماط ، بشكل عام ، كل ما هو مكتوب على الشريحة ، سنضمنه. كانت هذه أيضًا سلسلة من التجارب التي أظهرت أن كلمة "مضمنة" تعطي أفضل نتيجة للتصيير الأول وبداية التفاعل.
والآن إلى الأعداد. للحديث عن الأرقام ، عليك أن تقول كلمتين عن المقاييس.
لدينا فريق سرعة مخصص يهدف إلى جعل جميع كود الواجهة الأمامية تعمل بكفاءة. هذا يتعلق بالقالب من جانب الخادم ، وتحميل الموارد ، والتهيئة على العميل ، بشكل عام ، كل هذا.
لدينا مجموعة كاملة من المقاييس التي يتم إرسالها من الإنتاج إلى نظام التسجيل الخاص بنا. يمكننا التحكم في هذا في تجارب A / B. لدينا أدوات غير متصلة بالإنترنت ، بشكل عام ، نحن نتابع بنشاط كل هذا.
وقد استخدمنا هذه الأدوات عندما طبقنا هذا الكود الجديد الخاص بنا في React و TypeScript.
دعنا الآن نتتبع بمساعدة الأدوات غير المتصلة بالإنترنت (لأنني لم أتمكن من وضع تجربة صادقة عبر الإنترنت من شأنها أن تستخدم جميع مقاييسنا). دعونا نرى ما سيحدث إذا تراجعنا عن هذا الحل الحالي الخاص بنا لإنشاء تطبيق React على هذه المقاييس الرئيسية.
الأداة تعمل ببساطة شديدة. يتم أخذ شريحة من الطلبات ، في هذه الحالة ، يتم أخذ طلب بميزات في React ، لأنه لم تتم إعادة كتابة كل Serp في React بعد. ثم يتم إطلاق القوالب الخاصة بنا ، ويتم جمع القياسات ، وإدراجها في أداة مساعدة خاصة تقارن هذه النتائج والمقاييس وتجدها. في هذه الحالة ، تبقى النتائج ذات دلالة إحصائية فقط. بشكل عام ، كل شيء معقول هناك.
دعونا نرى ما سيحدث.
لم يُظهر تعطيل MultiPlugin ، الذي يجمع كل الترجمات بدلاً من الترجمة المطلوبة فقط ، أي تغييرات ذات دلالة إحصائية.
في البداية كنت منزعجًا بعض الشيء ، ثم أدركت ، في الواقع ، أن هذه ليست مشكلة ، لأنه ليس لدينا الآن العديد من الميزات التي تمت ترجمة العديد من الترجمات إلى React. لذلك ، عندما يكون هناك المزيد من هذه الميزات ، ستظهر هذه التغييرات المهمة بالتأكيد. إنه فقط الآن هناك ميزات معروضة بشكل أساسي في روسيا وليس لها ترجمات. ومقدار الكود الموجود في المكونات يتجاوز بشكل كبير كمية الترجمات. لذلك ، من غير المحسوس أن تكون جميع الترجمات في الطريق.
ربما يكون ملحوظًا في تجارب أكثر صدقًا ، إذا تم إجراء تجربة صادقة. لكن الأداة غير المتصلة بالإنترنت لم تُظهر هذه التغييرات.
إذا قمنا بتعطيل MainChunkPlugin ، فإن وقت بدء التفاعل يتباطأ ، كما أن تحميل HTML يتباطأ كثيرًا. لذلك ، فإن الشيء ضروري للغاية.
لماذا يتباطأ تحميل HTML ، لأن جميع التعليمات البرمجية التي تم تحميلها في هذا الجزء المنفصل بواسطة مورد منفصل مضمنة الآن في HTML. يبدو الأمر كما لو أننا نضمّن كل شيء ، لكن التفاعل يتباطأ أيضًا. من حيث المبدأ ، من المتوقع تماما.
والسؤال الآن: ماذا سيحدث إذا وضعت كل شيء في حزمة واحدة ، ولا تستخدم أي قطع ذات مكونات مشتركة؟ اتضح أن هذه ليست صورة سعيدة على الإطلاق.
يتباطأ العرض الأول بشكل كبير. التفاعل أيضًا تضاعف تقريبًا. هذا يجعل HTML أصغر حيث يبدأ تسليم جميع التعليمات البرمجية في مورد منفصل. لكن التفاعل ، كما ترى ، لا يساعد.
والتجمع. الشرائح الأخيرة.
يستغرق إنشاء تطبيق React App للمشروع الحالي ثلاث دقائق على جهاز كمبيوتر محمول. ومع كل أجراسنا وصفاراتنا - خمس دقائق. طويل؟
ومع ذلك ، في الواقع ، إذا جمعت معًا في حزمة واحدة ، فستكون ثلاث دقائق. الإنشاء بدون MultiPlugin يجعله أسرع من إنشاء تطبيق React. لكن كما أوضحت في الشرائح السابقة ، لا يمكننا رفض هذه التعديلات على نصوص البناء الأصلية ، لأنه بدونها ، ستصبح مقاييس السرعة سيئة للغاية.
لنستعرض الآن ما هو مفيد للتعلم من هذا التقرير.
بابل ليست الطريقة الوحيدة للعمل مع TypeScript. يمكن استخدام TSC و ts-node و ts-loader. إنه يعمل بشكل جيد.
ومع ذلك ، فإن عمليات التحقق من TypeScript ، وفحص النوع ، لا يلزم إجراؤها في كل مرة تقوم فيها بالبناء. هذا يبطئ كثيرا - كما تتذكر مرتين. لذلك ، من الأفضل وضع مثل هذه الأشياء في عمليات فحص منفصلة ، مثل خطافات الالتزام المسبق ، على سبيل المثال.
من الأفضل جمع المكونات المستخدمة بشكل متكرر في قطعة منفصلة. من المستحسن أيضًا تجميع المكونات المشتركة في أجزاء منفصلة ، لأن هذا يسمح بتحميل ما هو مطلوب فقط ، فقط فرق.
أهم شيء هو أنه إذا لم يكن لديك كل الأكواد المستخدمة في جميع الصفحات ، فأنت بحاجة إلى تقسيمها إلى إدخالات منفصلة ، وجمع حزم منفصلة وتنزيلها كما يرى المستخدم الأنواع المقابلة من نتائج البحث. قم بتنزيل الملفات التي تحتاجها فقط. هذا ، كما رأيت ، يعطي أفضل نتيجة. شيء واضح جدًا ، لكنني لست متأكدًا مما إذا كان الجميع يفعل ذلك ، لأنهم ما زالوا في تطبيق Create React.
الترجمة المتعددة طويلة جدا. لا تصدق ما إذا قال شخص ما أن الترجمة المتعددة على ما يرام وأن التخزين المؤقت في مكان ما بالداخل يمكنه التعامل مع كل هذا. يؤدي استخدام التحميل المسبق والمضمن أيضًا إلى نتائج.
عدة روابط عن المنجل:
- clck.ru/PdRdh و clck.ru/PdRjb - تقريران يدوران حول إعادة كتابة Serp in React ، هذه هي المرحلة الأولى ، حول كيفية وصولنا إلى هذا ولماذا بدأنا في القيام بذلك. التقرير الثاني عن كيفية تخطيطنا وفعلنا لكل هذا من وجهة نظر إدارية ، وما هي المراحل.
- clck.ru/PdRnr - تقرير حول مقاييس السرعة لدينا. إنه لمن تساءلوا فجأة عما هو موجود ، وكيف تعمل الأدوات عبر الإنترنت.
شكرا للجميع.