الحفاظ على التحكم: دليل Webpack و React ، Pt. 1
نشرت: 2022-03-11عند بدء مشروع React جديد ، لديك العديد من القوالب للاختيار من بينها: إنشاء تطبيق React ، و React react-boilerplate
، و React Starter Kit ، على سبيل المثال لا الحصر.
هذه القوالب ، التي اعتمدها آلاف المطورين ، قادرة على دعم تطوير التطبيقات على نطاق واسع جدًا. لكنهم يتركون تجربة المطور ويجمعون الناتج مثقلًا بالعديد من الإعدادات الافتراضية ، والتي قد لا تكون مثالية.
إذا كنت ترغب في الحفاظ على درجة أكبر من التحكم في عملية الإنشاء الخاصة بك ، فقد تختار الاستثمار في تكوين Webpack المخصص. كما ستتعلم من هذا البرنامج التعليمي Webpack ، فإن هذه المهمة ليست معقدة للغاية ، وقد تكون المعرفة مفيدة أيضًا عند استكشاف أخطاء تكوينات الأشخاص الآخرين وإصلاحها.
Webpack: الشروع في العمل
تختلف الطريقة التي نكتب بها JavaScript اليوم عن الكود الذي يمكن للمتصفح تنفيذه. نعتمد كثيرًا على أنواع أخرى من الموارد واللغات المترجمة والميزات التجريبية التي لم يتم دعمها بعد في المتصفحات الحديثة. Webpack عبارة عن مجمّع وحدات لجافا سكريبت يمكنه سد هذه الفجوة وإنتاج كود متوافق مع المتصفحات دون أي تكلفة عندما يتعلق الأمر بتجربة المطور.
قبل أن نبدأ ، يجب أن تضع في اعتبارك أن جميع التعليمات البرمجية المقدمة في هذا البرنامج التعليمي Webpack متاح أيضًا في شكل ملف تكوين Webpack / React مثال كامل على GitHub. لا تتردد في الرجوع إليه هناك والعودة إلى هذه المقالة إذا كان لديك أي أسئلة.
التكوين الأساسي
منذ Legato (الإصدار 4) ، لا يتطلب Webpack أي تكوين للتشغيل. سيؤدي اختيار وضع البناء إلى تطبيق مجموعة من الإعدادات الافتراضية أكثر ملاءمة للبيئة المستهدفة. وفقًا لروح هذه المقالة ، سنقوم بتنحية هذه الإعدادات الافتراضية جانبًا وتنفيذ تكوين معقول لكل بيئة هدف بأنفسنا.
أولاً ، نحتاج إلى تثبيت webpack
و webpack-cli
:
npm install -D webpack webpack-cli
ثم نحتاج إلى ملء webpack.config.js
بتكوين يضم الخيارات التالية:
-
devtool
: لتمكين إنشاء خريطة المصدر في وضع التطوير. -
entry
: الملف الرئيسي لتطبيق React الخاص بنا. -
output.path
: الدليل الجذر لتخزين ملفات الإخراج بتنسيق. -
output.filename
: نمط اسم الملف المراد استخدامه للملفات المُنشأة. -
output.publicPath
: المسار إلى الدليل الجذر حيث سيتم نشر الملفات على خادم الويب.
const path = require("path"); module.exports = function(_env, argv) { const isProduction = argv.mode === "production"; const isDevelopment = !isProduction; return { devtool: isDevelopment && "cheap-module-source-map", entry: "./src/index.js", output: { path: path.resolve(__dirname, "dist"), filename: "assets/js/[name].[contenthash:8].js", publicPath: "/" } }; };
التكوين أعلاه يعمل بشكل جيد لملفات JavaScript العادية. ولكن عند استخدام Webpack و React ، سنحتاج إلى إجراء تحويلات إضافية قبل شحن الكود إلى مستخدمينا. في القسم التالي ، سنستخدم Babel لتغيير طريقة تحميل Webpack لملفات JavaScript.
محمل JS
Babel هو مترجم JavaScript يحتوي على العديد من الإضافات لتحويل الكود. في هذا القسم ، سوف نقدمه كمحمل في تكوين Webpack الخاص بنا ونقوم بتكوينه لتحويل كود JavaScript الحديث إلى مثل هذا المفهوم من قبل المتصفحات الشائعة.
أولاً ، سنحتاج إلى تثبيت babel-loader
و @babel/core
:
npm install -D @babel/core babel-loader
ثم سنضيف قسم module
إلى تكوين Webpack الخاص بنا ، مما يجعل babel-loader
مسؤولاً عن تحميل ملفات JavaScript:
@@ -11,6 +11,25 @@ module.exports = function(_env, argv) { path: path.resolve(__dirname, "dist"), filename: "assets/js/[name].[contenthash:8].js", publicPath: "/" + }, + module: { + rules: [ + { + test: /\.jsx?$/, + exclude: /node_modules/, + use: { + loader: "babel-loader", + options: { + cacheDirectory: true, + cacheCompression: false, + envName: isProduction ? "production" : "development" + } + } + } + ] + }, + resolve: { + extensions: [".js", ".jsx"] } }; };
سنقوم بتهيئة Babel باستخدام ملف تكوين منفصل ، babel.config.js
. سيستخدم الميزات التالية:
-
@babel/preset-env
: يحول ميزات JavaScript الحديثة إلى كود متوافق مع الإصدارات السابقة. -
@babel/preset-react
reaction: يحول بناء جملة JSX إلى استدعاءات دالة JavaScript عادي الفانيليا. -
@babel/plugin-transform-runtime
: يقلل من تكرار الكود عن طريق استخراج مساعدي Babel إلى وحدات مشتركة. -
@babel/plugin-syntax-dynamic-import
: لتمكين بناء جملة الاستيراد الديناميكيimport()
في المتصفحات التي تفتقر إلى دعمPromise
الأصلي. -
@babel/plugin-proposal-class-properties
: لتمكين الدعم لاقتراح بناء جملة حقل المثيل العام ، لكتابة مكونات React القائمة على الفئة.
سنقوم أيضًا بتمكين بعض تحسينات الإنتاج الخاصة بـ React:
- تزيل
babel-plugin-transform-react-remove-prop-types
الدعامة غير الضرورية من كود الإنتاج. -
@babel/plugin-transform-react-inline-elements
element بتقييمReact.createElement
أثناء التجميع وتضمن النتيجة. - تستخلص
@babel/plugin-transform-react-constant-elements
response-الثابتة عناصر React الثابتة كثوابت.
سيقوم الأمر أدناه بتثبيت جميع التبعيات الضرورية:
npm install -D @babel/preset-env @babel/preset-react @babel/runtime @babel/plugin-transform-runtime @babel/plugin-syntax-dynamic-import @babel/plugin-proposal-class-properties babel-plugin-transform-react-remove-prop-types @babel/plugin-transform-react-inline-elements @babel/plugin-transform-react-constant-elements
ثم سنقوم بملء babel.config.js
بهذه الإعدادات:
module.exports = { presets: [ [ "@babel/preset-env", { modules: false } ], "@babel/preset-react" ], plugins: [ "@babel/plugin-transform-runtime", "@babel/plugin-syntax-dynamic-import", "@babel/plugin-proposal-class-properties" ], env: { production: { only: ["src"], plugins: [ [ "transform-react-remove-prop-types", { removeImport: true } ], "@babel/plugin-transform-react-inline-elements", "@babel/plugin-transform-react-constant-elements" ] } } };
يتيح لنا هذا التكوين كتابة JavaScript حديث بطريقة تتوافق مع جميع المتصفحات ذات الصلة. هناك أنواع أخرى من الموارد التي قد نحتاجها في تطبيق React ، والتي سنغطيها في الأقسام التالية.
محمل CSS
عندما يتعلق الأمر بتصميم تطبيقات React ، على أقل تقدير ، نحتاج إلى أن نكون قادرين على تضمين ملفات CSS عادية. سنقوم بذلك في Webpack باستخدام أدوات التحميل التالية:
-
css-loader
: يوزع ملفات CSS ، ويحل الموارد الخارجية ، مثل الصور والخطوط واستيراد الأنماط الإضافية. -
style-loader
: أثناء التطوير ، يضخ الأنماط المحملة في المستند في وقت التشغيل. -
mini-css-extract-plugin
: لاستخراج الأنماط المحملة في ملفات منفصلة لاستخدامها في الإنتاج للاستفادة من التخزين المؤقت للمتصفح.
لنقم بتثبيت برامج تحميل CSS أعلاه:
npm install -D css-loader style-loader mini-css-extract-plugin
ثم سنضيف قاعدة جديدة إلى قسم module.rules
في Webpack config:
@@ -1,4 +1,5 @@ const path = require("path"); +const MiniCssExtractPlugin = require("mini-css-extract-plugin"); module.exports = function(_env, argv) { const isProduction = argv.mode === "production"; @@ -25,6 +26,13 @@ module.exports = function(_env, argv) { envName: isProduction ? "production" : "development" } } + }, + { + test: /\.css$/, + use: [ + isProduction ? MiniCssExtractPlugin.loader : "style-loader", + "css-loader" + ] } ] },
سنضيف أيضًا MiniCssExtractPlugin
إلى قسم plugins
، والذي سنقوم بتمكينه فقط في وضع الإنتاج:
@@ -38,6 +38,13 @@ module.exports = function(_env, argv) { }, resolve: { extensions: [".js", ".jsx"] - } + }, + plugins: [ + isProduction && + new MiniCssExtractPlugin({ + filename: "assets/css/[name].[contenthash:8].css", + chunkFilename: "assets/css/[name].[contenthash:8].chunk.css" + }) + ].filter(Boolean) }; };
يعمل هذا التكوين مع ملفات CSS العادية ويمكن تمديده للعمل مع معالجات CSS المختلفة ، مثل Sass و PostCSS ، والتي سنناقشها في المقالة التالية.
محمل الصور
يمكن أيضًا استخدام Webpack لتحميل موارد ثابتة مثل الصور ومقاطع الفيديو والملفات الثنائية الأخرى. الطريقة الأكثر عمومية للتعامل مع مثل هذه الأنواع من الملفات هي استخدام أداة تحميل file-loader
أو أداة تحميل url-loader
، والتي ستوفر مرجعًا لعنوان URL للموارد المطلوبة للمستهلكين.
في هذا القسم ، سنضيف url-loader
للتعامل مع تنسيقات الصور الشائعة. ما يميز أداة url-loader
بصرف النظر عن أداة file-loader
هو أنه إذا كان حجم الملف الأصلي أصغر من حد معين ، فسيتم تضمين الملف بالكامل في عنوان URL كمحتويات مشفرة باستخدام base64 ، وبالتالي إزالة الحاجة إلى طلب إضافي.
أولا نقوم بتثبيت url-loader
:
npm install -D url-loader
ثم نضيف قاعدة جديدة إلى قسم module.rules
في Webpack config:
@@ -34,6 +34,16 @@ module.exports = function(_env, argv) { isProduction ? MiniCssExtractPlugin.loader : "style-loader", "css-loader" ] + }, + { + test: /\.(png|jpg|gif)$/i, + use: { + loader: "url-loader", + options: { + limit: 8192, + name: "static/media/[name].[hash:8].[ext]" + } + } } ] },
SVG
بالنسبة لصور SVG ، سنستخدم أداة @svgr/webpack
، والتي تحول الملفات المستوردة إلى مكونات React.
نقوم بتثبيت @svgr/webpack
:
npm install -D @svgr/webpack
ثم نضيف قاعدة جديدة إلى قسم module.rules
في Webpack config:
@@ -44,6 +44,10 @@ module.exports = function(_env, argv) { name: "static/media/[name].[hash:8].[ext]" } } + }, + { + test: /\.svg$/, + use: ["@svgr/webpack"] } ] },
يمكن أن تكون صور SVG كمكونات React ملائمة ، ويقوم @svgr/webpack
بإجراء التحسين باستخدام SVGO.

