CSS الأصلي

يوضح هذا الدليل كيفية استخدام معالجة CSS المدمجة في webpack عبر experiments.css، وكيفية ترحيل إعداد قائم بعيدًا عن css-loader وstyle-loader وmini-css-extract-plugin.

البدء

فعّل دعم CSS الأصلي في إعدادات webpack:

webpack.config.js

export default {
  experiments: {
    css: true,
  },
};

بعد تفعيل هذا الخيار، يتعامل webpack مع ملفات .css بوصفها modules من الدرجة الأولى؛ فيحلل @import وurl()، ويستخرج أوراق الأنماط، وينشئ content hashes، ويدعم CSS Modules، دون الحاجة إلى css-loader أو style-loader أو mini-css-extract-plugin.

استيراد CSS

بعد تفعيل التجربة، استورد ملفات .css مباشرة من JavaScript:

src/index.js

import "./styles.css";

const element = document.createElement("h1");
element.textContent = "Hello native CSS";
document.body.appendChild(element);

src/styles.css

h1 {
  color: #1f6feb;
}

يعالج webpack ملف CSS ويضمّنه في مخرجات البناء.

أنواع CSS modules

تضيف معالجة CSS المدمجة أربع قيم لـRule.type. ومعرفة النوع المناسب خطوة أساسية في الترحيل، لأن كل نوع يقابل وضعًا مختلفًا في css-loader ضمن modules.mode:

النوعنطاق الأسماءما يقابله في css-loader
cssعام، دون تحليل CSS Modulesmodules: false
css/globalتكون المحددات عامة، مع احترام :local()modules.mode: 'global'
css/moduleتكون المحددات محلية افتراضيًا، ويُستخدم :global() لجعلها عامةmodules.mode: 'local'
css/autoيختار css/module لملفات *.module.css و*.modules.css، ويختار css/global لما عداهاmodules.auto: true

القاعدة الافتراضية التي يضيفها webpack للنمط /\.css$/i هي css/auto؛ ولذلك تصبح ملفات *.module.css من نوع CSS Modules، بينما تبقى بقية الملفات عامة. يطابق هذا السلوك أكثر إعدادات css-loader شيوعًا دون تخصيص إضافي.

CSS Modules

عند استخدام css/auto، سمِّ الملف *.module.css أو *.modules.css لتفعيل CSS Modules له:

src/button.module.css

.button {
  background: #0d6efd;
  color: white;
  border: 0;
  border-radius: 4px;
  padding: 8px 12px;
}

src/index.js

import * as styles from "./button.module.css";

const button = document.createElement("button");
button.className = styles.button;
button.textContent = "Click me";
document.body.appendChild(button);

يمكنك تخصيص سلوك CSS Modules عبر خيارات parser وgenerator. راجع جميع الخيارات مع أمثلة أدناه:

webpack.config.js

export default {
  experiments: {
    css: true,
  },
  module: {
    parser: {
      "css/auto": {
        namedExports: true,
      },
    },
    generator: {
      "css/auto": {
        exportsConvention: "camel-case-only",
        localIdentName: "[uniqueName]-[id]-[local]",
      },
    },
  },
};

ميزات CSS Modules المدعومة

تفهم CSS Modules المدمجة ميزات التأليف نفسها التي يدعمها css-loader، لذا يمكن ترحيل معظم أوراق الأنماط دون تعديل:

  • composes: يركب class محلية من أخرى، بما في ذلك composes: foo from "./other.module.css"، وتكون قيمة التصدير قائمة أسماء classes مفصولة بمسافات.
  • @value: يعرّف قيمًا قابلة لإعادة الاستخدام ويستوردها، مثل @value primary: #1f6feb; و@value primary from "./vars.module.css".
  • :export: يصدّر أزواج مفاتيح وقيم مخصصة إلى JavaScript.
  • :local() و:global(): يبدلان نطاق الأسماء ضمن السطر في أي نوع من modules.
/* button.module.css */
@value brand: #1f6feb;

.base {
  padding: 8px 12px;
}
.primary {
  composes: base;
  background: brand;
}
:export {
  brandColor: brand;
}

أوضاع الإخراج (exportType)

يمكن إخراج CSS module واحدة بأربع طرائق. يحدد خيار parser المسمى exportType الطريقة المستخدمة، وكل واحدة منها تحل محل جزء مختلف من سلسلة الأدوات التقليدية:

exportTypeالسلوكما يحل محله
"link" (الافتراضي)يستخرج ملف .css يُحمّل عبر <link>mini-css-extract-plugin
"style"يحقن عنصر <style> من runtimestyle-loader
"text"يصدّر CSS في صورة stringcss-loader exportType: 'string'
"css-style-sheet"يصدّر كائن CSSStyleSheet قابلًا للإنشاءcss-loader exportType: 'css-style-sheet'

اضبطه عموميًا لكل نوع من modules:

export default {
  experiments: { css: true },
  module: {
    parser: {
      "css/auto": {
        exportType: "style",
      },
    },
  },
};

أو اضبطه في قاعدة محددة لمجموعة فرعية من الملفات:

export default {
  experiments: { css: true },
  module: {
    rules: [
      {
        test: /\.css$/i,
        type: "css/auto",
        parser: { exportType: "style" },
      },
    ],
  },
};

دليل الترحيل

نظرة سريعة

الإعداد التقليديالبديل المدمج
mini-css-extract-plugin (MiniCssExtractPlugin.loader)الاستخراج المدمج (exportType: "link" افتراضيًا)
MiniCssExtractPlugin filename / chunkFilenameoutput.cssFilename / output.cssChunkFilename
style-loaderexportType: "style"
css-loaderتحليل CSS المدمج (لا حاجة إلى loader)
css-loader url / importmodule.parser.css.url / import (كلاهما true افتراضيًا)
css-loader modules (الاكتشاف التلقائي لـ.module.css)نوع module ‏css/auto
css-loader modules.modeالنوع css/module أو css/global مع pure
css-loader modules.localIdentNamegenerator localIdentName
css-loader modules.exportLocalsConventiongenerator exportsConvention
css-loader modules.namedExportmodule.parser.css.namedExports (true افتراضيًا)
css-loader modules.exportOnlyLocalsgenerator exportsOnly
css-loader esModulegenerator esModule (true افتراضيًا)
css-loader exportType: 'string' / 'css-style-sheet'exportType: "text" / "css-style-sheet"

رحّل loader واحدة في كل مرة. رُتبت الأقسام التالية بحيث يبقى البناء سليمًا بعد كل خطوة.

1. ابدأ بإعداد تقليدي

webpack.config.js

import MiniCssExtractPlugin from "mini-css-extract-plugin";

export default {
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: [MiniCssExtractPlugin.loader, "css-loader"],
      },
    ],
  },
  plugins: [new MiniCssExtractPlugin()],
};

2. فعّل CSS المدمج

webpack.config.js

export default {
  experiments: {
    css: true,
  },
};

تتولى القاعدة المدمجة /\.css$/i التي تستخدم css/auto معالجة استيرادات .css. احذف القاعدة المخصصة والـplugin بعد التأكد في الأقسام التالية من وجود بديل لكل خيار تستخدمه.

3. استبدل mini-css-extract-plugin

تستخرج معالجة CSS المدمجة أوراق الأنماط وتضيف إليها content hashes افتراضيًا (exportType: "link")، ولذلك لم تعد بحاجة إلى الـplugin أو loader الخاصة بها:

webpack.config.js

-import MiniCssExtractPlugin from "mini-css-extract-plugin";
-
 export default {
+  experiments: {
+    css: true,
+  },
-  module: {
-    rules: [
-      {
-        test: /\.css$/i,
-        use: [MiniCssExtractPlugin.loader, "css-loader"],
-      },
-    ],
-  },
-  plugins: [new MiniCssExtractPlugin()],
 };

انقل خيارات الـplugin المتبقية إلى البدائل الآتية:

mini-css-extract-pluginالبديل المدمج
filenameoutput.cssFilename
chunkFilenameoutput.cssChunkFilename
loader publicPathoutput.publicPath
loader esModulegenerator esModule (true افتراضيًا)
ignoreOrderلا مقابل له؛ فمعالجة CSS المدمجة لا تصدر تحذيرات تعارض الترتيب

webpack.config.js

export default {
  experiments: { css: true },
  output: {
    cssFilename: "[name].[contenthash].css",
    cssChunkFilename: "[id].[contenthash].css",
  },
};

4. استبدل css-loader

لمعظم خيارات css-loader بديل مدمج ضمن module.parser.css وmodule.generator.css. تتطابق القيم الافتراضية الشائعة، أي تفعيل url وimport وnamedExports، مع إعداد css-loader المعتاد، لذا لا تحتاج مشاريع كثيرة إلى تخصيص parser إطلاقًا.

