وحدات الأصول

تتيح وحدات الأصول (Asset Modules) استخدام ملفات الأصول، مثل الخطوط والأيقونات وغيرها، دون إعداد loaders إضافية.

قبل webpack 5 كان من الشائع استخدام:

  • raw-loader لاستيراد ملف كسلسلة نصية
  • url-loader لتضمين ملف داخل الحزمة على هيئة data URI
  • file-loader لإخراج ملف إلى مجلد المخرجات

تستبدل أنواع وحدات الأصول كل هذه الـ loaders بإضافة 5 أنواع جديدة من الوحدات:

  • asset/resource يُخرج ملفًا منفصلًا ويُصدّر عنوان URL. كان يمكن تحقيق ذلك سابقًا باستخدام file-loader.
  • asset/inline يُصدّر data URI للأصل. كان يمكن تحقيق ذلك سابقًا باستخدام url-loader.
  • asset/source يُصدّر الكود المصدري للأصل. كان يمكن تحقيق ذلك سابقًا باستخدام raw-loader.
  • asset/bytes يُصدّر منظور Uint8Array للأصل.
  • asset يختار تلقائيًا بين تصدير data URI وإخراج ملف منفصل. كان يمكن تحقيق ذلك سابقًا باستخدام url-loader مع حد لحجم الأصل.

عند استخدام loaders الأصول القديمة (أي file-loader/url-loader/raw-loader) مع وحدات الأصول في webpack 5، قد ترغب في منع وحدات الأصول من معالجة أصولك مرة أخرى لأن ذلك سيؤدي إلى تكرار الأصول. يمكن فعل ذلك بتعيين نوع وحدة الأصل إلى 'javascript/auto'.

webpack.config.js

export default {
  module: {
   rules: [
      {
        test: /\.(png|jpg|gif)$/i,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 8192,
            }
          },
        ],
+       type: 'javascript/auto'
      },
   ]
  },
}

لاستبعاد الأصول القادمة من استدعاءات new URL من loaders الأصول، أضف dependency: { not: ['url'] } إلى إعدادات الـ loader.

webpack.config.js

export default {
  module: {
    rules: [
      {
        test: /\.(png|jpg|gif)$/i,
+       dependency: { not: ['url'] },
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 8192,
            },
          },
        ],
      },
    ],
  }
}

المسار العام

افتراضيًا، وتحت الغطاء، ينفذ النوع asset التعبير __webpack_public_path__ + import.meta. هذا يعني أن ضبط output.publicPath في إعداداتك يتيح لك تجاوز عنوان URL الذي يُحمّل منه asset.

التجاوز أثناء التشغيل

إذا عيّنت __webpack_public_path__ داخل الكود، فالطريقة المطلوبة لتحقيق ذلك دون كسر منطق تحميل asset هي التأكد من تشغيله كأول كود في تطبيقك، وعدم استخدام دالة لفعل ذلك. مثال ذلك وجود ملف باسم publicPath.js بالمحتوى التالي:

__webpack_public_path__ = "https://cdn.url.com";

ثم تحدّث حقل entry في webpack.config.js ليبدو كالتالي:

export default {
  entry: ["./publicPath.js", "./App.js"],
};

بدلًا من ذلك، يمكنك فعل ما يلي داخل App.js دون تعديل إعدادات webpack. العيب الوحيد هو أنك ستحتاج إلى فرض ترتيب التنفيذ هنا، وقد يتعارض ذلك مع بعض أدوات linting.

import "./publicPath.js";

نوع المورد

webpack.config.js

import path from 'path';
import { fileURLToPath } from 'url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

export default {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  },
+ module: {
+   rules: [
+     {
+       test: /\.png/,
+       type: 'asset/resource',
+     },
+   ],
+ },
};

src/index.js

import mainImage from "./images/main.png";

img.src = mainImage; // '/dist/151cfcfa1bd74779aadb.png'

سيتم إخراج جميع ملفات .png إلى مجلد المخرجات وحقن مساراتها داخل الحزم. بالإضافة إلى ذلك، يمكنك تخصيص outputPath وpublicPath لها.

اسم ملف مخرجات مخصص

