من v1 إلى v2 أو v3
تشرح الأقسام التالية أهم التغييرات عند الانتقال من webpack 1 إلى 2.
resolve.root وresolve.fallback وresolve.modulesDirectories
تم استبدال هذه الخيارات بخيار واحد هو resolve.modules. راجع resolving لمزيد من الاستخدامات.
resolve: {
- root: path.join(__dirname, "src")
+ modules: [
+ path.join(__dirname, "src"),
+ "node_modules"
+ ]
}resolve.extensions
لم يعد هذا الخيار يحتاج إلى تمرير نص فارغ. تم نقل هذا السلوك إلى resolve.enforceExtension. راجع resolving لمزيد من الاستخدامات.
resolve.*
تغيرت عدة APIs هنا. لم تُذكر بالتفصيل لأنها ليست شائعة الاستخدام. راجع resolving للتفاصيل.
module.loaders أصبحت module.rules
استُبدلت إعدادات loaders القديمة بنظام rules أقوى، يسمح بإعداد loaders وأشياء أخرى.
ولأسباب توافقية، ما زالت صيغة module.loaders القديمة صالحة، وما زالت الأسماء القديمة تُقرأ.
لكن اصطلاحات التسمية الجديدة أوضح وأسهل للفهم، وهذا سبب جيد لتحديث الإعدادات إلى module.rules.
module: {
- loaders: [
+ rules: [
{
test: /\.css$/,
- loaders: [
- "style-loader",
- "css-loader?modules=true"
+ use: [
+ {
+ loader: "style-loader"
+ },
+ {
+ loader: "css-loader",
+ options: {
+ modules: true
+ }
+ }
]
},
{
test: /\.jsx$/,
loader: "babel-loader", // Do not use "use" here
options: {
// ...
}
}
]
}ربط loaders كسلسلة
كما في webpack 1، يمكن ربط loaders كسلسلة لتمرير الناتج من loader إلى آخر. باستخدام خيار الإعداد rule.use، يمكن ضبط use كـ array من loaders.
في webpack 1، كان من الشائع ربط loaders باستخدام !. هذا الأسلوب لا يزال مدعومًا فقط عبر الخيار القديم module.loaders.
module: {
- loaders: [{
+ rules: [{
test: /\.less$/,
- loader: "style-loader!css-loader!less-loader"
+ use: [
+ "style-loader",
+ "css-loader",
+ "less-loader"
+ ]
}]
}إزالة الإضافة التلقائية -loader من أسماء modules
لم يعد ممكنًا حذف اللاحقة -loader عند الإشارة إلى loaders:
module: {
rules: [
{
use: [
- "style",
+ "style-loader",
- "css",
+ "css-loader",
- "less",
+ "less-loader",
]
}
]
}ما زال بإمكانك تفعيل السلوك القديم باستخدام خيار الإعداد resolveLoader.moduleExtensions، لكن هذا غير موصى به.
+ resolveLoader: {
+ moduleExtensions: ["-loader"]
+ }راجع #2986 لمعرفة سبب هذا التغيير.
لم يعد json-loader مطلوبًا
عندما لا يكون هناك loader معدّ لملف JSON، سيحاول webpack تلقائيًا تحميل ملف JSON باستخدام json-loader.
module: {
rules: [
- {
- test: /\.json/,
- loader: "json-loader"
- }
]
}قررنا فعل ذلك لتقليل الفروقات بين بيئات webpack وnode.js وbrowserify.
حل loaders في الإعدادات يتم نسبة إلى context
في webpack 1، كانت loaders المعرّفة في الإعدادات تُحل نسبة إلى الملف المطابق. أما في webpack 2، فتُحل loaders المعرّفة نسبة إلى خيار context.
هذا يحل بعض مشاكل modules المكررة التي تسببها loaders عند استخدام npm link أو عند الإشارة إلى modules خارج context.
يمكنك إزالة بعض الحلول الالتفافية القديمة:
module: {
rules: [
{
// ...
- loader: require.resolve("my-loader")
+ loader: "my-loader"
}
]
},
resolveLoader: {
- root: path.resolve(__dirname, "node_modules")
}إزالة module.preLoaders وmodule.postLoaders
module: {
- preLoaders: [
+ rules: [
{
test: /\.js$/,
+ enforce: "pre",
loader: "eslint-loader"
}
]
}sourceMap في UglifyJsPlugin
أصبحت القيمة الافتراضية لخيار sourceMap في UglifyJsPlugin هي false بدل true. هذا يعني أنك إذا كنت تستخدم source maps للكود المصغّر، أو تريد أرقام أسطر صحيحة لتحذيرات uglifyjs، فيجب ضبط sourceMap: true داخل UglifyJsPlugin.
devtool: "source-map",
plugins: [
new UglifyJsPlugin({
+ sourceMap: true
})
]warnings في UglifyJsPlugin
أصبحت القيمة الافتراضية لخيار compress.warnings في UglifyJsPlugin هي false بدل true.
هذا يعني أنك إذا أردت رؤية تحذيرات uglifyjs، فيجب ضبط compress.warnings على true.
devtool: "source-map",
plugins: [
new UglifyJsPlugin({
+ compress: {
+ warnings: true
+ }
})
]minimize loaders في UglifyJsPlugin
لم يعد UglifyJsPlugin يحوّل loaders إلى minimize mode. على المدى الطويل، يجب تمرير إعداد minimize: true عبر خيارات loader. راجع توثيق loader لمعرفة الخيارات المناسبة.
ستتم إزالة minimize mode الخاصة بـ loaders في webpack 3 أو إصدار لاحق.
للحفاظ على التوافق مع loaders القديمة، يمكن تحويل loaders إلى minimize mode عبر plugin:
plugins: [
+ new webpack.LoaderOptionsPlugin({
+ minimize: true
+ })
]تمت إزالة DedupePlugin
لم يعد webpack.optimize.DedupePlugin مطلوبًا. أزله من إعداداتك.
BannerPlugin - تغيير كاسر
لم يعد BannerPlugin يقبل parameterين، بل يقبل كائن options واحدًا.
plugins: [
- new webpack.BannerPlugin('Banner', {raw: true, entryOnly: true});
+ new webpack.BannerPlugin({banner: 'Banner', raw: true, entryOnly: true});
]OccurrenceOrderPlugin مفعّل افتراضيًا
أصبح OccurrenceOrderPlugin مفعّلًا افتراضيًا، وتمت إعادة تسميته من OccurenceOrderPlugin في webpack 1.
لذلك تأكد من إزالة plugin من إعداداتك:
plugins: [
// webpack 1
- new webpack.optimize.OccurenceOrderPlugin()
// webpack 2
- new webpack.optimize.OccurrenceOrderPlugin()
]ExtractTextWebpackPlugin - تغيير كاسر
يتطلب ExtractTextPlugin الإصدار 2 حتى يعمل مع webpack 2.
npm install --save-dev extract-text-webpack-plugin
تغييرات الإعدادات لهذا plugin في الغالب تغييرات في الصيغة.
ExtractTextPlugin.extract
module: {
rules: [
{
test: /.css$/,
- loader: ExtractTextPlugin.extract("style-loader", "css-loader", { publicPath: "/dist" })
+ use: ExtractTextPlugin.extract({
+ fallback: "style-loader",
+ use: "css-loader",
+ publicPath: "/dist"
+ })
}
]
}new ExtractTextPlugin({options})
plugins: [
- new ExtractTextPlugin("bundle.css", { allChunks: true, disable: false })
+ new ExtractTextPlugin({
+ filename: "bundle.css",
+ disable: false,
+ allChunks: true
+ })
]full dynamic requires تفشل افتراضيًا الآن
الاعتماد الذي يحتوي على expression فقط، مثل require(expr)، سينشئ الآن context فارغًا بدل context للمجلد كاملًا.
يجب إعادة كتابة كود بهذا الشكل لأنه لن يعمل مع ES2015 modules. إذا لم يكن ذلك ممكنًا، يمكنك استخدام ContextReplacementPlugin لإعطاء compiler تلميحًا عن resolving الصحيح.
استخدام arguments مخصصة في CLI والإعدادات
إذا كنت تستخدم CLI لتمرير arguments مخصصة إلى الإعدادات بهذا الشكل:
webpack --custom-stuff
// webpack.config.js
const customStuff = process.argv.includes("--custom-stuff");
/* ... */
module.exports = config;فقد تلاحظ أن هذا لم يعد مسموحًا. أصبحت CLI أكثر صرامة الآن.
بدلًا من ذلك، توجد واجهة مخصصة لتمرير arguments إلى الإعدادات. يجب استخدامها، وقد تعتمد عليها أدوات مستقبلية.
webpack --env.customStuff
module.exports = function (env) {
const { customStuff } = env;
/* ... */
return config;
};راجع CLI.
require.ensure وAMD require صارت غير متزامنة
أصبحت هذه الدوال دائمًا غير متزامنة، بدل استدعاء callback بشكل متزامن إذا كان chunk محملًا بالفعل.
تعتمد require.ensure الآن على Promise الأصلية. إذا كنت تستخدم require.ensure في بيئة لا توفرها، فستحتاج إلى polyfill.
إعداد Loader يتم عبر options
لم يعد بإمكانك إعداد loader بخاصية مخصصة داخل webpack.config.js. يجب فعل ذلك عبر options. الإعداد التالي الذي يستخدم الخاصية ts لم يعد صالحًا مع webpack 2:
module.exports = {
// ...
module: {
rules: [
{
test: /\.tsx?$/,
loader: "ts-loader",
},
],
},
// لا يعمل مع webpack 2
ts: { transpileOnly: false },
};ما هي options؟
سؤال جيد. بدقة، هي واحدة من طريقتين لإعداد webpack loader. في السابق كانت options تُسمى query، وكانت نصًا يمكن إضافته إلى اسم loader. تشبه query string، لكنها تملك قدرات أكبر:
module.exports = {
// ...
module: {
rules: [
{
test: /\.tsx?$/,
loader: `ts-loader?${JSON.stringify({ transpileOnly: false })}`,
},
],
},
};ويمكن أيضًا أن تكون object مستقلًا يُمرر بجانب loader:
module.exports = {
// ...
module: {
rules: [
{
test: /\.tsx?$/,
loader: "ts-loader",
options: { transpileOnly: false },
},
],
},
};context في LoaderOptionsPlugin
بعض loaders تحتاج إلى معلومات context وتقرأها من الإعدادات. على المدى الطويل، يجب تمرير هذه المعلومات عبر خيارات loader. راجع توثيق loader لمعرفة الخيارات المناسبة.
للحفاظ على التوافق مع loaders القديمة، يمكن تمرير هذه المعلومات عبر plugin:
plugins: [
+ new webpack.LoaderOptionsPlugin({
+ options: {
+ context: __dirname
+ }
+ })
]debug
كان خيار debug يحوّل loaders إلى debug mode في webpack 1. على المدى الطويل، يجب تمريره عبر خيارات loader. راجع توثيق loader لمعرفة الخيارات المناسبة.
ستتم إزالة debug mode الخاصة بـ loaders في webpack 3 أو إصدار لاحق.
للحفاظ على التوافق مع loaders القديمة، يمكن تحويل loaders إلى debug mode عبر plugin:
- debug: true,
plugins: [
+ new webpack.LoaderOptionsPlugin({
+ debug: true
+ })
]Code Splitting مع ES2015
في webpack 1، كان بإمكانك استخدام require.ensure() كطريقة لتحميل chunks بشكل كسول داخل تطبيقك:
require.ensure([], (require) => {
const foo = require("./module");
});تعرّف مواصفة ES2015 Loader الدالة import() كطريقة لتحميل ES2015 Modules ديناميكيًا وقت التشغيل. يتعامل webpack مع import() كنقطة تقسيم، ويضع module المطلوب في chunk منفصل. تأخذ import() اسم module كـ argument وترجع Promise.
function onClick() {
import("./module")
.then((module) => module.default)
.catch((err) => {
console.log("Chunk loading failed");
});
}الخبر الجيد: يمكن الآن التعامل مع فشل تحميل chunk لأنها مبنية على Promise.
Dynamic expressions
يمكن تمرير expression جزئي إلى import(). يتم التعامل مع هذا بطريقة مشابهة للتعابير في CommonJS، حيث ينشئ webpack context يحتوي على كل الملفات الممكنة.
تنشئ import() chunk منفصلًا لكل module محتمل.
function route(path, query) {
return import(`./routes/${path}/route`).then(
(route) => new route.Route(query),
);
}
// ينشئ هذا chunk منفصلًا لكل route محتملخلط ES2015 مع AMD وCommonJS
كما هو الحال مع AMD وCommonJS، يمكنك خلط أنواع modules الثلاثة بحرية، حتى داخل الملف نفسه. يتصرف webpack بطريقة مشابهة لـ babel وnode-eps في هذه الحالة:
// CommonJS يستهلك ES2015 Module
const book = require("./book");
book.currentPage;
book.readPage();
book.default === "This is a book";// ES2015 Module يستهلك CommonJS
import fs from "node:fs"; // module.exports map to default
typeof fs.readFileSync === "function";// ES2015 Module يستهلك CommonJS
import { readFileSync } from "node:fs"; // named exports are read from returned object+
typeof readFileSync === "function";من المهم أن تطلب من Babel ألا يقرأ رموز modules هذه حتى يتمكن webpack من استخدامها. يمكنك فعل ذلك عبر الإعداد التالي في .babelrc أو خيارات babel-loader.
.babelrc
{
"presets": [["es2015", { "modules": false }]]
}تلميحات
ليست تغييرات واجبة، لكنها فرص للتحسين.
Template strings
يدعم webpack الآن template strings داخل expressions. هذا يعني أنه يمكنك استخدامها في constructs الخاصة بـ webpack:
- require("./templates/" + name);
+ require(`./templates/${name}`);Configuration Promise
يدعم webpack الآن إرجاع Promise من ملف الإعدادات. هذا يسمح بتنفيذ معالجة غير متزامنة داخل ملف الإعدادات.
webpack.config.js
module.exports = function () {
return fetchLangs().then((lang) => ({
entry: "...",
// ...
plugins: [new DefinePlugin({ LANGUAGE: lang })],
}));
};مطابقة loaders بشكل متقدم
يدعم webpack الآن أشياء أكثر للمطابقة عند تطبيق loaders.
module.exports = {
// ...
module: {
rules: [
{
resource: /filename/, // يطابق "/path/filename.js"
resourceQuery: /^\?querystring$/, // يطابق "?querystring"
issuer: /filename/, // يطابق "/path/something.js" إذا طُلب من "/path/filename.js"
},
],
},
};خيارات CLI إضافية
توجد بعض خيارات CLI الجديدة التي يمكنك استخدامها:
--define process.env.NODE_ENV="production" راجع DefinePlugin.
--display-depth يعرض المسافة إلى entry point لكل module.
--display-used-exports يعرض معلومات عن exports المستخدمة داخل module.
--display-max-modules يحدد عدد modules المعروضة في output، والقيمة الافتراضية 15.
-p يعرّف الآن process.env.NODE_ENV بالقيمة "production" أيضًا.
تغييرات Loader
تغييرات تهم مؤلفي loaders فقط.
Cacheable
أصبحت loaders قابلة للتخزين المؤقت افتراضيًا. يجب على loader تعطيل ذلك بنفسه إذا لم يكن قابلًا للتخزين المؤقت.
// Cacheable loader
module.exports = function(source) {
- this.cacheable();
return source;
} // Not cacheable loader
module.exports = function(source) {
+ this.cacheable(false);
return source;
}خيارات معقدة
كان webpack 1 يدعم فقط خيارات loaders القابلة للتحويل عبر JSON.stringify.
أما webpack 2 فيدعم الآن أي كائن JS كخيارات loader.
قبل webpack 2.2.1، أي من 2.0.0 إلى 2.2.0، كان استخدام الخيارات المعقدة يتطلب استخدام ident لكائن options حتى يمكن الإشارة إليه من loaders أخرى. تمت إزالة هذا في 2.2.1، لذلك لا تحتاج عمليات الترقية الحالية إلى استخدام مفتاح ident.
{
test: /\.ext/
use: {
loader: '...',
options: {
- ident: 'id',
fn: () => require('./foo.js')
}
}
}


