دوال Module

يغطي هذا القسم كل الدوال المتاحة داخل الكود الذي تم تجميعه بواسطة webpack. عند استخدام webpack لتحزيم تطبيقك، يمكنك الاختيار بين عدة صيغ للـ modules، مثل ES6 وCommonJS وAMD.

رغم أن webpack يدعم عدة صيغ للـ modules، فإننا نوصي باتباع صيغة واحدة للمحافظة على الاتساق وتجنب السلوكيات الغريبة أو الأخطاء. في الواقع، يفرض webpack هذه التوصية على ملفات .mjs وملفات .cjs وملفات .js عندما يحتوي أقرب ملف package.json أب على حقل "type" بقيمة "module" أو "commonjs". انتبه لهذه القيود قبل المتابعة:

  • ملفات .mjs أو .js مع "type": "module" في package.json
    • لا يُسمح بـ CommonJS، مثل require أو module.exports أو exports.
    • يجب كتابة امتدادات الملفات عند الاستيراد، مثل استخدام import './src/App.mjs' بدل import './src/App'. يمكنك تعطيل هذا القيد عبر Rule.resolve.fullySpecified.
  • ملفات .cjs أو .js مع "type": "commonjs" في package.json
    • لا تتوفر import ولا export.
  • ملفات .wasm مع "type": "module" في package.json
    • يجب كتابة امتداد الملف عند استيراد ملف wasm.

ES6 (موصى به)

يدعم webpack 2 صيغة ES6 modules بشكل أصلي، وهذا يعني أنك تستطيع استخدام import وexport بدون أداة مثل babel للتعامل مع ذلك. مع ذلك، قد تحتاج إلى babel لميزات ES6+ الأخرى. يدعم webpack الدوال التالية:

import

يستورد exports من module آخر بشكل ثابت.

import MyModule from "./my-module.js";
import { NamedExport } from "./other-module.js";

يمكنك أيضًا استيراد Data URI:

import "data:text/javascript;charset=utf-8;base64,Y29uc29sZS5sb2coJ2lubGluZSAxJyk7";
import {
  fn,
  number,
} from "data:text/javascript;charset=utf-8;base64,ZXhwb3J0IGNvbnN0IG51bWJlciA9IDQyOwpleHBvcnQgY29uc3QgZm4gPSAoKSA9PiAiSGVsbG8gd29ybGQiOw==";

export

صدّر أي شيء كـ default أو كتصدير مسمى.

// Named exports
export const Count = 5;
export function Multiply(a, b) {
  return a * b;
}

// Default export
export default {
  // Some data...
};

import()

function(string path):Promise

يحمّل modules بشكل ديناميكي. يتعامل webpack مع استدعاءات import() كنقاط تقسيم، أي أن module المطلوب وأبناءه يُفصلون داخل chunk مستقل.

if (module.hot) {
  import("lodash").then((_) => {
    // افعل شيئًا باستخدام lodash، المعروف أيضًا باسم '_'
  });
}

Dynamic expressions in import()

لا يمكن استخدام عبارة import ديناميكية بالكامل مثل import(foo). السبب أن foo قد يكون أي مسار لأي ملف في نظامك أو مشروعك.

يجب أن تحتوي import() على قدر من المعلومات عن مكان module. يمكن حصر bundling في مجلد محدد أو مجموعة ملفات محددة، بحيث عند استخدام expression ديناميكي يتم تضمين كل module يمكن طلبه عبر import(). مثلًا، import(`./locale/${language}.json`) سيحزم فقط كل ملفات .json داخل مجلد ./locale ومجلداته الفرعية داخل chunk الجديد، وسيستبعد الملفات ذات الامتدادات الأخرى. وقت التشغيل، بعد حساب المتغير language، سيكون أي ملف مثل english.json أو german.json متاحًا للاستخدام.

// تخيل أن لدينا دالة لمعرفة اللغة من cookies أو تخزين آخر
const language = detectVisitorLanguage();
import(`./locale/${language}.json`).then((module) => {
  // افعل شيئًا بالترجمات
});

Magic Comments

عند إضافة comments إلى import، يمكننا فعل أشياء مثل تسمية chunk أو اختيار modes مختلفة. للاطلاع على القائمة الكاملة لهذه magic comments، راجع الكود أدناه ثم شرح وظيفة كل تعليق.

// هدف واحد
import(
  /* webpackChunkName: "my-chunk-name" */
  /* webpackMode: "lazy" */
  /* webpackExports: ["default", "named"] */
  /* webpackFetchPriority: "high" */
  "node:module"
);

