التجميع الذكي: كيفية خدمة التعليمات البرمجية القديمة فقط للمتصفحات القديمة

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

يتلقى موقع الويب اليوم جزءًا كبيرًا من حركة المرور الخاصة به من المتصفحات دائمة الخضرة - والتي يتمتع معظمها بدعم جيد لـ ES6 + ومعايير JavaScript الجديدة وواجهات برمجة التطبيقات لمنصة الويب الجديدة وسمات CSS. ومع ذلك ، لا تزال المتصفحات القديمة بحاجة إلى الدعم في المستقبل القريب - نسبة استخدامها كبيرة بما يكفي لعدم تجاهلها ، اعتمادًا على قاعدة المستخدمين لديك.

تكشف نظرة سريعة على جدول استخدام caniuse.com أن المتصفحات دائمة الخضرة تحتل حصة الأسد في سوق المتصفحات - أكثر من 75٪. على الرغم من ذلك ، فإن القاعدة هي أن تسبق CSS ، وتحويل جميع JavaScript إلى ES5 ، وتضمين polyfills لدعم كل مستخدم نهتم به.

في حين أن هذا مفهوم من سياق تاريخي - لطالما كان الويب يدور حول التحسين التدريجي - يبقى السؤال: هل نقوم بإبطاء الويب بالنسبة لغالبية مستخدمينا من أجل دعم مجموعة متضائلة من المتصفحات القديمة؟

Transpilation إلى ES5 ، و polyfills لمنصة الويب ، و ES6 + polyfills ، و CSS بادئة
طبقات التوافق المختلفة لتطبيق الويب. (عرض النسخة الكبيرة)

تكلفة دعم المتصفحات القديمة

دعنا نحاول فهم كيف يمكن للخطوات المختلفة في خط أنابيب بناء نموذجي أن تضيف وزناً لمواردنا الأمامية:

التحويل إلى ES5

لتقدير مقدار الوزن الذي يمكن أن يضيفه التحويل إلى حزمة JavaScript ، أخذت بعض مكتبات JavaScript الشائعة المكتوبة أصلاً في ES6 + وقارنت أحجام حزمها قبل الترجمة وبعدها:

مكتبة بحجم
(مصغر ES6)
بحجم
(مصغر ES5)
فرق
TodoMVC 8.4 كيلو بايت 11 كيلو بايت 24.5٪
قابل للسحب 53.5 كيلوبايت 77.9 كيلوبايت 31.3٪
لوكسون 75.4 كيلوبايت 100.3 كيلوبايت 24.8٪
Video.js 237.2 كيلوبايت 335.8 كيلوبايت 29.4٪
PixiJS 370.8 كيلوبايت 452 كيلو بايت 18٪

في المتوسط ​​، تكون الحزم غير المترجمة أصغر بنحو 25٪ من تلك التي تم تحويلها إلى ES5. هذا ليس مفاجئًا نظرًا لأن ES6 + يوفر طريقة أكثر إحكاما وتعبيرية لتمثيل المنطق المكافئ وأن نقل بعض هذه الميزات إلى ES5 يمكن أن يتطلب الكثير من التعليمات البرمجية.

ES6 + Polyfills

بينما يقوم Babel بعمل جيد في تطبيق التحويلات النحوية على كود ES6 + الخاص بنا ، إلا أن الميزات المضمنة التي تم تقديمها في ES6 + - مثل Promise و Map and Set وطرق المصفوفات والسلسلة الجديدة - لا تزال بحاجة إلى إعادة التعبئة. يمكن أن يضيف إدخال babel-polyfill كما هو ما يقرب من 90 كيلوبايت إلى حزمتك المصغرة.

المزيد بعد القفز! أكمل القراءة أدناه ↓

منصة ويب Polyfills