افتراضيًا، تُخرج وحدات asset/resource الملفات إلى مجلد المخرجات باسم ملف على النمط [hash][ext][query].

يمكنك تعديل هذا القالب بتعيين output.assetModuleFilename في إعدادات webpack:

webpack.config.js

import path from "node:path";
import { fileURLToPath } from 'node:url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

export default {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist'),
+   assetModuleFilename: 'images/[hash][ext][query]',
  },
  module: {
    rules: [
      {
        test: /\.png/,
        type: 'asset/resource',
      },
    ],
  },
};

حالة أخرى لتخصيص اسم ملف المخرجات هي إخراج نوع معيّن من الأصول إلى مجلد محدد:

import path from "node:path";
import { fileURLToPath } from 'node:url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

export default {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist'),
+   assetModuleFilename: 'images/[hash][ext][query]',
  },
  module: {
    rules: [
+     {
+       test: /\.html/,
+       type: 'asset/resource',
+       generator: {
+         filename: 'static/[hash][ext][query]',
+       },
+     },
    ],
  },
};

بهذه الإعدادات، سيتم إخراج جميع ملفات html إلى مجلد static داخل مجلد المخرجات.

Rule.generator.filename مماثل لـ output.assetModuleFilename، ويعمل فقط مع نوعي الوحدات asset وasset/resource.

تضمين الأصول

webpack.config.js

import path from "node:path";
import { fileURLToPath } from 'node:url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

export default {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist'),
  },
  module: {
    rules: [
+     {
+       test: /\.svg/,
+       type: 'asset/inline',
+     },
    ],
  },
};

src/index.js

import metroMap from "./images/metro.svg";

block.style.background = `url(${metroMap})`; // url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDo...vc3ZnPgo=)

سيتم حقن جميع ملفات .svg داخل الحزم على هيئة data URI.

مولّد data URI مخصص

افتراضيًا، تمثل data URI التي يُخرجها webpack محتوى الملف بعد ترميزه باستخدام خوارزمية Base64.

إذا أردت استخدام خوارزمية ترميز مخصصة، يمكنك تحديد دالة مخصصة لترميز محتوى الملف:

webpack.config.js

import path from "node:path";
import { fileURLToPath } from 'node:url';
+ import svgToMiniDataURI from "mini-svg-data-uri";

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);


export default {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.svg/,
        type: 'asset/inline',
+       generator: {
+         dataUrl: content => {
+           content = content.toString();
+           return svgToMiniDataURI(content);
+         },
+       },
      },
    ],
  },
};

الآن سيتم ترميز جميع ملفات .svg بواسطة حزمة mini-svg-data-uri.

نوع المصدر

webpack.config.js

import path from "node:path";
import { fileURLToPath } from 'node:url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

export default {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist'),
  },
  module: {
    rules: [
+     {
+       test: /\.txt/,
+       type: 'asset/source',
+     },
    ],
  },
};

src/example.txt

Hello world

src/index.js

import exampleText from "./example.txt";

block.textContent = exampleText; // 'Hello world';

استخدام بديل:

src/index.js

import exampleText from "./example.txt" with { type: "text" };

block.textContent = exampleText; // 'Hello world';

سيتم حقن جميع ملفات .txt داخل الحزم كسلاسل نصية بترميز UTF-8.

أصول URL

عند استخدام new URL('./path/to/asset', import.meta.url)، ينشئ webpack وحدة أصول أيضًا.

src/index.js

const logo = new URL("./logo.svg", import.meta.url);

اعتمادًا على target في إعداداتك، سيترجم webpack الكود أعلاه إلى نتيجة مختلفة:

// target: web
new URL(
  `${__webpack_public_path__}logo.svg`,
  document.baseURI || self.location.href,
);

// target: webworker
new URL(`${__webpack_public_path__}logo.svg`, self.location);

// target: node, node-webkit, nwjs, electron-main, electron-renderer, electron-preload, async-node
new URL(
  `${__webpack_public_path__}logo.svg`,
  require("node:url").pathToFileUrl(__filename),
);
// أي targets عند تفعيل مخرجات وحدات ECMA
new URL(`${__webpack_public_path__}logo.svg`, import.meta.url);