// عدة أهداف محتملة
import(
  /* webpackInclude: /\.json$/ */
  /* webpackExclude: /\.noimport\.json$/ */
  /* webpackChunkName: "my-chunk-name" */
  /* webpackMode: "lazy" */
  /* webpackPrefetch: true */
  /* webpackPreload: true */
  `./locale/${language}`
);

import(/* webpackIgnore: true */ "ignored-module.js");
webpackIgnore

استخدام JavaScript

يعطل تحليل dynamic import عند ضبطه على true.

عند استخدام import.meta.url، لا يبقى كما هو؛ بل يُستبدل بناءً على baseURI. في modules، يُستبدل بـ new URL("./", import.meta.url)، وفي الحالات الأخرى تكون القيمة الافتراضية document.baseURI. هذا يضمن عمل relative URLs بشكل صحيح، بما يتوافق مع سياق base URL.

import(/* webpackIgnore: true */ "ignored-module.js");

new URL(/* webpackIgnore: true */ "./file1.css", import.meta.url);

استخدام CSS

يمكن لتعليق webpackIgnore التحكم فيما إذا كان webpack سيعالج import أو مرجع URL محددًا. يعمل في حالات معينة مباشرة، لكنه لا يدعم كل الحالات افتراضيًا لأسباب تتعلق بالأداء.

ندعم webpackIgnore في الحالات التالية:

@import /* webpackIgnore: false */ url(./basic.css);

.class {
  color: red;
  background: /* webpackIgnore: true */ url("./url/img.png");
}

.class {
  background-image: image-set(
    /*webpackIgnore:  true*/ url(./url/img1x.png) 1x,
    url(./url/img2x.png) 2x,
    url(./url/img3x.png) 3x
  );
}

استخدام HTML

5.107.0+

عند تفعيل experiments.html، يمكن وضع webpackIgnore كتعليق HTML مباشرة قبل الوسم لتجاوز حل URL لخصائص مثل src وhref وsrcset وما شابه. يُترك الوسم كما هو في HTML الصادر. هذا يعكس السلوك الذي يوفره html-loader.

<!-- webpackIgnore: true -->
<img src="https://cdn.example.com/logo.png" />

<!-- webpackIgnore: true -->
<script src="/legacy/external.js"></script>

تُقرأ قيمة magic-comment بالسياق نفسه المستخدم في parsers الخاصة بـ JS وCSS؛ القيم غير boolean تصدر UnsupportedFeatureWarning.

webpackChunkName

اسم chunk الجديد. منذ webpack 2.6.0، يتم دعم placeholders [index] و[request] داخل النص المعطى، حيث تُستبدل برقم متزايد أو باسم الملف المحلول فعليًا على الترتيب. إضافة هذا التعليق تجعل chunk المنفصل يسمى [my-chunk-name].js بدل [id].js.

webpackFetchPriority
5.87.0+

يضبط fetchPriority لاستيرادات ديناميكية محددة. ويمكن أيضًا ضبط قيمة افتراضية عامة لكل dynamic imports باستخدام خيار module.parser.javascript.dynamicImportFetchPriority.

import(
  /* webpackFetchPriority: "high" */
  "path/to/module"
);
webpackMode

منذ webpack 2.6.0، يمكن تحديد modes مختلفة لحل dynamic imports. الخيارات التالية مدعومة:

  • 'lazy' (الافتراضي): يولد chunk قابلًا للتحميل الكسول لكل module يتم استيراده عبر import().
  • 'lazy-once': يولد chunk واحدًا قابلًا للتحميل الكسول يكفي كل استدعاءات import(). يتم جلب chunk عند أول استدعاء لـ import()، وتستخدم الاستدعاءات اللاحقة الاستجابة الشبكية نفسها. هذا لا يكون منطقيًا إلا في حالة عبارة ديناميكية جزئية، مثل import(`./locales/${language}.json`)، حيث توجد عدة مسارات modules يمكن طلبها.
  • 'eager': لا يولد chunk إضافيًا. تُضمّن كل modules داخل chunk الحالي ولا تُرسل طلبات شبكة إضافية. ما زالت الدالة ترجع Promise لكنها تكون محلولة مسبقًا. بخلاف static import، لا يُنفّذ module إلا عند استدعاء import().
  • 'weak': يحاول تحميل module إذا كانت دالة module قد حُمّلت بطريقة أخرى، مثل أن chunk آخر استوردها أو script يحتوي على module تم تحميله. ما زالت الدالة ترجع Promise، لكنها لا تنجح إلا إذا كانت chunks موجودة بالفعل على client. إذا لم يكن module متاحًا، تُرفض Promise. لن يتم تنفيذ طلب شبكة أبدًا. هذا مفيد لـ universal rendering عندما تكون chunks المطلوبة مقدمة يدويًا دائمًا في الطلبات الأولية، مثل تضمينها داخل الصفحة، لكنه ليس مناسبًا عندما يؤدي تنقل المستخدم داخل التطبيق إلى import لم يكن مقدمًا أوليًا.