تم تبسيط تطوير تطبيقات الويب الحديثة نظرًا لتوافر عدد كبير من واجهات برمجة تطبيقات المتصفح الجديدة. العناصر الشائعة الاستخدام هي fetch ، لطلب الموارد ، IntersectionObserver ، لمراقبة رؤية العناصر بكفاءة ، ومواصفات URL ، مما يجعل قراءة عناوين URL على الويب ومعالجتها أسهل.

يمكن أن يكون لإضافة polyfill المتوافقة مع المواصفات لكل من هذه الميزات تأثير ملحوظ على حجم الحزمة.

بادئة CSS

أخيرًا ، لنلقِ نظرة على تأثير إضافة بادئات CSS. في حين أن البادئات لن تضيف الكثير من الوزن الميت إلى الحزم كما تفعل تحويلات البناء الأخرى - خاصةً لأنها تنضغط جيدًا عند Gzip'd - لا يزال هناك بعض المدخرات التي يتعين تحقيقها هنا.

مكتبة بحجم
(مصغر ، مسبوق لآخر 5 إصدارات من المتصفح)
بحجم
(مصغر ، مسبوق لإصدار المستعرض الأخير)
فرق
التمهيد 159 كيلو بايت 132 كيلو بايت 17٪
بولما 184 كيلو بايت 164 كيلو بايت 10.9٪
المؤسسة 139 كيلو بايت 118 كيلو بايت 15.1٪
واجهة المستخدم الدلالية 622 كيلو بايت 569 كيلو بايت 8.5٪

دليل عملي لكود كفاءة الشحن

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

الحزمة الحديثة أصغر من الحزمة القديمة لأنها فقدت بعض طبقات التوافق.
تشعب الحزم لدينا. (عرض النسخة الكبيرة)

هذه الفكرة ليست جديدة تمامًا. خدمات مثل Polyfill.io هي محاولات لتعويض بيئات المتصفح ديناميكيًا في وقت التشغيل. لكن مثل هذه الأساليب تعاني من بعض النواقص:

  • يقتصر اختيار polyfills على تلك المدرجة في الخدمة - ما لم تستضيف الخدمة وتحافظ عليها بنفسك.
  • نظرًا لأن polyfilling يحدث في وقت التشغيل وهي عملية حظر ، يمكن أن يكون وقت تحميل الصفحة أعلى بشكل ملحوظ للمستخدمين على المتصفحات القديمة.
  • يؤدي تقديم ملف polyfill حسب الطلب لكل مستخدم إلى إدخال الانتروبيا إلى النظام ، مما يجعل استكشاف الأخطاء وإصلاحها أكثر صعوبة عندما تسوء الأمور.

أيضًا ، هذا لا يحل مشكلة الوزن المضافة عن طريق ترشيح كود التطبيق ، والتي يمكن أن تكون في بعض الأحيان أكبر من polyfill نفسها.

دعونا نرى كيف يمكننا حل جميع مصادر سخام التي حددناها حتى الآن.

الأدوات التي سنحتاجها

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

1. تعريف المتصفحات الحديثة والقديمة

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

Firefox> = 53 ؛ الحافة> = 15 ؛ الكروم> = 58 ؛ iOS> = 10.1
المتصفحات التي تدعم ES6 + وسمات CSS الجديدة وواجهات برمجة تطبيقات المتصفح مثل Promises and Fetch. (عرض النسخة الكبيرة)

يمكن لتكوين قائمة المستعرضات في جذر مشروعك تخزين هذه المعلومات. يمكن استخدام الأقسام الفرعية "البيئة" لتوثيق مجموعتي المستعرضات ، مثل:

 [modern] Firefox >= 53 Edge >= 15 Chrome >= 58 iOS >= 10.1 [legacy] > 1%

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

2. ES6 + Transpiling و Polyfilling

لتحويل JavaScript الخاص بنا بطريقة تراعي البيئة ، سنستخدم babel-preset-env .

