إزالة الشيفرة الميتة (Tree Shaking)
يمكن أن نضيف شيفرة إلى حزمتنا لا تُستخدم في أي مكان في تطبيقنا. يمكن إزالة هذه الشيفرة الميتة لتقليل حجم الحزمة ومنع تحميل بيانات إضافية دون حاجة. وتُسمى عملية إزالة الشيفرة الميتة قبل إضافتها إلى حزمتنا بـ إزالة الشيفرة الميتة (tree shaking).
على الرغم من أن إزالة الشيفرة الميتة تعمل مع الوحدات البسيطة مثل وحدة math، هناك بعض الحالات التي قد تكون فيها صعبة.
المفاهيم
تهدف إزالة الشيفرة الميتة إلى إزالة الشيفرة التي لن تُستخدم أبدًا من حزمة JavaScript نهائية. وإذا نُفذت بشكل صحيح، يمكنها تقليل حجم حزم JavaScript وخفض زمن التنزيل والتحليل و، في بعض الحالات، التنفيذ. في معظم تطبيقات JavaScript الحديثة التي تستخدم أداة تجميع الوحدات (مثل webpack أو Rollup)، من المتوقع أن تكون أداة التجميع هي التي تزيل الشيفرة الميتة تلقائيًا.
تخيل تطبيقك وتبعياته على هيئة شجرة بناء نحوي مجرد (نريد أن «نُرجّ» شجرة الصياغة لتحسينها). كل عقدة في الشجرة هي تبعية تمنح تطبيقك وظائف. في إزالة الشيفرة الميتة، تُعامل ملفات الإدخال على أنها رسم بياني. كل عقدة في الرسم البياني هي عبارة ذات المستوى الأعلى تسمى «جزءًا» في الشيفرة. وإزالة الشيفرة الميتة هي اجتياز للرسم البياني يبدأ من نقطة الدخول ويضع علامة على أي مسارات تم اجتيازها لتضمينها.
يمكن لكل مكوّن أن يصرّح بالرموز، ويشير إلى الرموز، ويعتمد على ملفات أخرى. حتى تُعلَّم الأجزاء على أنها ذات آثار جانبية أو ليست كذلك. على سبيل المثال، العبارة let firstName = 'Jane' لا تُعد لها آثار جانبية، ويمكن إزالتها دون أي فرق ملحوظ إذا لم يحتج أحد إلى firstName. لكن العبارة let firstName = getName() لها آثار جانبية، لأن استدعاء getName() لا يمكن إزالته دون تغيير معنى الشيفرة، حتى لو لم يحتج أحد إلى firstName.
الاستيرادات
يمكن إجراء إزالة الشيفرة الميتة فقط للوحدات المعرّفة بصيغة وحدات ES2015 (import وexport). وتحدد طريقة استيرادك للوحدات ما إذا كان من الممكن إجراء إزالة الشيفرة الميتة لها.
تبدأ إزالة الشيفرة الميتة بزيارة جميع أجزاء ملف نقطة الدخول التي لها آثار جانبية، ثم تعبر حواف الرسم البياني حتى يتم الوصول إلى أقسام جديدة. بمجرد اكتمال الاجتياز، لا تتضمن حزمة JavaScript إلا الأجزاء التي تم الوصول إليها أثناء الاجتياز. وتُترك بقية الأجزاء خارجها. لنقل إننا نعرّف ملف utilities.js التالي:
export function read(props) {
return props.book
}
export function nap(props) {
return props.winks
}
ثم لدينا ملف index.js التالي:
import { read } from 'utilities';
eventHandler = (e) => {
read({ book: e.target.value })
}
في هذا المثال، لا تعد nap() مهمة، لذلك لن تُضمَّن إلى الحزمة.
الآثار الجانبية
عندما نستورد وحدة ES6، تُنفَّذ هذه الوحدة فورًا. وقد يحدث أننا لا نشير إلى صادرات الوحدة في أي مكان في شيفرتنا، لكن الوحدة نفسها تؤثر في النطاق العام أثناء تنفيذها (مثل polyfills أو أوراق أنماط عالمية). يطلق على ذلك أثر جانبي. ورغم أننا لا نشير إلى صادرات الوحدة نفسها، إذا كانت الوحدة قد صدّرت قيمًا في الأصل، فلا يمكن إجراء إزالة الشيفرة الميتة لها بسبب السلوك الخاص عند استيرادها!
يقدم توثيق Webpack شرحًا واضحًا لإزالة الشيفرة الميتة وكيفية تجنب كسرها.