webpackPrefetch

يخبر المتصفح أن resource قد تكون مطلوبة غالبًا في تنقل مستقبلي. راجع الدليل لمزيد من المعلومات عن طريقة عمل webpackPrefetch.

webpackPreload

يخبر المتصفح أن resource قد تكون مطلوبة أثناء التنقل الحالي. راجع الدليل لمزيد من المعلومات عن طريقة عمل webpackPreload.

webpackInclude

تعبير regular expression تتم مطابقته أثناء حل import. فقط modules المطابقة سيتم حزمها.

webpackExclude

تعبير regular expression تتم مطابقته أثناء حل import. أي module يطابقه لن يتم حزمها.

webpackExports

يخبر webpack بأن يحزم فقط exports المحددة من module المستورد ديناميكيًا عبر import(). يمكن أن يقلل حجم output الخاص بـ chunk. متاح منذ webpack 5.0.0-beta.18.

CommonJS

هدف CommonJS هو تحديد منظومة JavaScript خارج المتصفح. يدعم webpack دوال CommonJS التالية:

require

require(dependency: String);

يجلب exports من module آخر بشكل متزامن. سيضمن compiler توفر dependency داخل output bundle.

import $ from "jquery";
import myModule from "my-module";

يمكن أيضًا تفعيل magic comments لـ require. راجع module.parser.javascript.commonjsMagicComments للمزيد.

require.resolve

require.resolve(dependency: String);

يجلب ID الخاص بـ module بشكل متزامن. سيضمن compiler توفر dependency داخل output bundle. يُنصح بالتعامل معه كقيمة opaque لا تُستخدم إلا مع require.cache[id] أو __webpack_require__(id)، والأفضل تجنب هذا الاستخدام.

راجع module.id لمزيد من المعلومات.

require.cache

عدة استدعاءات require للـ module نفسه تؤدي إلى تنفيذ module مرة واحدة فقط وتصدير واحد فقط. لذلك يوجد cache في runtime. إزالة قيم من هذا cache تؤدي إلى تنفيذ جديد للـ module وتصدير جديد.

import d1 from "dependency";

// في ESM، تتم إدارة module caching تلقائيًا.
// حذف cache يدويًا كما في CommonJS غير مدعوم.
if (import.meta.webpackHot) {
  import.meta.webpackHot.accept("dependency", (newModule) => {
    // تعامل مع تحديث module هنا
  });
}
// in file.js
// في ESM، التلاعب اليدوي بالـ cache غير مدعوم.
// يتعامل webpack مع module caching داخليًا.

require.ensure

require.ensure(
  dependencies: String[],
  callback: function(require),
  errorCallback: function(error),
  chunkName: String
)

يفصل dependencies المحددة إلى bundle مستقل يتم تحميله بشكل غير متزامن. عند استخدام صيغة CommonJS modules، هذه هي الطريقة الوحيدة لتحميل dependencies ديناميكيًا. أي يمكن تشغيل هذا الكود أثناء التنفيذ، ولا تُحمّل dependencies إلا إذا تحققت شروط معينة.

const a = require("normal-dep");

if (module.hot) {
  import("b").then(() => {
    import("c").then((c) => {
      // افعل شيئًا خاصًا...
    });
  });
}

parameters التالية مدعومة بالترتيب الموضح أعلاه:

  • dependencies: array من strings تعلن كل modules المطلوبة لتنفيذ الكود داخل callback.
  • callback: دالة ينفذها webpack بعد تحميل dependencies. يتم إرسال implementation لدالة require كـ parameter إلى هذه الدالة. يمكن لجسم الدالة استخدامه لاستدعاء require() لأي modules إضافية يحتاجها للتنفيذ.
  • errorCallback: دالة تُنفّذ عندما يفشل webpack في تحميل dependencies.
  • chunkName: اسم يُعطى للـ chunk الذي ينشئه استدعاء require.ensure() هذا. عبر تمرير chunkName نفسه لعدة استدعاءات require.ensure()، يمكن دمج كودها داخل chunk واحد، مما ينتج bundle واحدًا فقط يحتاج المتصفح إلى تحميله.

AMD

Asynchronous Module Definition (AMD) هي مواصفة JavaScript تحدد واجهة لكتابة modules وتحميلها. يدعم webpack دوال AMD التالية:

define (with factory)

define([name: String], [dependencies: String[]], factoryMethod: function(...))

إذا تم توفير dependencies، فسيتم استدعاء factoryMethod مع exports الخاصة بكل dependency بالترتيب نفسه. إذا لم يتم توفير dependencies، فسيتم استدعاء factoryMethod مع require وexports وmodule لأسباب توافقية. إذا أرجعت هذه الدالة قيمة، فسيتم تصدير تلك القيمة من module. يضمن compiler توفر كل dependency.