لنبدأ في تهيئة ملف .babelrc على جذر مشروعنا بهذا:

 { "presets": [ ["env", { "useBuiltIns": "entry"}] ] }

يتيح تمكين علامة useBuiltIns لشركة Babel إمكانية تعديل الميزات المضمنة بشكل انتقائي والتي تم تقديمها كجزء من ES6 +. نظرًا لأنه يقوم بتصفية polyfill لتشمل فقط تلك التي تتطلبها البيئة ، فإننا نخفف من تكلفة الشحن باستخدام babel-polyfill بالكامل.

لكي تعمل هذه العلامة ، سنحتاج أيضًا إلى استيراد babel-polyfill في نقطة الدخول الخاصة بنا.

 // In import "babel-polyfill";

سيؤدي القيام بذلك إلى استبدال استيراد babel-polyfill الكبير باستيراد دقيق ، تمت تصفيته حسب بيئة المتصفح التي نستهدفها.

 // Transformed output import "core-js/modules/es7.string.pad-start"; import "core-js/modules/es7.string.pad-end"; import "core-js/modules/web.timers"; …

3. ميزات منصة الويب Polyfilling

لشحن polyfills لميزات منصة الويب إلى مستخدمينا ، سنحتاج إلى إنشاء نقطتي دخول لكلا البيئتين:

 require('whatwg-fetch'); require('es6-promise').polyfill(); // … other polyfills

وهذا:

 // polyfills for modern browsers (if any) require('intersection-observer');

هذه هي الخطوة الوحيدة في تدفقنا التي تتطلب درجة معينة من الصيانة اليدوية. يمكننا أن نجعل هذه العملية أقل عرضة للخطأ عن طريق إضافة eslint-plugin-Compatible إلى المشروع. يحذرنا هذا المكون الإضافي عندما نستخدم ميزة متصفح لم يتم تعبئتها بعد.

4. بادئة CSS

أخيرًا ، دعنا نرى كيف يمكننا تقليل بادئات CSS للمتصفحات التي لا تتطلب ذلك. نظرًا لأن autoprefixer كانت إحدى الأدوات الأولى في النظام البيئي لدعم القراءة من ملف تكوين قائمة browserslist ، فليس لدينا الكثير لنفعله هنا.

يجب أن يكون إنشاء ملف تكوين PostCSS بسيط في جذر المشروع كافيًا:

 module.exports = { plugins: [ require('autoprefixer') ], }

ضع كل شيء معا

