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 Modules | modules: 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> من runtime | style-loader |
"text" | يصدّر CSS في صورة string | css-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 / chunkFilename | output.cssFilename / output.cssChunkFilename |
style-loader | exportType: "style" |
css-loader | تحليل CSS المدمج (لا حاجة إلى loader) |
css-loader url / import | module.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.localIdentName | generator localIdentName |
css-loader modules.exportLocalsConvention | generator exportsConvention |
css-loader modules.namedExport | module.parser.css.namedExports (true افتراضيًا) |
css-loader modules.exportOnlyLocals | generator exportsOnly |
css-loader esModule | generator 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 | البديل المدمج |
|---|---|
filename | output.cssFilename |
chunkFilename | output.cssChunkFilename |
loader publicPath | output.publicPath |
loader esModule | generator 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 | البديل المدمج |
|---|---|
url | module.parser.css.url — default true |
import | module.parser.css.import — default true |
importLoaders | لا مقابل له؛ تُطبق loaders في السلسلة تلقائيًا على الملفات المستوردة عبر @import |
sourceMap | يتحكم فيه devtool، ويدعم إدخال css خاصًا بالنوع |
esModule | module.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.localIdentName | generator localIdentName |
modules.exportLocalsConvention | generator exportsConvention |
modules.namedExport | parser namedExports، وقيمته الافتراضية true |
modules.exportOnlyLocals | generator exportsOnly |
modules.localIdentHashSalt | generator localIdentHashSalt |
modules.localIdentHashFunction | generator 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.
| الخيار | النوع | الافتراضي | الوصف |
|---|---|---|---|
import | boolean | true | معالجة قواعد @import. |
url | boolean | true | معالجة url() وimage-set() وsrc() وimage(). |
namedExports | boolean | true | تصدير الأسماء المحلية في CSS Modules كصادرات مسماة في ES modules. |
exportType | "link" | "style" | "text" | "css-style-sheet" | "link" | كيفية إخراج CSS؛ راجع أوضاع الإخراج. |
pure | boolean | false | وضع pure الصارم؛ يجب أن يحتوي كل محدد على class أو id محلي. يخص css/module وcss/auto فقط. |
as | "stylesheet" | "block-contents" | "stylesheet" | تحليل المصدر كورقة أنماط كاملة أو كمحتوى كتلة. |
animation | boolean | true | إعادة تسمية أسماء @keyframes المحلية. |
container | boolean | true | إعادة تسمية أسماء @container المحلية. |
customIdents | boolean | true | إعادة تسمية المعرّفات المخصصة. |
dashedIdents | boolean | true | إعادة تسمية المعرّفات ذات الشرطات، مثل الخصائص المخصصة. |
function | boolean | true | إعادة تسمية أسماء @function المحلية. |
grid | boolean | true | إعادة تسمية معرّفات خطوط 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
| الخيار | النوع | الافتراضي | الوصف |
|---|---|---|---|
localIdentName | string | function | "[uniqueName]-[id]-[local]" (تطوير) / "[fullhash]" (إنتاج) | قالب أسماء classes المحلية المولّدة. |
exportsConvention | "as-is" | "camel-case" | "camel-case-only" | "dashes" | "dashes-only" | function | "as-is" | اصطلاح تسمية الأسماء المحلية المصدّرة. |
exportsOnly | boolean | true للأهداف التي لا تحتوي document مثل node، وإلا false | تصدير الأسماء المحلية فقط دون إخراج ورقة أنماط (SSR). |
esModule | boolean | true | استخدام صيغة ES modules في JavaScript المولّدة. |
localIdentHashFunction | string | output.hashFunction | دالة hash المستخدمة في localIdentName. |
localIdentHashDigest | string | "base64url" | ترميز hash للمعرّفات المحلية. |
localIdentHashDigestLength | number | 6 | طول hash للمعرّفات المحلية. |
localIdentHashSalt | string | output.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 متقدمة، فتحقق من كل جزء قبل إتمام الترحيل.