define(["jquery", "my-module"], ($, myModule) =>
  // افعل شيئًا باستخدام $ وmyModule...

  // صدّر دالة
  function doSomething() {
    // ...
  });

define (with value)

define(value: !Function)

سيصدّر هذا value المقدمة. يمكن أن تكون value هنا أي شيء باستثناء دالة.

define({
  answer: 42,
});

require (amd-version)

require(dependencies: String[], [callback: function(...)])

بشكل مشابه لـ require.ensure، ستُفصل dependencies المحددة إلى bundle مستقل يتم تحميله بشكل غير متزامن. سيتم استدعاء callback مع exports الخاصة بكل dependency داخل array المسماة dependencies.

import("b").then((b) => {
  import("c").then((c) => {
    // استخدم b و c
  });
});

Labeled Modules

يفعّل LabeledModulesPlugin الداخلي استخدام الدوال التالية للتصدير والطلب داخل modules:

export label

يصدّر value المحددة. يمكن أن تأتي label قبل function declaration أو variable declaration. يكون اسم الدالة أو اسم المتغير هو identifier الذي تُصدر تحته القيمة.

export: const answer = 42;
export: function method(value) {
  // افعل شيئًا...
};

require label

يجعل كل exports من dependency متاحة داخل النطاق الحالي. يمكن أن تظهر label الخاصة بـ require قبل string. يجب أن تصدّر dependency القيم باستخدام label الخاصة بـ export. لا يمكن استهلاك CommonJS أو AMD modules بهذه الطريقة.

some-dependency.js

export: const answer = 42;
export: function method(value) {
  // افعل شيئًا...
};
require: 'some-dependency';
console.log(answer);
method(...);

Webpack

إلى جانب صيغ modules الموضحة أعلاه، يسمح webpack أيضًا بعدة دوال مخصصة خاصة به:

require.context

require.context(
  (directory: String),
  (includeSubdirs: Boolean) /* اختياري، الافتراضي true */,
  (filter: RegExp) /* اختياري، الافتراضي /^\.\/.*$/ أي ملف */,
  (mode: String) /* اختياري، 'sync' | 'eager' | 'weak' | 'lazy' | 'lazy-once'، الافتراضي 'sync' */
);

حدد مجموعة كاملة من dependencies باستخدام مسار إلى directory، وخيار includeSubdirs، وfilter للتحكم الأدق في modules المضمنة، وmode لتحديد طريقة التحميل. يمكن بعد ذلك حل modules الأساسية لاحقًا:

const context = import.meta.webpackContext("components", {
  recursive: true,
  regExp: /\.html$/,
});
const componentA = context.resolve("componentA");

إذا تم ضبط mode على 'lazy'، فسيتم تحميل modules الأساسية بشكل غير متزامن:

const context = import.meta.webpackContext("locales", {
  recursive: true,
  regExp: /\.json$/,
  mode: "lazy",
});
context("localeA").then((locale) => {
  // افعل شيئًا باستخدام locale
});

القائمة الكاملة للـ modes المتاحة وسلوكها موضحة في توثيق import().

require.include

require.include((dependency: String));

يضمّن dependency بدون تنفيذه. يمكن استخدام ذلك لتحسين موضع module داخل output chunks.

import("a");
import("b");
import("c").then((moduleC) => {
  // استخدم moduleC هنا
});

سينتج عن ذلك output التالي:

  • entry chunk: file.js وa.
  • anonymous chunk: b.
  • anonymous chunk: c.

بدون require.include('a') كان سيتم تكراره في كلا anonymous chunks.

require.resolveWeak

يشبه require.resolve، لكنه لا يسحب module إلى bundle. هذا ما يُعد dependency "ضعيفًا".

if (__webpack_modules__[require.resolveWeak("module")]) {
  // افعل شيئًا عندما يكون module متاحًا...
}
if (require.cache[require.resolveWeak("module")]) {
  // افعل شيئًا عندما يكون module قد حُمّل سابقًا...
}

// يمكنك تنفيذ resolves ديناميكية "context"
// بطريقة مشابهة لدوال require/import الأخرى.
const page = "Foo";
__webpack_modules__[require.resolveWeak(`./page/${page}`)];

warning

إذا كان مصدر module يحتوي على require لا يمكن تحليله بشكل ثابت، فسيصدر تحذير critical dependencies.

مثال:

someFn(require);
require.bind(null);
require(variable);
·تعديل هذه الصفحة
السابق ›
واجهة Logger
‹ التالي
متغيرات Module

1 مساهم

RlxChap2