الآن بعد أن حددنا جميع تكوينات المكونات الإضافية المطلوبة ، يمكننا تجميع تكوين webpack الذي يقرأ هذه ويخرج بنائين منفصلين في مجلدات dist/modern و dist/legacy .

 const MiniCssExtractPlugin = require('mini-css-extract-plugin') const isModern = process.env.BROWSERSLIST_ENV === 'modern' const buildRoot = path.resolve(__dirname, "dist") module.exports = { entry: [ isModern ? './polyfills.modern.js' : './polyfills.legacy.js', "./main.js" ], output: { path: path.join(buildRoot, isModern ? 'modern' : 'legacy'), filename: 'bundle.[hash].js', }, module: { rules: [ { test: /\.jsx?$/, use: "babel-loader" }, { test: /\.css$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader'] } ]}, plugins: { new MiniCssExtractPlugin(), new HtmlWebpackPlugin({ template: 'index.hbs', filename: 'index.html', }), }, };

للإنهاء ، سننشئ بعض أوامر البناء في ملف package.json بنا:

 "scripts": { "build": "yarn build:legacy && yarn build:modern", "build:legacy": "BROWSERSLIST_ENV=legacy webpack -p --config webpack.config.js", "build:modern": "BROWSERSLIST_ENV=modern webpack -p --config webpack.config.js" }

هذا هو. يجب أن يمنحنا yarn build الآن بنائين متكافئين في الوظائف.

تقديم الحزمة المناسبة للمستخدمين

يساعدنا إنشاء تصميمات منفصلة في تحقيق النصف الأول فقط من هدفنا. ما زلنا بحاجة إلى تحديد الحزمة المناسبة وتقديمها للمستخدمين.

هل تتذكر تكوين قائمة المستعرضات الذي حددناه سابقًا؟ ألن يكون من الجيد أن نستخدم نفس التكوين لتحديد الفئة التي ينتمي إليها المستخدم؟

أدخل قائمة المتصفحات- useragent. كما يوحي الاسم ، يمكن browserslist-useragent قراءة تكوين قائمة browserslist الخاصة بنا ثم مطابقة وكيل مستخدم بالبيئة ذات الصلة. يوضح المثال التالي ذلك مع خادم Koa:

 const Koa = require('koa') const app = new Koa() const send = require('koa-send') const { matchesUA } = require('browserslist-useragent') var router = new Router() app.use(router.routes()) router.get('/', async (ctx, next) => { const useragent = ctx.get('User-Agent') const isModernUser = matchesUA(useragent, { env: 'modern', allowHigherVersions: true, }) const index = isModernUser ? 'dist/modern/index.html', 'dist/legacy/index.html' await send(ctx, index); });

هنا ، يضمن تعيين علامة allowHigherVersions أنه إذا تم إصدار إصدارات أحدث من المستعرض - تلك التي لم تصبح بعد جزءًا من قاعدة بيانات Can I Use - فسيظل تقريرًا صحيحًا للمتصفحات الحديثة.

تتمثل إحدى وظائف قائمة browserslist-useragent في ضمان مراعاة المراوغات الخاصة بالمنصة أثناء مطابقة وكلاء المستخدم. على سبيل المثال ، تستخدم جميع المتصفحات على iOS (بما في ذلك Chrome) WebKit كمحرك أساسي وستتم مطابقتها مع استعلام قائمة المتصفحات الخاص بـ Safari.

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

الخلاصة: هل هذا يستحق كل هذا العناء؟

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

1. الصيانة والاختبار

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

ومع ذلك ، هناك خطر نظري صغير مرتبط بالاعتماد على Babel لإنتاج حزمتين مختلفتين من الرموز ، كل منهما تحتاج إلى العمل بشكل جيد في بيئتها الخاصة.

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

2. بناء الوقت مقابل وقت التشغيل

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

3. السرعة المعززة تدريجياً

تصبح تجربة المستخدمين على المتصفحات الحديثة أسرع بشكل ملحوظ ، بينما يستمر المستخدمون على المتصفحات القديمة في الحصول على نفس الحزمة كما كان من قبل ، دون أي عواقب سلبية.

4. استخدام ميزات المتصفح الحديثة بسهولة

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

خدمة الحزمة التفاضلية في الإنتاج

نظرًا للمزايا المهمة ، اعتمدنا خط أنابيب البناء هذا عند إنشاء تجربة سداد جديدة للهاتف المحمول لعملاء Urban Ladder ، أحد أكبر بائعي التجزئة للأثاث والديكور في الهند.

في مجموعتنا المحسّنة بالفعل ، تمكنا من توفير ما يقرب من 20٪ من موارد Gzip'd CSS وجافا سكريبت التي ترسل الأسلاك إلى مستخدمي الهاتف المحمول الحديث. نظرًا لأن أكثر من 80٪ من زوارنا يوميًا كانوا يستخدمون هذه المتصفحات دائمة الخضرة ، فقد كان الجهد المبذول يستحق التأثير.

مزيد من الموارد

  • "تحميل Polyfills فقط عند الحاجة" ، فيليب والتون
  • @babel/preset-env
    إعداد بابل الذكي مسبقًا
  • قائمة المستعرضات "أدوات"
    النظام البيئي للمكونات الإضافية المصممة لقائمة المستعرضات
  • هل بإمكاني استخدم
    جدول المشاركة السوقية للمتصفح الحالي