JavaScript المتقدم (ES6+)

محزّمات الوحدات (Module Bundlers)

13 دقيقة الدرس 33 من 40

محزّمات الوحدات (Module Bundlers)

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

ما هي محزّمات الوحدات؟

محزّمات الوحدات هي أدوات بناء تأخذ الوحدات مع التبعيات وتولّد موارد ثابتة تمثل تلك الوحدات:

الغرض الأساسي: تجمع المحزّمات ملفات JavaScript متعددة في ملفات محسّنة أقل، وتحل التبعيات، وتحوّل الكود (transpile، minify)، وتتيح ميزات مثل تقسيم الكود وإزالة الكود غير المستخدم.

لماذا نحتاج إلى محزّمات الوحدات؟

تحل محزّمات الوحدات عدة مشاكل حرجة في تطوير الويب الحديث:

المشاكل التي تحلها المحزّمات: 1. توافق المتصفح - ليست كل المتصفحات تدعم وحدات ES6 - تحويل JavaScript الحديث إلى ES5 2. تحسين الأداء - تقليل عدد طلبات HTTP - تصغير وضغط الكود - إزالة الكود غير المستخدم (tree shaking) 3. إدارة التبعيات - حل تبعيات الوحدات تلقائياً - التعامل مع حزم npm و node_modules 4. تجربة المطور - استبدال الوحدات الساخن (HMR) - خرائط المصدر للتصحيح - خادم التطوير مع إعادة التحميل التلقائي 5. إدارة الموارد - معالجة CSS والصور والخطوط - تجميع جميع الموارد معاً

محزّمات الوحدات الشائعة

لنستكشف المحزّمات الأكثر شعبية في نظام JavaScript البيئي:

1. Webpack - الأكثر شعبية وغنى بالميزات - قابل للتكوين بدرجة عالية - نظام بيئي كبير من الإضافات والمحمّلات - منحنى تعلم أكثر حدة 2. Rollup - ممتاز للمكتبات - ينتج مخرجات أنظف - إزالة أفضل للكود غير المستخدم - تكوين أبسط 3. Vite - حديث وسريع للغاية - يستخدم وحدات ES الأصلية في التطوير - بناءات إنتاج قائمة على Rollup - الحد الأدنى من التكوين 4. Parcel - بدون تكوين - سريع وسهل الاستخدام - رائع للمبتدئين - تحويل الموارد التلقائي 5. esbuild - سريع للغاية (مكتوب بـ Go) - واجهة برمجة بسيطة - تستخدمه أدوات أخرى (Vite يستخدم esbuild) - نظام إضافات محدود

أساسيات Webpack

Webpack هو المحزّم الأكثر استخداماً. إليك تكوين أساسي:

webpack.config.js: const path = require('path'); module.exports = { // نقطة الدخول - من أين تبدأ التحزيم entry: './src/index.js', // المخرجات - أين توضع الحزمة output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') }, // الوضع - تطوير أو إنتاج mode: 'development', // قواعد الوحدات - كيفية معالجة أنواع الملفات المختلفة module: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'] } } }, { test: /\.css$/, use: ['style-loader', 'css-loader'] } ] }, // تكوين خادم التطوير devServer: { static: './dist', hot: true } };
نصيحة: ابدأ بتكوين بسيط وأضف الميزات تدريجياً حسب الحاجة. معظم المشاريع لا تحتاج إلى تكوينات Webpack معقدة.

فهم نقاط الدخول

تخبر نقاط الدخول المحزّم من أين يبدأ بناء رسم بياني التبعيات:

نقطة دخول واحدة: module.exports = { entry: './src/index.js' }; نقاط دخول متعددة: module.exports = { entry: { app: './src/app.js', admin: './src/admin.js' }, output: { filename: '[name].bundle.js', path: path.resolve(__dirname, 'dist') } }; // ينشئ: app.bundle.js و admin.bundle.js

المحمّلات (Loaders) والإضافات (Plugins)

تحوّل المحمّلات الملفات، بينما تؤدي الإضافات مهام أوسع:

المحمّلات الشائعة: babel-loader: تحويل ES6+ إلى ES5 { test: /\.js$/, use: 'babel-loader' } css-loader: استيراد ملفات CSS { test: /\.css$/, use: ['style-loader', 'css-loader'] } file-loader: التعامل مع الصور والخطوط { test: /\.(png|jpg|gif)$/, use: ['file-loader'] } sass-loader: تجميع Sass إلى CSS { test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader'] }
الإضافات الشائعة: const HtmlWebpackPlugin = require('html-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); module.exports = { plugins: [ // توليد ملف HTML مع علامات script new HtmlWebpackPlugin({ template: './src/index.html' }), // استخراج CSS إلى ملفات منفصلة new MiniCssExtractPlugin({ filename: '[name].css' }) ] };

تقسيم الكود (Code Splitting)

قسّم كودك إلى أجزاء أصغر يمكن تحميلها عند الطلب:

الاستيرادات الديناميكية لتقسيم الكود: // بدلاً من: import Calculator from './calculator.js'; // استخدم الاستيراد الديناميكي: button.addEventListener('click', async () => { const { default: Calculator } = await import('./calculator.js'); const calc = new Calculator(); calc.calculate(); }); // ينشئ Webpack جزءاً منفصلاً تلقائياً
فصل كود المورّدين والتطبيق: module.exports = { optimization: { splitChunks: { chunks: 'all', cacheGroups: { vendor: { test: /[\\/]node_modules[\\/]/, name: 'vendors', priority: 10 }, common: { minChunks: 2, priority: 5, reuseExistingChunk: true } } } } };

إزالة الكود غير المستخدم (Tree Shaking)

تزيل إزالة الكود غير المستخدم الكود غير المستخدم من الحزمة النهائية:

مثال - إزالة الكود غير المستخدم في العمل: // utils.js export function used() { return 'This function is used'; } export function unused() { return 'This function is never called'; } // app.js import { used } from './utils.js'; console.log(used()); // في بناء الإنتاج، ستتم إزالة unused() // هذا يقلل حجم الحزمة
مهم: تعمل إزالة الكود غير المستخدم بشكل أفضل مع وحدات ES6. لا يمكن إزالة الكود غير المستخدم من وحدات CommonJS (require/module.exports) بفعالية.

Vite - البديل الحديث

يقدم Vite تجربة تطوير أسرع بالحد الأدنى من التكوين:

vite.config.js: import { defineConfig } from 'vite'; export default defineConfig({ // الدليل الجذر root: './src', // دليل مخرجات البناء build: { outDir: '../dist', rollupOptions: { input: { main: './src/index.html' } } }, // خادم التطوير server: { port: 3000, open: true } });
بدء Vite: # خادم التطوير npm run dev # بناء الإنتاج npm run build # معاينة بناء الإنتاج npm run preview

وحدات ES في المتصفحات

تدعم المتصفحات الحديثة وحدات ES بشكل أصلي، لكن المحزّمات لا تزال تقدم فوائد:

وحدات ES الأصلية في HTML: <!DOCTYPE html> <html> <head> <title>الوحدات الأصلية</title> </head> <body> <!-- دعم الوحدات الأصلية --> <script type="module"> import { add } from './math.js'; console.log(add(2, 3)); </script> <!-- خرائط الاستيراد للمحددات المجردة --> <script type="importmap"> { "imports": { "lodash": "https://cdn.skypack.dev/lodash" } } </script> <script type="module"> import _ from 'lodash'; console.log(_.capitalize('hello')); </script> </body> </html>
قيد: تجري الوحدات الأصلية طلب HTTP واحد لكل وحدة. للتطبيقات الكبيرة مع العديد من الوحدات، قد يكون هذا بطيئاً. تحسّن المحزّمات هذا بدمج الوحدات.

تقنيات تحسين البناء

حسّن بناءات الإنتاج الخاصة بك للأداء:

تكوين Webpack للإنتاج: const TerserPlugin = require('terser-webpack-plugin'); module.exports = { mode: 'production', optimization: { minimize: true, minimizer: [new TerserPlugin({ terserOptions: { compress: { drop_console: true, // إزالة console.log }, }, })], // تقسيم الأجزاء splitChunks: { chunks: 'all', }, // جزء وقت التشغيل للتخزين المؤقت الأفضل runtimeChunk: 'single', }, // المخرجات مع تجزئة المحتوى للتخزين المؤقت output: { filename: '[name].[contenthash].js', path: path.resolve(__dirname, 'dist'), clean: true // تنظيف مجلد dist قبل البناء } };

بناءات التطوير مقابل الإنتاج

تكوينات مختلفة لبيئات مختلفة:

التطوير: - بناءات أسرع - خرائط المصدر للتصحيح - استبدال الوحدات الساخن (HMR) - مخرجات قابلة للقراءة - بدون تصغير الإنتاج: - محسّن للأداء - مصغّر ومضغوط - إزالة الكود غير المستخدم مفعّلة - تقسيم الكود - تحسين الموارد - تجزئة المحتوى للتخزين المؤقت

مثال من العالم الحقيقي: مشروع Webpack

إليك مثالاً كاملاً على بنية مشروع Webpack:

بنية المشروع: my-app/ ├── src/ │ ├── index.js │ ├── styles.css │ ├── components/ │ │ ├── header.js │ │ └── footer.js │ └── utils/ │ └── helpers.js ├── dist/ (مولّد) ├── package.json └── webpack.config.js package.json: { "scripts": { "dev": "webpack serve --mode development", "build": "webpack --mode production" }, "devDependencies": { "webpack": "^5.88.0", "webpack-cli": "^5.1.0", "webpack-dev-server": "^4.15.0", "babel-loader": "^9.1.0", "@babel/core": "^7.22.0", "@babel/preset-env": "^7.22.0" } } src/index.js: import './styles.css'; import { Header } from './components/header.js'; import { Footer } from './components/footer.js'; import { formatDate } from './utils/helpers.js'; const app = document.getElementById('app'); app.innerHTML = ` ${Header()} <main> <p>Today is ${formatDate(new Date())}</p> </main> ${Footer()} `; // استيراد ديناميكي لتقسيم الكود document.getElementById('load-chart').addEventListener('click', async () => { const { Chart } = await import('./components/chart.js'); new Chart().render(); });

تمرين تطبيقي:

المهمة: أعد مشروع Webpack بسيط مع:

  1. نقطة دخول في src/index.js
  2. مخرجات إلى dist/bundle.js
  3. محمّل Babel لتحويل ES6+
  4. محمّل CSS للأنماط
  5. خادم تطوير على المنفذ 8080

الحل:

webpack.config.js: const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist'), clean: true }, module: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: [['@babel/preset-env', { targets: "> 0.25%, not dead" }]] } } }, { test: /\.css$/, use: ['style-loader', 'css-loader'] } ] }, plugins: [ new HtmlWebpackPlugin({ template: './src/index.html' }) ], devServer: { static: './dist', port: 8080, hot: true, open: true }, mode: 'development' }; الأوامر: npm install --save-dev webpack webpack-cli webpack-dev-server npm install --save-dev babel-loader @babel/core @babel/preset-env npm install --save-dev style-loader css-loader html-webpack-plugin npm run dev // بدء خادم التطوير

الملخص

في هذا الدرس، تعلمت:

  • محزّمات الوحدات تجمع وتحسّن وحدات JavaScript للإنتاج
  • المحزّمات الشائعة تشمل Webpack و Rollup و Vite و Parcel و esbuild
  • يستخدم Webpack المحمّلات لتحويل الملفات والإضافات للمهام الأوسع
  • يسمح تقسيم الكود بتحميل الكود عند الطلب لأداء أفضل
  • إزالة الكود غير المستخدم تزيل الكود غير المستخدم من حزمك
  • Vite يقدم تجربة تطوير حديثة وسريعة بالحد الأدنى من التكوين
  • يجب تحسين بناءات الإنتاج بالتصغير وتقسيم الكود
التالي: في الدرس التالي، سنستكشف أنماط التصميم في JavaScript لكتابة كود نظيف وسهل الصيانة!