خيار css-loaderالبديل المدمج
urlmodule.parser.css.url — default true
importmodule.parser.css.import — default true
importLoadersلا مقابل له؛ تُطبق loaders في السلسلة تلقائيًا على الملفات المستوردة عبر @import
sourceMapيتحكم فيه devtool، ويدعم إدخال css خاصًا بالنوع
esModulemodule.generator.css.esModule، وقيمته الافتراضية true
exportType: 'string'parser exportType: "text"
exportType: 'css-style-sheet'parser exportType: "css-style-sheet"
modules (اكتشاف تلقائي)نوع module المدمج css/auto
modules.mode: 'local'النوع css/module
modules.mode: 'global'النوع css/global
modules.mode: 'pure'parser pure: true
modules.localIdentNamegenerator localIdentName
modules.exportLocalsConventiongenerator exportsConvention
modules.namedExportparser namedExports، وقيمته الافتراضية true
modules.exportOnlyLocalsgenerator exportsOnly
modules.localIdentHashSaltgenerator localIdentHashSalt
modules.localIdentHashFunctiongenerator localIdentHashFunction

فمثلًا، إعداد CSS Modules التالي في css-loader:

export default {
  module: {
    rules: [
      {
        test: /\.module\.css$/i,
        use: [
          {
            loader: "css-loader",
            options: {
              modules: {
                localIdentName: "[local]-[hash:base64:6]",
                exportLocalsConvention: "camel-case-only",
                namedExport: true,
              },
            },
          },
        ],
      },
    ],
  },
};

يصبح:

webpack.config.js

export default {
  experiments: { css: true },
  module: {
    parser: {
      "css/auto": {
        namedExports: true,
      },
    },
    generator: {
      "css/auto": {
        localIdentName: "[local]-[hash:base64:6]",
        exportsConvention: "camel-case-only",
      },
    },
  },
};

تعمل بعض خيارات css-loader بطريقة مختلفة:

  • getLocalIdent: بدلًا من دالة مخصصة، تعتمد معالجة CSS المدمجة على قالب localIdentName لتوليد الأسماء، وهو يقبل دالة أيضًا.
  • getJSON: تصدّر CSS module نفسها خريطة أسماء classes، ويمكن قراءتها من module graph الخاص بـcompilation. لذلك تستطيع plugin صغيرة تحويلها إلى JSON إذا كان إطار العمل يحتاج إلى ملف على القرص. وغالبًا لا تحتاج إليها أصلًا في التصيير من جهة الخادم؛ راجع التصيير من جهة الخادم.
  • لا يوجد بديل مدمج لـlocalIdentRegExp أو دوال التصفية callback في url وimport. احتفظ بـcss-loader للملفات المتأثرة، أو استبعد طلبات محددة باستخدام IgnorePlugin.

5. استبدل style-loader

إذا كنت تستخدم style-loader لحقن الأنماط في وقت التشغيل بدل استخراج ملف، فاضبط exportType: "style":

webpack.config.js

export default {
  experiments: { css: true },
  module: {
    parser: {
      "css/auto": {
        exportType: "style",
      },
    },
  },
};

يحقن هذا الإعداد عنصر <style> من webpack runtime، ويغطي الاستخدام الافتراضي لـstyle-loader (injectType: "styleTag"). قيده بقاعدة واحدة إذا أردت حقن بعض الملفات فقط مع استخراج البقية:

export default {
  experiments: { css: true },
  module: {
    rules: [
      {
        test: /\.inline\.css$/i,
        type: "css/auto",
        parser: { exportType: "style" },
      },
    ],
  },
};

ملاحظات حول خيارات style-loader: يقابل injectType: "linkTag" القيمة الافتراضية exportType: "link"، أي الاستخراج. ولا يوجد بديل مدمج للخيارات attributes وinsert وstyleTagTransform، لذا احتفظ بـstyle-loader إذا كنت تعتمد عليها.

6. واصل استخدام المعالجات المسبقة (Sass وLess وPostCSS)

تحل معالجة CSS المدمجة محل CSS loaders، لا loaders الخاصة بالمعالجات المسبقة. أبقِ loader المعالج المسبق ضمن use واضبط type في القاعدة على css/auto ليتعامل webpack مع مخرجات loader بوصفها CSS:

webpack.config.js

export default {
  experiments: { css: true },
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        use: ["postcss-loader", "sass-loader"],
        type: "css/auto",
      },
    ],
  },
};

يصرّف sass-loader الشيفرة إلى CSS، ثم يعالجها postcss-loader، وبعد ذلك تتولى معالجة CSS المدمجة الاستخراج وurl() وCSS Modules. يعمل النمط نفسه مع less-loader وstylus-loader وما شابههما.