ملاحظة: بالنسبة لبعض الرسوم المتحركة أو حتى تأثيرات تمرير الماوس ، قد تحتاج إلى معالجة SVG باستخدام JavaScript. لحسن الحظ ، @svgr/webpack
بتضمين محتويات SVG في حزمة JavaScript افتراضيًا ، مما يسمح لك بتجاوز قيود الأمان اللازمة لذلك.
محمل الملفات
عندما نحتاج إلى الإشارة إلى أي أنواع أخرى من الملفات ، فإن أداة تحميل file-loader
العامة ستؤدي المهمة. إنه يعمل بشكل مشابه url-loader
، حيث يوفر عنوان URL للأصل للرمز الذي يتطلبه ، لكنه لا يحاول تحسينه.
كما هو الحال دائمًا ، نقوم أولاً بتثبيت الوحدة النمطية Node.js. في هذه الحالة ، file-loader
:
npm install -D file-loader
ثم نضيف قاعدة جديدة إلى قسم module.rules
في Webpack config. علي سبيل المثال:
@@ -48,6 +48,13 @@ module.exports = function(_env, argv) { { test: /\.svg$/, use: ["@svgr/webpack"] + }, + { + test: /\.(eot|otf|ttf|woff|woff2)$/, + loader: require.resolve("file-loader"), + options: { + name: "static/media/[name].[hash:8].[ext]" + } } ] },
أضفنا هنا أداة تحميل file-loader
لتحميل الخطوط ، والتي يمكنك الرجوع إليها من ملفات CSS الخاصة بك. يمكنك تمديد هذا المثال لتحميل أي أنواع أخرى من الملفات التي تحتاجها.
البرنامج المساعد للبيئة
يمكننا استخدام Webpack's DefinePlugin()
لفضح متغيرات البيئة من بيئة البناء إلى كود التطبيق الخاص بنا. علي سبيل المثال:
@@ -1,5 +1,6 @@ const path = require("path"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); +const webpack = require("webpack"); module.exports = function(_env, argv) { const isProduction = argv.mode === "production"; @@ -65,7 +66,12 @@ module.exports = function(_env, argv) { new MiniCssExtractPlugin({ filename: "assets/css/[name].[contenthash:8].css", chunkFilename: "assets/css/[name].[contenthash:8].chunk.css" - }) + }), + new webpack.DefinePlugin({ + "process.env.NODE_ENV": JSON.stringify( + isProduction ? "production" : "development" + ) + }) ].filter(Boolean) }; };
هنا process.env.NODE_ENV
بسلسلة تمثل وضع البناء: "development"
أو "production"
.
البرنامج المساعد HTML
في حالة عدم وجود ملف index.html
، تصبح حزمة JavaScript الخاصة بنا عديمة الفائدة ، حيث لا يمكن لأي شخص العثور عليها. في هذا القسم ، html-webpack-plugin
لإنشاء ملف HTML لنا.
نقوم بتثبيت html-webpack-plugin
:
npm install -D html-webpack-plugin
ثم نضيف html-webpack-plugin
إلى قسم plugins
في Webpack config:
@@ -1,6 +1,7 @@ const path = require("path"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const webpack = require("webpack"); +const HtmlWebpackPlugin = require("html-webpack-plugin"); module.exports = function(_env, argv) { const isProduction = argv.mode === "production"; @@ -71,6 +72,10 @@ module.exports = function(_env, argv) { "process.env.NODE_ENV": JSON.stringify( isProduction ? "production" : "development" ) + }), + new HtmlWebpackPlugin({ + template: path.resolve(__dirname, "public/index.html"), + inject: true }) ].filter(Boolean) };
ملف public/index.html
الذي تم إنشاؤه سوف يقوم بتحميل الحزمة الخاصة بنا وتشغيل تطبيقنا.
الاقوي
هناك العديد من تقنيات التحسين التي يمكننا استخدامها في عملية البناء الخاصة بنا. سنبدأ بتقليل الكود ، وهي عملية يمكننا من خلالها تقليل حجم الحزمة الخاصة بنا دون أي تكلفة من حيث الوظائف. سنستخدم مكونين إضافيين لتقليل الكود الخاص بنا: terser-webpack-plugin
لرمز JavaScript ، و optimize-css-assets-webpack-plugin
لـ CSS.
دعنا نثبتها:
npm install -D terser-webpack-plugin optimize-css-assets-webpack-plugin
ثم سنضيف قسم optimization
إلى التكوين الخاص بنا:
@@ -2,6 +2,8 @@ const path = require("path"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const webpack = require("webpack"); const HtmlWebpackPlugin = require("html-webpack-plugin"); +const TerserWebpackPlugin = require("terser-webpack-plugin"); +const OptimizeCssAssetsPlugin = require("optimize-css-assets-webpack-plugin"); module.exports = function(_env, argv) { const isProduction = argv.mode === "production"; @@ -75,6 +77,27 @@ module.exports = function(_env, argv) { isProduction ? "production" : "development" ) }) - ].filter(Boolean) + ].filter(Boolean), + optimization: { + minimize: isProduction, + minimizer: [ + new TerserWebpackPlugin({ + terserOptions: { + compress: { + comparisons: false + }, + mangle: { + safari10: true + }, + output: { + comments: false, + ascii_only: true + }, + warnings: false + } + }), + new OptimizeCssAssetsPlugin() + ] + } }; };
ستضمن الإعدادات أعلاه توافق التعليمات البرمجية مع جميع المتصفحات الحديثة.
تقسيم الكود
تقسيم الكود هو أسلوب آخر يمكننا استخدامه لتحسين أداء تطبيقنا. يمكن أن يشير تقسيم الكود إلى طريقتين مختلفتين:
- باستخدام بيان
import()
، يمكننا استخراج أجزاء من التطبيق تشكل جزءًا كبيرًا من حجم الحزمة لدينا ، وتحميلها عند الطلب. - يمكننا استخراج التعليمات البرمجية التي تتغير بشكل أقل تكرارًا ، من أجل الاستفادة من التخزين المؤقت للمتصفح وتحسين الأداء للزائرين المتكررين.
سنقوم بتعبئة قسم optimization.splitChunks
من تكوين Webpack الخاص بنا بإعدادات لاستخراج تبعيات الطرف الثالث والأجزاء الشائعة في ملفات منفصلة:
@@ -99,7 +99,29 @@ module.exports = function(_env, argv) { sourceMap: true }), new OptimizeCssAssetsPlugin() - ] + ], + splitChunks: { + chunks: "all", + minSize: 0, + maxInitialRequests: 20, + maxAsyncRequests: 20, + cacheGroups: { + vendors: { + test: /[\\/]node_modules[\\/]/, + name(module, chunks, cacheGroupKey) { + const packageName = module.context.match( + /[\\/]node_modules[\\/](.*?)([\\/]|$)/ + )[1]; + return `${cacheGroupKey}.${packageName.replace("@", "")}`; + } + }, + common: { + minChunks: 2, + priority: -10 + } + } + }, + runtimeChunk: "single" } }; };
دعنا نلقي نظرة أعمق على الخيارات التي استخدمناها هنا:
-
chunks: "all"
: بشكل افتراضي ، يؤثر استخراج المقاطع الشائع فقط على الوحدات المحملةimport()
. يتيح هذا الإعداد تحسين التحميل عند نقطة الدخول أيضًا. -
minSize: 0
: بشكل افتراضي ، تصبح الأجزاء التي تزيد عن حد حجم معين مؤهلة للاستخراج. يتيح هذا الإعداد التحسين لجميع التعليمات البرمجية الشائعة بغض النظر عن حجمها. -
maxInitialRequests: 20
andmaxAsyncChunks: 20
: تزيد هذه الإعدادات الحد الأقصى لعدد ملفات المصدر التي يمكن تحميلها بالتوازي لاستيراد نقطة الإدخال وواردات نقطة الانقسام ، على التوالي.
بالإضافة إلى ذلك ، نحدد تكوين cacheGroups
التالي:
-
vendors
: تكوين الاستخراج للوحدات النمطية للجهات الخارجية.-
test: /[\\/]node_modules[\\/]/
: نمط اسم الملف لمطابقة تبعيات الطرف الثالث. -
name(module, chunks, cacheGroupKey)
: تقوم المجموعات بفصل الأجزاء من نفس الوحدة معًا عن طريق منحهم اسمًا شائعًا.
-
-
common
: تكوين استخلاص الأجزاء الشائعة من كود التطبيق.-
minChunks: 2
: تعتبر القطعة شائعة إذا تمت الإشارة إليها من وحدتين على الأقل. -
priority: -10
: يعين أولوية سلبية لمجموعة ذاكرة التخزين المؤقتcommon
بحيث يتم النظر في الأجزاء لمجموعة ذاكرة التخزين المؤقتvendors
أولاً.
-
نقوم أيضًا باستخراج رمز وقت تشغيل Webpack في جزء واحد يمكن مشاركته بين نقاط دخول متعددة ، من خلال تحديد runtimeChunk: "single"
.
خادم ديف
حتى الآن ، ركزنا على إنشاء وتحسين إنتاج تطبيقنا ، ولكن Webpack لديه أيضًا خادم الويب الخاص به مع إعادة التحميل المباشر والإبلاغ عن الأخطاء ، مما سيساعدنا في عملية التطوير. إنه يسمى webpack-dev-server
، ونحتاج إلى تثبيته بشكل منفصل:
npm install -D webpack-dev-server
في هذا المقتطف ، نقدم قسم devServer
في تهيئة Webpack الخاصة بنا:
@@ -120,6 +120,12 @@ module.exports = function(_env, argv) { } }, runtimeChunk: "single" + }, + devServer: { + compress: true, + historyApiFallback: true, + open: true, + overlay: true } }; };
استخدمنا هنا الخيارات التالية:
-
compress: true
: لتمكين ضغط الأصول لعمليات إعادة التحميل بشكل أسرع. -
historyApiFallback: true
: تمكين الرجوع إلىindex.html
للتوجيه المستند إلى المحفوظات. -
open: true
: يفتح المتصفح بعد تشغيل خادم dev. -
overlay: true
: يعرض أخطاء Webpack في نافذة المتصفح.
قد تحتاج أيضًا إلى تكوين إعدادات الوكيل لإعادة توجيه طلبات واجهة برمجة التطبيقات إلى خادم الواجهة الخلفية.
Webpack و React: مُحسَّن للأداء وجاهز!
لقد تعلمنا للتو كيفية تحميل أنواع الموارد المختلفة باستخدام Webpack ، وكيفية استخدام Webpack في بيئة التطوير ، والعديد من التقنيات لتحسين بنية الإنتاج. إذا كنت بحاجة إلى ذلك ، يمكنك دائمًا مراجعة ملف التكوين الكامل للحصول على مصدر إلهام لإعداد React / Webpack الخاص بك. شحذ هذه المهارات هو السعر القياسي لأي شخص يقدم خدمات تطوير React.
في الجزء التالي من هذه السلسلة ، سنتوسع في هذا التكوين بإرشادات لحالات استخدام أكثر تحديدًا ، بما في ذلك استخدام TypeScript ومعالجات CSS الأولية وتقنيات التحسين المتقدمة التي تتضمن التقديم من جانب الخادم و ServiceWorkers. ابق على اتصال لمعرفة كل ما تحتاج لمعرفته حول Webpack لنقل تطبيق React إلى مرحلة الإنتاج.