التطوير
إذا كنت تتابع الأدلة، فمن المفترض أن يكون لديك فهم قوي لبعض أساسيات webpack. قبل أن نكمل، دعنا نلقي نظرة على إعداد بيئة تطوير تجعل حياتنا أسهل قليلًا.
لنبدأ بتعيين mode إلى 'development' و title إلى 'Development'.
webpack.config.js
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import HtmlWebpackPlugin from 'html-webpack-plugin';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
export default {
+ mode: 'development',
entry: {
index: './src/index.js',
print: './src/print.js',
},
plugins: [
new HtmlWebpackPlugin({
- title: 'Output Management',
+ title: 'Development',
}),
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
clean: true,
},
};استخدام Source Maps
عندما يقوم webpack بدمج الكود المصدري الخاص بك، قد يصبح من الصعب تتبع الأخطاء والتحذيرات إلى موقعها الأصلي. على سبيل المثال، إذا قمت بدمج ثلاثة ملفات مصدر (a.js و b.js و c.js) داخل ملف واحد (bundle.js) وكان أحد الملفات يحتوي على خطأ، فإن الـ stack trace سيشير إلى bundle.js. هذا ليس مفيدًا دائمًا لأنك غالبًا تريد معرفة الملف الأصلي الذي جاء منه الخطأ.
لتسهيل تتبع الأخطاء والتحذيرات، توفر JavaScript ما يسمى بـ source maps، والتي تقوم بربط الكود المترجم بالكود الأصلي. إذا كان الخطأ قادمًا من b.js، فإن source map ستخبرك بذلك بالضبط.
هناك العديد من الخيارات المختلفة المتاحة عندما يتعلق الأمر بالـ source maps. تأكد من الاطلاع عليها حتى تتمكن من ضبطها حسب احتياجاتك.
في هذا الدليل، سنستخدم خيار inline-source-map، وهو مناسب لأغراض الشرح (لكن ليس للإنتاج):
webpack.config.js
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import HtmlWebpackPlugin from 'html-webpack-plugin';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
export default {
mode: 'development',
entry: {
index: './src/index.js',
print: './src/print.js',
},
+ devtool: 'inline-source-map',
plugins: [
new HtmlWebpackPlugin({
title: 'Development',
}),
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
clean: true,
},
};الآن دعنا نتأكد أن لدينا شيئًا لنقوم بتصحيحه، لذلك سنقوم بإنشاء خطأ داخل ملف print.js:
src/print.js
export default function printMe() {
- console.log('I get called from print.js!');
+ cosnole.log('I get called from print.js!');
}قم بتشغيل npm run build، ويجب أن يتم البناء بشيء مشابه لهذا:
...
[webpack-cli] Compilation finished
asset index.bundle.js 1.38 MiB [emitted] (name: index)
asset print.bundle.js 6.25 KiB [emitted] (name: print)
asset index.html 272 bytes [emitted]
runtime modules 1.9 KiB 9 modules
cacheable modules 530 KiB
./src/index.js 406 bytes [built] [code generated]
./src/print.js 83 bytes [built] [code generated]
./node_modules/lodash/lodash.js 530 KiB [built] [code generated]
webpack 5.x.x compiled successfully in 706 msالآن افتح ملف index.html الناتج داخل المتصفح. اضغط على الزر وانظر إلى الـ console حيث يتم عرض الخطأ. يجب أن يبدو الخطأ كالتالي:
Uncaught ReferenceError: cosnole is not defined
at HTMLButtonElement.printMe (print.js:2)يمكننا أن نرى أن الخطأ يحتوي أيضًا على مرجع إلى الملف (print.js) ورقم السطر (2) الذي حدث فيه الخطأ. هذا رائع لأننا نعرف الآن بالضبط أين يجب أن ننظر لإصلاح المشكلة.
اختيار أداة تطوير
سرعان ما يصبح تشغيل npm run build يدويًا في كل مرة تريد فيها ترجمة الكود أمرًا مزعجًا.
هناك عدة خيارات متاحة في webpack تساعدك على ترجمة الكود تلقائيًا كلما تم تغييره:
- Watch Mode الخاصة بـ webpack
- webpack-dev-server
- webpack-dev-middleware
في أغلب الحالات، ربما سترغب باستخدام webpack-dev-server، لكن دعنا نستعرض جميع الخيارات السابقة.
استخدام Watch Mode
يمكنك توجيه webpack لكي "يراقب" جميع الملفات داخل dependency graph الخاصة بك بحثًا عن التغييرات. إذا تم تعديل أحد هذه الملفات، فسيتم إعادة ترجمة الكود تلقائيًا حتى لا تضطر لتشغيل البناء يدويًا.
دعنا نضيف npm script يقوم بتشغيل Watch Mode الخاصة بـ webpack:
package.json
{
"name": "webpack-demo",
"version": "1.0.0",
"description": "",
"private": true,
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
+ "watch": "webpack --watch",
"build": "webpack"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"html-webpack-plugin": "^5.6.6",
"webpack": "^5.105.0",
"webpack-cli": "^7.0.0"
},
"dependencies": {
"lodash": "^4.17.21"
}
}الآن قم بتشغيل npm run watch من سطر الأوامر ولاحظ كيف يقوم webpack بترجمة الكود.
ستلاحظ أنه لا يغلق سطر الأوامر لأنه يراقب الملفات حاليًا.
الآن، بينما webpack يراقب ملفاتك، دعنا نزيل الخطأ الذي أضفناه سابقًا:
src/print.js
export default function printMe() {
- cosnole.log('I get called from print.js!');
+ console.log('I get called from print.js!');
}الآن احفظ الملف وتحقق من نافذة الطرفية. يجب أن ترى أن webpack أعاد ترجمة الملف المعدل تلقائيًا!
العيب الوحيد هو أنك لا تزال بحاجة إلى تحديث المتصفح يدويًا لرؤية التغييرات. سيكون من الأفضل لو حدث ذلك تلقائيًا أيضًا، لذلك دعنا نجرب webpack-dev-server الذي يفعل ذلك بالضبط.
استخدام webpack-dev-server
يوفر لك webpack-dev-server خادم ويب بسيط مع إمكانية إعادة التحميل التلقائي (live reloading). دعنا نقوم بإعداده:
npm install --save-dev webpack-dev-serverقم بتعديل ملف الإعدادات لإخبار dev server بمكان البحث عن الملفات:
webpack.config.js
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import HtmlWebpackPlugin from 'html-webpack-plugin';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
export default {
mode: 'development',
entry: {
index: './src/index.js',
print: './src/print.js',
},
devtool: 'inline-source-map',
+ devServer: {
+ static: './dist',
+ },
plugins: [
new HtmlWebpackPlugin({
title: 'Development',
}),
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
clean: true,
},
+ optimization: {
+ runtimeChunk: 'single',
+ },
};هذا يخبر webpack-dev-server بأن يقوم بتقديم الملفات من مجلد dist على localhost:8080.
دعنا نضيف script لتشغيل dev server بسهولة:
package.json
{
"name": "webpack-demo",
"version": "1.0.0",
"description": "",
"private": true,
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"watch": "webpack --watch",
+ "start": "webpack serve --open",
"build": "webpack"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"html-webpack-plugin": "^5.6.6",
"webpack": "^5.105.0",
"webpack-cli": "^7.0.0",
"webpack-dev-server": "^5.2.3"
},
"dependencies": {
"lodash": "^4.17.21"
}
}الآن يمكننا تشغيل npm start من سطر الأوامر وسنرى أن المتصفح يفتح الصفحة تلقائيًا. إذا قمت الآن بتعديل أي من ملفات المصدر وحفظها، فسيقوم خادم الويب بإعادة التحميل تلقائيًا بعد ترجمة الكود. جرّب ذلك!
يحتوي webpack-dev-server على العديد من الخيارات القابلة للتخصيص. توجّه إلى التوثيق لمعرفة المزيد.
استخدام webpack-dev-middleware
webpack-dev-middleware هو wrapper يقوم بإرسال الملفات التي يعالجها webpack إلى السيرفر. يتم استخدامه داخليًا داخل webpack-dev-server، لكنه متاح أيضًا كحزمة مستقلة للسماح بإعدادات أكثر تخصيصًا عند الحاجة. سنلقي نظرة على مثال يجمع بين webpack-dev-middleware وسيرفر express.
دعنا نثبت express و webpack-dev-middleware حتى نبدأ:
npm install --save-dev express webpack-dev-middlewareالآن نحتاج إلى إجراء بعض التعديلات على ملف إعدادات webpack لضمان عمل الـ middleware بشكل صحيح:
webpack.config.js
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import HtmlWebpackPlugin from 'html-webpack-plugin';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
export default {
mode: 'development',
entry: {
index: './src/index.js',
print: './src/print.js',
},
devtool: 'inline-source-map',
devServer: {
static: './dist',
},
plugins: [
new HtmlWebpackPlugin({
title: 'Development',
}),
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
clean: true,
+ publicPath: '/',
},
};سيتم استخدام publicPath داخل ملف السيرفر أيضًا لضمان تقديم الملفات بشكل صحيح على http://localhost:3000. سنحدد رقم المنفذ لاحقًا. الخطوة التالية هي إعداد سيرفر express المخصص:
project
webpack-demo
├── package.json
├── package-lock.json
├── webpack.config.js
+ ├── server.js
├── /dist
├── /src
│ ├── index.js
│ └── print.js
└── /node_modulesserver.js
import express from "express";
import webpack from "webpack";
import webpackDevMiddleware from "webpack-dev-middleware";
import config from "./webpack.config.js";
const app = express();
const compiler = webpack(config);
// أخبر express باستخدام webpack-dev-middleware
// واستخدام ملف webpack.config.js كأساس للإعدادات.
app.use(
webpackDevMiddleware(compiler, {
publicPath: config.output.publicPath,
}),
);
// تقديم الملفات على المنفذ 3000.
app.listen(3000, () => {
console.log("Example app listening on port 3000!\n");
});الآن أضف npm script لتسهيل تشغيل السيرفر:
package.json
{
"name": "webpack-demo",
"version": "1.0.0",
"description": "",
"private": true,
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"watch": "webpack --watch",
"start": "webpack serve --open",
+ "server": "node server.js",
"build": "webpack"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"express": "^5.2.1",
"html-webpack-plugin": "^5.6.6",
"webpack": "^5.105.0",
"webpack-cli": "^7.0.0",
"webpack-dev-middleware": "^8.0.3",
"webpack-dev-server": "^5.2.3"
},
"dependencies": {
"lodash": "^4.17.21"
}
}الآن داخل الطرفية قم بتشغيل npm run server، ويجب أن تحصل على مخرجات مشابهة لهذا:
Example app listening on port 3000!
...
<i> [webpack-dev-middleware] asset index.bundle.js 1.38 MiB [emitted] (name: index)
<i> asset print.bundle.js 6.25 KiB [emitted] (name: print)
<i> asset index.html 274 bytes [emitted]
<i> runtime modules 1.9 KiB 9 modules
<i> cacheable modules 530 KiB
<i> ./src/index.js 406 bytes [built] [code generated]
<i> ./src/print.js 83 bytes [built] [code generated]
<i> ./node_modules/lodash/lodash.js 530 KiB [built] [code generated]
<i> webpack 5.x.x compiled successfully in 709 ms
<i> [webpack-dev-middleware] Compiled successfully.
<i> [webpack-dev-middleware] Compiling...
<i> [webpack-dev-middleware] assets by status 1.38 MiB [cached] 2 assets
<i> cached modules 530 KiB (javascript) 1.9 KiB (runtime) [cached] 12 modules
<i> webpack 5.x.x compiled successfully in 19 ms
<i> [webpack-dev-middleware] Compiled successfully.الآن افتح متصفحك وانتقل إلى http://localhost:3000. يجب أن ترى تطبيق webpack الخاص بك يعمل بشكل صحيح!
ضبط محرر النصوص الخاص بك
عند استخدام الترجمة التلقائية للكود، قد تواجه مشاكل عند حفظ الملفات. بعض المحررات تحتوي على ميزة "safe write" والتي قد تتداخل مع إعادة الترجمة.
لتعطيل هذه الميزة في بعض المحررات الشائعة، راجع القائمة التالية:
- Sublime Text 3: أضف
atomic_save: 'false'إلى تفضيلات المستخدم الخاصة بك. - JetBrains IDEs (مثل WebStorm): قم بإلغاء تحديد "Use safe write" من
Preferences > Appearance & Behavior > System Settings. - Vim: أضف
:set backupcopy=yesإلى إعداداتك.
الخاتمة
الآن بعد أن تعلمت كيفية ترجمة الكود تلقائيًا وتشغيل سيرفر تطوير، يمكنك الانتقال إلى الدليل التالي الذي سيغطي Code Splitting.