7. التصيير من جهة الخادم (node وweb)

يتطلب SSR عادةً عمليتي بناء: حزمة web للمتصفح وحزمة node للخادم. ويجب أن تتطابق أسماء classes في CSS Modules كي تجري hydration للشيفرة المصيّرة على الخادم بصورة سليمة لدى العميل. كان getJSON في css-loader يُستخدم غالبًا لمزامنة هذه الأسماء، أما مع CSS المدمج فيمكن تجنب هذه الخطوة تمامًا بجعل localIdentName حتميًا ومتطابقًا بين الأهداف.

استخدم قالبًا مبنيًا على المسار دون hash على مستوى compilation كاملة، حتى يُنتج اسم class نفسه لكل هدف:

webpack.config.js

const common = {
  experiments: { css: true },
  module: {
    rules: [
      {
        test: /\.module\.css$/i,
        type: "css/module",
        generator: {
          // تبقى `[file]__[local]` ثابتة بين الأهداف، فلا حاجة إلى المزامنة عبر getJSON.
          localIdentName: "[file]__[local]",
        },
      },
    ],
  },
};

export default [
  { ...common, name: "web", target: "web" },
  { ...common, name: "node", target: "node" },
];

عند استخدام هدف node تكون القيمة الافتراضية في CSS generator هي exportsOnly: true؛ ولذلك يصدّر بناء الخادم خريطة أسماء classes فقط ولا يُخرج ورقة أنماط، وهو ما يحتاجه مصيّر SSR بالضبط. في المقابل، يواصل بناء المتصفح استخراج CSS الفعلي. وإذا فضّلت تخصيصًا واحدًا، فإن target: ["web", "node"] ينشئ حزمة شاملة تعمل في البيئتين.

8. أبقِ الاستيرادات كما هي وتحقق من النتيجة

تبقى استيرادات JavaScript كما هي:

import "./styles.css";
import * as styles from "./button.module.css";

ثم تحقق مما يلي:

  • تُطبق الأنماط بصورة صحيحة في بيئة التطوير.
  • تُخرج ملفات .css المستخرجة في بيئة الإنتاج.
  • تتطابق صادرات CSS Modules مع طريقة استخدامها حاليًا.

جميع الخيارات مع أمثلة

اضبط الخيارات لكل نوع من modules ضمن module.parser وmodule.generator. المفاتيح المتاحة هي css وcss/auto وcss/global وcss/module. تستخدم الأمثلة التالية css/auto لأنه النوع الذي تعتمد عليه القاعدة الافتراضية.

خيارات Parser

القيمة الافتراضية لجميع خيارات parser المنطقية التالية هي true.

الخيارالنوعالافتراضيالوصف
importbooleantrueمعالجة قواعد @import.
urlbooleantrueمعالجة url() وimage-set() وsrc() وimage().
namedExportsbooleantrueتصدير الأسماء المحلية في CSS Modules كصادرات مسماة في ES modules.
exportType"link" | "style" | "text" | "css-style-sheet""link"كيفية إخراج CSS؛ راجع أوضاع الإخراج.
purebooleanfalseوضع pure الصارم؛ يجب أن يحتوي كل محدد على class أو id محلي. يخص css/module وcss/auto فقط.
as"stylesheet" | "block-contents""stylesheet"تحليل المصدر كورقة أنماط كاملة أو كمحتوى كتلة.
animationbooleantrueإعادة تسمية أسماء @keyframes المحلية.
containerbooleantrueإعادة تسمية أسماء @container المحلية.
customIdentsbooleantrueإعادة تسمية المعرّفات المخصصة.
dashedIdentsbooleantrueإعادة تسمية المعرّفات ذات الشرطات، مثل الخصائص المخصصة.
functionbooleantrueإعادة تسمية أسماء @function المحلية.
gridbooleantrueإعادة تسمية معرّفات خطوط grid ومناطقه.
export default {
  experiments: { css: true },
  module: {
    parser: {
      "css/auto": {
        import: true,
        url: true,
        namedExports: true,
        exportType: "link",
        pure: false,
        // أعد تسمية @keyframes فقط، واترك معرّفات @container وgrid دون تغيير.
        animation: true,
        container: false,
        grid: false,
      },
    },
  },
};

خيارات Generator