ابتداءً من webpack 5.38.0، أصبحت Data URLs مدعومة أيضًا داخل new URL():

src/index.js

const url = new URL("data:,", import.meta.url);
console.log(url.href === "data:,");
console.log(url.protocol === "data:");
console.log(url.pathname === ",");

نوع الأصل

webpack.config.js

import path from "node:path";
import { fileURLToPath } from 'node:url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

export default {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
+     {
+       test: /\.txt/,
+       type: 'asset',
+     },
    ],
  },
};

الآن سيختار webpack تلقائيًا بين resource وinline وفق شرط افتراضي: الملف الذي يقل حجمه عن 8kb سيُعامل كنوع وحدة inline، وإلا فسيُعامل كنوع وحدة resource.

يمكنك تغيير هذا الشرط بتعيين خيار Rule.parser.dataUrlCondition.maxSize على مستوى قاعدة الوحدة في إعدادات webpack:

webpack.config.js

import path from "node:path";
import { fileURLToPath } from 'node:url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

export default {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist'),
  },
  module: {
    rules: [
      {
        test: /\.txt/,
        type: 'asset',
+       parser: {
+         dataUrlCondition: {
+           maxSize: 4 * 1024, // 4 كيلوبايت
+         },
+       },
      },
    ],
  },
};

يمكنك أيضًا تحديد دالة لتقرير ما إذا كان ينبغي تضمين الوحدة أم لا.

نوع البايتات

webpack.config.js

import path from "node:path";
import { fileURLToPath } from 'node:url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

export default {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist'),
  },
  module: {
    rules: [
+     {
+       test: /\.txt/,
+       type: 'asset/bytes',
+     },
    ],
  },
};

src/example.txt

Hello world

src/index.js

import exampleText from "./example.txt";

const decoder = new TextDecoder("utf-8");
const textString = decoder.decode(exampleText);

block.textContent = textString; // 'Hello world';

استخدام بديل:

src/index.js

import exampleText from "./example.txt" with { type: "bytes" };

const decoder = new TextDecoder("utf-8");
const textString = decoder.decode(exampleText);

block.textContent = textString; // 'Hello world';

سيتم حقن جميع ملفات .txt داخل الحزم كبايتات خام (Uint8Array)، دون أي ترميز نصي أو تحويل.

استبدال صيغة الـ Inline Loader

قبل وحدات الأصول وWebpack 5، كان من الممكن استخدام الصيغة المضمنة مع الـ loaders القديمة المذكورة أعلاه.

يوصى الآن بإزالة كل صيغ inline loader واستخدام شرط resourceQuery لمحاكاة وظيفة الصيغة المضمنة.

على سبيل المثال، عند استبدال raw-loader بنوع asset/source:

- import myModule from 'raw-loader!my-module';
+ import myModule from 'my-module?raw';

وفي إعدادات webpack:

module: {
    rules: [
    // ...
+     {
+       resourceQuery: /raw/,
+       type: 'asset/source',
+     }
    ]
  },

وإذا أردت استبعاد الأصول الخام من المعالجة بواسطة loaders أخرى، فاستخدم شرطًا سلبيًا:

module: {
    rules: [
    // ...
+     {
+       test: /\.m?js$/,
+       resourceQuery: { not: [/raw/] },
+       use: [ ... ]
+     },
      {
        resourceQuery: /raw/,
        type: 'asset/source',
      }
    ]
  },

أو قائمة قواعد oneOf. هنا سيتم تطبيق أول قاعدة مطابقة فقط:

module: {
    rules: [
    // ...
+     { oneOf: [
        {
          resourceQuery: /raw/,
          type: 'asset/source',
        },
+       {
+         test: /\.m?js$/,
+         use: [ ... ]
+       },
+     ] }
    ]
  },

تعطيل إخراج الأصول

في حالات استخدام مثل التصيير من جهة الخادم (Server side rendering)، قد ترغب في تعطيل إخراج الأصول، وهذا ممكن باستخدام خيار emit تحت Rule.generator:

export default {
  // …
  module: {
    rules: [
      {
        test: /\.png$/i,
        type: "asset/resource",
        generator: {
          emit: false,
        },
      },
    ],
  },
};
Edit this page·

1 Contributor

arabpolice