من 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')
    }
  }
}
·تعديل هذه الصفحة
السابق ›
من v3 إلى v4

1 مساهم

RlxChap2