تعمل استراتيجية جديدة لتقسيم حزمة الويب في Next.js وGatsby على تقليل الرمز المكرر لتحسين أداء تحميل الصفحة.
يعمل Chrome على التعاون من خلال الأدوات في منظومة JavaScript المتكاملة المفتوحة المصدر. تم مؤخرًا إدخال عدد من التحسينات الجديدة لتحسين أداء تحميل Next.js Gatsby تتناول هذه المقالة استراتيجية تقسيم دقيقة محسَّنة. التي يتم شحنها الآن بشكل افتراضي في كلا إطاري العمل.
مقدمة
على غرار العديد من أُطر عمل الويب، يستخدم Next.js وGatsby webpack كحزمة أساسية.
بِرْنَامِجْ. تم طرح الإصدار 3 من webpack.
CommonsChunkPlugin
لإتاحة
وحدات إخراج مشتركة بين نقاط الإدخال المختلفة في "شائع" واحد (أو عدد قليل) مقطع (أو
الأجزاء). يمكن تنزيل الرمز المشترك بشكل منفصل وتخزينه في ذاكرة التخزين المؤقت للمتصفح مبكرًا،
تؤدي إلى تحسين أداء التحميل.
وأصبح هذا النمط شائعًا في العديد من أطر تطبيقات الصفحة الواحدة التي استخدمت نقطة دخول بدا الإعداد التالي على النحو التالي:
على الرغم من أن مفهوم تجميع جميع التعليمات البرمجية للوحدة المشتركة في مقطع واحد له دلالات عملية
والقيود. يمكن تنزيل الوحدات غير المشتركة في كل نقطة دخول للمسارات التي لا تستخدمها
يؤدي إلى تنزيل عدد أكبر من اللازم من الرمز. على سبيل المثال، عند تحميل page1
مقطع common
، فإنه يحمّل رمز moduleC
على الرغم من أن page1
لا يستخدم moduleC
.
لهذا السبب، إلى جانب عدد قليل من الأسباب الأخرى، أزال الإصدار 4 من webpack المكوّن الإضافي لصالح علامة
أولاً: SplitChunksPlugin
تقسيم محسّن
تكون الإعدادات التلقائية لـ SplitChunksPlugin
مناسبة لمعظم المستخدمين. تُعد المقاطع المقسمة المتعددة
يتم إنشاؤها استنادًا إلى عدد من الشروط
لمنع جلب رمز مكرّر في مسارات متعدّدة.
ومع ذلك، لا تزال العديد من أطر عمل الويب التي تستخدم هذا المكون الإضافي تتبع "single-commons" طريقة معالجة المقطع
التقسيم. على سبيل المثال، سينشئ Next.js حزمة commons
تحتوي على أي وحدة
يتم استخدامها في أكثر من 50% من الصفحات وجميع تبعيات إطارات العمل (react
وreact-dom
وما إلى ذلك).
const splitChunksConfigs = {
…
prod: {
chunks: 'all',
cacheGroups: {
default: false,
vendors: false,
commons: {
name: 'commons',
chunks: 'all',
minChunks: totalPages > 2 ? totalPages * 0.5 : 2,
},
react: {
name: 'commons',
chunks: 'all',
test: /[\\/]node_modules[\\/](react|react-dom|scheduler|use-subscription)[\\/]/,
},
},
},
على الرغم من أن تضمين تعليمة برمجية تعتمد على إطار العمل في مقطع مشترك يعني أنه يمكن تنزيلها التخزين المؤقت لأي نقطة دخول، والدليل القائم على الاستخدام لتضمين الوحدات الشائعة المستخدمة في أكثر من نصف الصفحات ليس فعالاً جدًا سيؤدي تعديل هذه النسبة إلى إحدى النتيجتين التاليتين:
- وفي حال تقليل النسبة، يتم تنزيل المزيد من الرموز غير الضرورية.
- في حال زيادة النسبة، يتم تكرار المزيد من الرموز عبر مسارات متعددة.
لحل هذه المشكلة، اعتمدت Next.js علامة
التكوين لـ SplitChunksPlugin
التي تقلل
أي رموز غير ضرورية لأي مسار.
- يتم تقسيم أي وحدة تابعة لجهة خارجية كبيرة بما يكفي (أكبر من 160 كيلوبايت) إلى وحدة خاصة بها. مقطع
- يتم إنشاء مقطع
frameworks
منفصل لتبعيات إطار العمل (react
وreact-dom
وما إلى ذلك) - يتم إنشاء العدد المطلوب من المقاطع المشتركة (ما يصل إلى 25 مقطعًا)
- تم تغيير الحد الأدنى لحجم المقطع الذي سيتم إنشاؤه إلى 20 كيلوبايت
وتوفّر استراتيجية التقسيم الدقيقة هذه المزايا التالية:
- تحسين أوقات تحميل الصفحات يُعد إنشاء أجزاء متعددة مشتركة، بدلاً من مقطع واحد، إلى تقليل مقدار الرموز غير اللازمة (أو المكررة) لأي نقطة إدخال.
- تحسين التخزين المؤقت أثناء عمليات التنقّل تقسيم المكتبات الكبيرة وتبعيات إطار العمل إلى أجزاء منفصلة يقلل من إمكانية إلغاء ذاكرة التخزين المؤقت نظرًا لأنه من غير المحتمل أن حتى يتم إجراء الترقية.
يمكنك الاطّلاع على الإعدادات الكاملة التي اعتمدتها Next.js في webpack-config.ts
.
المزيد من طلبات HTTP
SplitChunksPlugin
حدد أساس التقسيم الدقيق وتطبيق هذا النهج على
إطار عمل مثل Next.js مفهومًا جديدًا تمامًا. ومع ذلك، لا تزال العديد من الأطر
تستخدم استدلال واحد و"شائع" الحزم لعدة أسباب. يتضمن ذلك القلق من أن
فالعديد من طلبات HTTP يمكن أن تؤثر سلبًا في أداء الموقع الإلكتروني.
يمكن للمتصفّحات فتح عدد محدود من اتصالات بروتوكول TCP لمصدر واحد (6 فقط بالنسبة إلى Chrome)، لذا ومن خلال خفض عدد المقاطع التي تنتجها أداة التجميع، يمكن أن يضمن ذلك إجمالي عدد الطلبات يبقى أقل من هذا الحدّ. ومع ذلك، ينطبق هذا فقط على HTTP/1.1. تعدد الإرسال في HTTP/2 تسمح ببث طلبات متعددة بالتوازي باستخدام اتصال واحد على المصدر. بعبارة أخرى، لا داعي للقلق بشكل عام بشأن الحدّ من عدد المجموعات الصادرة عن أداة التجميع.
تتوافق جميع المتصفحات الرئيسية مع HTTP/2. فريقا Chrome وNext.js
معرفة ما إذا كانت زيادة عدد الطلبات عن طريق تقسيم عنصر "commons" المفرد في Next.js مجموعة
إلى أجزاء مشتركة متعددة على أداء التحميل بأي شكل من الأشكال. بدأوا بقياس
أداء موقع واحد مع تعديل الحد الأقصى لعدد الطلبات المتوازية باستخدام
maxInitialRequests
الموقع.
وفي متوسط ثلاث عمليات لتجارب متعددة على صفحة ويب واحدة،
load
،
start-render
وظل مقياس سرعة عرض المحتوى على الصفحة كما هو عند تغيير الحد الأقصى
عدد الطلبات (من 5 إلى 15). ومن المثير للاهتمام أننا لاحظنا انخفاضًا طفيفًا في النفقات العامة للأداء فقط
بعد تقسيمها بشدة على مئات الطلبات.
وقد أظهر ذلك أن الالتزام بالحد الأدنى الموثوق به (من 20 إلى 25 طلبًا) قد حقق التوازن الصحيح
بين أداء التحميل وكفاءة التخزين المؤقت بعد إجراء بعض الاختبارات المرجعية، تم تحديد 25 باعتباره
عدد maxInitialRequest
.
أدى تعديل الحد الأقصى لعدد الطلبات التي تحدث بالتوازي إلى حدوث أكثر من طلب وفصلها بشكل مناسب لكل نقطة دخول إلى حد كبير مقدار التعليمات البرمجية غير اللازمة للصفحة نفسها.
كانت هذه التجربة تتعلق فقط بتعديل عدد الطلبات لمعرفة ما إذا كان هناك أي
تأثير سلبي في أداء تحميل الصفحة تشير النتائج إلى أنّ ضبط maxInitialRequests
على
كانت الإضافة 25
في الصفحة التجريبية مثالية لأنّها قلّلت حجم حمولة JavaScript بدون إبطاء.
أسفل الصفحة. لا يزال إجمالي مقدار JavaScript المطلوب لترطيب الصفحة قائمًا
نفس الشيء، وهو ما يفسر سبب عدم تحسّن أداء تحميل الصفحة بالضرورة مع
مقدار التعليمات البرمجية.
يستخدم webpack 30 كيلوبايت كحد أدنى افتراضي لحجم المقطع المطلوب إنشاؤه. ومع ذلك، يمكن أن يساعد إقران
بدلاً من ذلك، أدّى استخدام قيمة maxInitialRequests
التي تبلغ 25 مع حد أدنى للحجم يبلغ 20 كيلوبايت إلى تحسين في التخزين المؤقت.
تقليل الحجم باستخدام المقاطع الدقيقة
تعتمد العديد من أطر العمل، بما في ذلك Next.js، على التوجيه من جهة العميل (الذي تتم معالجته بواسطة JavaScript) لإدخال علامات نص برمجي أحدث لكل انتقال للمسار. ولكن كيف يحددون هذه المجموعات الديناميكية مسبقًا في وقت الإنشاء؟
يستخدم Next.js ملف بيان إنشاء من جانب الخادم لتحديد المقاطع الإخراج التي تستخدمها نقاط الدخول المختلفة. ولتوفير هذه المعلومات للعميل أيضًا، يمكن استخدام موجز من جانب العميل إنشاء ملف بيان لتعيين جميع التبعيات لكل نقطة إدخال.
// Returns a promise for the dependencies for a particular route
getDependencies (route) {
return this.promisedBuildManifest.then(
man => (man[route] && man[route].map(url => `/_next/${url}`)) || []
)
}
وتم طرح استراتيجية التقسيم الأكثر دقة هذه لأول مرة في Next.js وراء علامة، حيث تم اختبارها على عدد المستخدمين في مرحلة مبكرة. رأى العديد منهم انخفاضًا كبيرًا في إجمالي عدد رموز JavaScript المستخدَمة في بأكمله:
الموقع الإلكتروني | التغيير في إجمالي JavaScript | نسبة الاختلاف |
---|---|---|
https://www.barnebys.com/ | -238 كيلوبايت | -23% |
https://sumup.com/ | -220 كيلوبايت | -30% |
https://www.hashicorp.com/ | -11 ميغابايت | -71% |
تم شحن النسخة النهائية تلقائيًا في الإصدار 9.2.
Gatsby
اعتاد Gatsby على اتباع النهج نفسه المتمثل في استخدام إرشادات لتحديد الوحدات المشتركة:
config.optimization = {
…
splitChunks: {
name: false,
chunks: `all`,
cacheGroups: {
default: false,
vendors: false,
commons: {
name: `commons`,
chunks: `all`,
// if a chunk is used more than half the components count,
// we can assume it's pretty global
minChunks: componentsCount > 2 ? componentsCount * 0.5 : 2,
},
react: {
name: `commons`,
chunks: `all`,
test: /[\\/]node_modules[\\/](react|react-dom|scheduler)[\\/]/,
},
ومن خلال تحسين إعدادات حزمة الويب لديها لاعتماد استراتيجية تقسيم دقيقة مماثلة، استطاعت الشركة أيضًا لاحظت انخفاضًا كبيرًا في JavaScript في العديد من المواقع الإلكترونية الكبيرة:
الموقع الإلكتروني | التغيير في إجمالي JavaScript | نسبة الاختلاف |
---|---|---|
https://www.gatsbyjs.org/ | -680 كيلوبايت | -22% |
https://www.thirdandgrove.com/ | -390 كيلوبايت | -25% |
https://ghost.org/ | -1.1 ميغابايت | -35% |
https://reactjs.org/ | -80 كيلوبايت | -8% |
ألقِ نظرة على PR لفهم كيفية تنفيذ هذا المنطق في تهيئة حزمة الويب، والذي يتم شحنه تلقائيًا في الإصدار 2.20.7.
الخاتمة
لا يقتصر مفهوم شحن الأجزاء الدقيقة على Next.js أو Gatsby أو حتى webpack. كل الأقسام تحسين استراتيجية تقسيم التطبيق إذا كان يتبع مجموعة كبيرة من "الشائعة" نهج الحزمة، بصرف النظر عن أداة تجميع الوحدات المستخدمة أو إطار العمل أو الوحدة.
- إذا كنت ترغب في رؤية تحسينات التقسيم نفسها المطبقة على تطبيق vanilla React، يمكنك الاطّلاع على نموذج React هذا التطبيق. تستخدم نسخة مبسّطة من استراتيجية التقسيم الدقيق ويمكن أن تساعدك في بدء تطبيق نوعًا من المنطق لموقعك.
- بالنسبة إلى البيانات المجمّعة، يتم إنشاء المقاطع بشكل دقيق بشكل تلقائي. إلقاء نظرة على
manualChunks
إذا كنت تريد تنفيذ الإجراء يدويًا تكوين السلوك.