الخيارالنوعالافتراضيالوصف
localIdentNamestring | function"[uniqueName]-[id]-[local]" (تطوير) / "[fullhash]" (إنتاج)قالب أسماء classes المحلية المولّدة.
exportsConvention"as-is" | "camel-case" | "camel-case-only" | "dashes" | "dashes-only" | function"as-is"اصطلاح تسمية الأسماء المحلية المصدّرة.
exportsOnlybooleantrue للأهداف التي لا تحتوي document مثل node، وإلا falseتصدير الأسماء المحلية فقط دون إخراج ورقة أنماط (SSR).
esModulebooleantrueاستخدام صيغة ES modules في JavaScript المولّدة.
localIdentHashFunctionstringoutput.hashFunctionدالة hash المستخدمة في localIdentName.
localIdentHashDigeststring"base64url"ترميز hash للمعرّفات المحلية.
localIdentHashDigestLengthnumber6طول hash للمعرّفات المحلية.
localIdentHashSaltstringoutput.hashSaltقيمة salt المستخدمة في hash المعرّفات المحلية.
export default {
  experiments: { css: true },
  module: {
    generator: {
      "css/auto": {
        localIdentName: "[uniqueName]-[id]-[local]",
        exportsConvention: "camel-case-only",
        esModule: true,
        exportsOnly: false,
        localIdentHashDigest: "base64url",
        localIdentHashDigestLength: 6,
      },
    },
  },
};

يقبل exportsConvention أيضًا دالة تعيد string أو string[]. وعند إعادة مصفوفة، يُصدّر الاسم المحلي تحت عدة أسماء بديلة، بما يطابق سلوك css-loader.

أمثلة شائعة

CSS Modules مع صادرات مسماة

src/app.module.css

.primary {
  color: #1f6feb;
}
.large-text {
  font-size: 2rem;
}

src/index.js

import { largeText, primary } from "./app.module.css";

document.body.classList.add(primary, largeText);

webpack.config.js

export default {
  experiments: { css: true },
  module: {
    generator: {
      "css/auto": {
        exportsConvention: "camel-case-only",
      },
    },
  },
};

استخراج ملفات CSS بأسماء تتضمن hash للإنتاج

webpack.config.js

export default {
  mode: "production",
  experiments: { css: true },
  output: {
    cssFilename: "css/[name].[contenthash].css",
    cssChunkFilename: "css/[id].[contenthash].css",
  },
};

حقن وسوم <style> في وقت التشغيل على طريقة style-loader

webpack.config.js

export default {
  experiments: { css: true },
  module: {
    parser: {
      "css/auto": {
        exportType: "style",
      },
    },
  },
};

استيراد ورقة أنماط قابلة للإنشاء

src/index.js

import sheet from "./theme.css" with { type: "css" };

document.adoptedStyleSheets = [sheet];

يحوّل webpack سمة الاستيراد with { type: "css" } تلقائيًا إلى exportType: "css-style-sheet"، ويمنحك instance من CSSStyleSheet.

استيراد CSS في صورة string

webpack.config.js

export default {
  experiments: { css: true },
  module: {
    parser: {
      "css/auto": {
        exportType: "text",
      },
    },
  },
};

src/index.js

import css from "./styles.css";

const style = new CSSStyleSheet();
style.replaceSync(css);

استخدام الأنماط العامة وmodules محددة النطاق معًا

مع قاعدة css/auto الافتراضية، تكون ملفات *.module.css محددة النطاق وتبقى بقية الملفات عامة، دون أي تخصيص إضافي:

import "./reset.css"; // عام
import * as card from "./card.module.css"; // محدد النطاق

الحالة التجريبية والقيود المعروفة

ما يزال experiments.css تجريبيًا؛ تعامل معه كميزة اختيارية واختبره بعناية قبل تعميمه.

  • قد تستمر واجهات API والسلوك في التطور قبل اعتمادها افتراضيًا في webpack v6.
  • لا توجد بدائل مباشرة لبعض خيارات loaders: ‏localIdentRegExp ودوال التصفية في css-loader، وخيارات attributes وinsert وstyleTagTransform في style-loader. احتفظ بالـloader للملفات التي تحتاج إليها. يقابل getLocalIdent صيغة الدالة في localIdentName، أما getJSON وSSR فتغطيهما طريقة مطابقة أسماء classes بين الأهداف.
  • لا يوجد بديل لـimportLoaders؛ إذ تُطبق loaders في السلسلة تلقائيًا على الملفات المستوردة عبر @import.
  • إذا كان مشروعك يعتمد على سلاسل loaders متقدمة، فتحقق من كل جزء قبل إتمام الترحيل.
·تعديل هذه الصفحة

1 مساهم

arabpolice