# تحديث واجهتك الأمامية للبناء بناء الخط: من CDNs إلى التراكم

<datetime class="hidden">2025-11-11T02:15</datetime>

<!-- category -- ASP.NET, Tailwind, Webpack, Frontend -->
## أولاً

خلال العام الماضي، تعثرت، وجربت، وكسرت الأشياء، وطوّرت تدريجياً خط بناء الواجهة الأمامية لهذه المدونة. لم تكن هذه بعض الخطط الرئيسية التي نُفّذت بشكل خاطئ - لقد كانت تجربة وخطأ، الكثير من الأخطاء، والمثابرة العنيدة مدفوعة برؤية واضحة لما أردت تحقيقه.

أنا لست مُعلم واجهة أو مُطوراً أو مُطوراً على شبكة الإنترنت الذي أُحبط من قيود الاعتمادات القائمة على نظام CDN وقرر أنه يجب أن يكون هناك طريقة أفضل. هذه المقالة تُوثق الرحلة الفوضوية التكرارية من بسيطة `<script>` علامات تشير إلى unpgg إلى خط أنابيب عصري يعمل فعلاً.

لقد قمت بالكثير من الأخطاء على طول الطريق (والذي سأوثقه) قضيت ساعات في تصحيح الأخطاء المشفرة في ويب باقة أخطاء، وربما أعيد تركيبها `node_modules` لكنَّ المثابرة تؤتي ثمارها ، وتعلَّمت مبلغا هائلا من خلال تصميمي على تصحيح هذا الوضع .

إذا كنت مطوراً لشبكة الإنترنت يحدق في ملفات تشكيلات ويبباك يتساءل ما على الأرض الذي وضعت نفسك فيه - هذه المقالة هي لك.

في الأيام الأولى من الاتصال بالموقع الأول الذي بنيته من أجل المال (الذي لم يكن منبأ إباحي في بيرل... قصة لوقت آخر) كان موقعاً لشرطة الثلج الذي انحرف عن خط الهاتف الآلي.

في تلك الأيام كانت المخاوف مختلفة تماماً. جافاسكربت كان غير معروف عملياً. أعتقد أن حتى الـ Divs كانت RARE (في تلك الأيام IE كان `div` (و) كان `layer`و ستكون محظوظاً لو كان لمستخدميك اتصال بـ 56K. لذا تعلمت أن أفضل (حتى أشياء مثل خلفيات الصورة المملوءة للعناصر المدرجة في القائمة) وما إلى ذلك...

في وقت لاحق في مايكروسوفت عملت حتى على صورة آلية أداة spritter في أشكال ويب -- كان رهيبا: لقد استخرجت صورا صغيرة من صفحة، ولدت CSS و صورة sprite تلقائيا. ولكن للأسف، مثل وقتي في مايكروسوفت، لم يكن من المفترض أن يكون.

باختصار، إنّه هاجس أنّي تحملتُ كامل مسيرتي المهنيّة، لا أحد يحبّ الموقع البطيء!

**أن تكون عادلة**، CDNs لَيستْ شرّاً أصيلاً.

**الكشف عن شيء**: هذه المدونات مُهَدَّسَةُ عمداً. *الحاجة* لكن بناءه بهذه الطريقة، دعوني أتعلم أدوات الواجهة الحديثة بشكل صحيح، وبشكل حاسم أعطاني شيئاً حقيقياً لأكتب عنه وأدرسه. أحياناً أفضل طريقة للتعلم هي بناء شيء سخيف قليلاً وتوثيق الرحلة.

[TOC]

## المشكلة مع NCDNs

وفي البداية، كان نهجي واضحاً:

```html
<!-- Old CDN-based approach -->
<script src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"></script>
<script src="https://unpkg.com/htmx.org@2.x.x"></script>
<script src="https://cdn.jsdelivr.net/npm/easymde/dist/easymde.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/easymde/dist/easymde.min.css">
```

وفي حين أن هذا العمل ناجح، فإنه ينطوي على عدة أوجه قصور أصبحت محبطة بشكل متزايد:

- **قضايا الموثوقية**اسطوانات CDN تعمل بالهبوط. لقد رأيت ان هناك حالات نقص في الاسطوانات. عندما تكون في الأسفل، يكون موقعك معطوباً.
- **سلوك التحميل الذي لا يمكن التنبؤ به**: استجابات CDN يمكن أن تتفاوت تفاوتاً حاداً. أحياناً سريعة، وأحياناً بطيئة، وأحياناً تُدخل ظروفاً عرقية حيث تُحمِل المكتبات في النظام الخطأ.
- **باء - المسائل المتعلقة المتعلقة بالوصل إلى الوقت في الوقت**ليس لديك سيطرة على عندما تحمل النصوص على بعضها البعض. هذا سبب لي صداع لا نهاية له مع بدء المكتبة
- **طلبات HTTTP متعددة متعددة**: تحتاج كل مكتبة إلى طلب منفصل، حتى مع HTTTP/2
- **لا توجد شجرة تهتز**تُحمّلُ المكتبةَ الكاملةَ، حتى لو أنت فقط تَستعملُ a جزء منها
- **الأمم المتحدة**DCDNs قد تخدم نسخ مختلفة إلا إذا قمت بلصقها بشكل واضح، وحتى النسخ المثبتة يمكن أن تتصرف بشكل مختلف عبر مزودي CDN
- **عدم التيقن من وقت البناء**لا توجد طريقة للقبض على المتناقضات حتى وقت التشغيل - في كثير من الأحيان عندما يقوم المستخدم بالإبلاغ عنها
- **& مُنتم*** لا يمكن أن تُخطّط عبر الحدود أو تزيل الشفرة المميتة
- **التنمية التي يجري تشغيلها**:يتطلب الاتصال بالإنترنت، والذي يكون مزعجاً عند العمل على القطارات أو الطائرات

لذا أردت التحكم بالتحكم بالضبط في ما استخدمه موقعي واحتاجه للعمل. أردت أن أجمع فقط ما أحتاجه، وأضمن طلبية تحميل موثوق بها، وأحسن أداء. هذا يعني الانتقال بعيداً عن CDNs وصوب نهج تجميعي.

إنها أبسط لإنشاء، ومع HTTTP/2 وHTTTP/3 التعدد التكسين، فإن العرض العام متعدد الطلبات أقل أهمية بكثير مما كان عليه. بالنسبة للعديد من المشاريع، وخاصة المشاريع الصغيرة أو النماذج، يبقى CDNs خياراً معقولاً تماماً. ولكن بالنسبة لموقع إنتاج حيث أردت التحكم، الموثوقية، والتعظيم،

## النهج العصري للمدّ الحديث

وضعي الحالي يحزم كل مُعتمدات جافاسكربت من خلال حزمة ويب والعمليات CSS من خلال PostSSS.

### لماذا علب ويب بدلا من فيتي؟

قبل الغوص في التفاصيل التقنية، ينبغي لي أن أخاطب الفيل في الغرفة: لماذا أستخدم ويبباك عندما يكون هناك فيتي، رولوب، إسبنال، وغيرها من الحزم الحديثة؟

الاجابة الصادقة بسيطة : **كنت أعرف مسبقاً على ويب باك**.

كنت قد استخدمت حزمة ويب على نطاق واسع عند تدريسي دورة "بدء تطوير شبكة الإنترنت"، وكانت الأداة التي فهمتها. عندما قررت تحديث خط أنابيب بناء هذه المدونة، كنت بالفعل في مواجهة منحنى تعليمي حاد - فهم كسر الأشجار، تقسيم الرموز، أنظمة الوحدات، خطوط أنابيب ما بعد CSSS، وكيفية دمج كل هذا مع ASP.NET الأساسية.

**عادة ما تكون ممارسة جيدة عند العمل على المشاريع؛ الحد من 'الجديد' إلى 'القابل للادارة'. إنها طريقة أسهل للحد من عدم اليقين.**

إضافة "تعلّم أداة بناء جديدة تماماً" فوق ذلك بدا غير ضروري. حزمة ويب تعمل. إنها ناضجة. لديها وثائق ممتازة ونظام إيكولوجي هائل. والأهم من ذلك، يمكنني أن أركز طاقتي التعليمية على * على سبيل المثال:* من الحشوة الحديثة بدلاً من الحيل الخفية لأداة معينة.

**هل فايت أسرع؟** بالتأكيد، خادوم تطوير فيتي مع وحدات محلية لـ ESS والربط الذي يعمل بـ espany هو أسرع بكثير من حزمة ويب. بالنسبة للمشاريع الكبيرة مع المئات من الوحدات، الفرق كبير.

**هل يجب أن تستخدم فيتي لمشروع جديد؟** على الأرجح، نعم. إذا كنت تبدأ من جديد وليس لديك معرفة قائمة لـ Webpack، Vite على الأرجح الخيار الأفضل. هو أسرع، أسهل للتهيئة، ويمثل النهج الحديث لـ واجهة الأداة.

**هل أندم على استخدام حزمة ويب؟** ليس على الإطلاق. لقد أوصلني إلى حيث كنت بحاجة إلى أن أكون. الدروس التي تعلمتها عن التجميع، تقسيم الرموز، والتعظيم هي قابلة للتحويل إلى أي أداة بناء. وبصراحة، بالنسبة لمقياس هذه المدونة، الفرق في الأداء بين ويبباك وفيت هو لا يُذكر - نحن نتحدث عن ملي ثانية في التنمية إعادة بناء الأوقات.

وهو جانب رئيسي لكيفية بناء الأشياء، تبدأ مع ما هو EASY والبناء من ذلك الأساس.

والدرس الأعمى هنا هو أنه: **التقدم المحرز في النجاح والكمال**كان من الممكن أن أمضي أسابيع في البحث عن "أفضل" حزمة، مقارنة النقاط المرجعية، قراءة مقالات المقارنة، والتلميح على القرار. بدلاً من ذلك، اخترت الأداة التي أعرفها، جعلتها تعمل، وتحركت إلى الأمام. لقد أبقتني هذه العملية على النقل البحري بدلاً من التداول إلى ما لا نهاية.

ربما في يوم ما قد أهاجر إلى فيت. ربما لن أفعل. على أي حال، هذه المدونة لديها خط أنابيب بناء حديث، متفائل يعمل بشكل موثوق - وهذا ما يهم.

### 

الـ [`package.json`](https://github.com/scottgal/mostlylucidweb/blob/main/Mostlylucid/package.json) هذا الهيكل أخذ مني عدة محاولات للحصول على الحق - في البداية، نوغيت و npm هما KINDA متشابهان ولكن nppm أقل ودية بكثير عند إضافة pacakugs.

```json
{
  "dependencies": {
    //NOTE - This is a pre-release of these enhancements, the 1.0.0 release is OUT NOW!
    "@mostlylucid/mermaid-enhancements": "^1.0.0-alpha0",
    "alpinejs": "^3.14.1",
    "codemirror": "5.65.13",
    "core-js": "^3.39.0",
    "easymde": "2.20.0",
    "flatpickr": "^4.6.13",
    "highlight.js": "^11.10.0",
    "highlightjs-cshtml-razor": "^2.1.1",
    "html-to-image": "^1.11.13",
    "htmx.org": "^2.0.1",
    "mermaid": "^11.0.2",
    "regenerator-runtime": "^0.14.1",
    "svg-pan-zoom": "^3.6.2"
  },
  //These are just used for build; not needed to RUN the app so we have a separate place for 'em
  "devDependencies": {
    "@babel/core": "7.26.9",
    "@babel/preset-env": "7.26.9",
    "@tailwindcss/aspect-ratio": "^0.4.2",
    "@tailwindcss/forms": "^0.5.7",
    "@tailwindcss/typography": "^0.5.12",
    "autoprefixer": "10.4.21",
    "babel-loader": "10.0.0",
    "cpx": "1.5.0",
    "css-loader": "7.1.2",
    "cssnano": "7.0.6",
    "daisyui": "^4.12.10",
    "mini-css-extract-plugin": "^2.9.4",
    "npm-run-all": "4.1.5",
    "postcss": "8.5.3",
    "postcss-cli": "11.0.1",
    "postcss-import": "^16.1.0",
    "rimraf": "6.0.1",
    "style-loader": "4.0.0",
    "tailwindcss": "3.4.17",
    "terser-webpack-plugin": "^5.3.10",
    "webpack": "^5.91.0",
    "webpack-cli": "^5.1.4"
  }
}
```

**المُمْكِلَات** (Alpin.js, HTMX, EaseMDE, et al., et al.,) **وثائق التفويض** أدوات البناء (Webpack، Babel، معالجات PostCSS، وما إلى ذلك).

### 

الـ `package.json` تم تطوير الخادوم إلى حد كبير:

```json
{
  "scripts": {
    "clean": "rimraf ./.tmp ./wwwroot/css/dist ./wwwroot/js/dist",
    "copy:static": "cpx \"src/css/{raleway,easymde-overrides}.css\" \"wwwroot/css/dist\"",
    "copy:highlight": "cpx \"src/css/highlight/*.min.css\" \"wwwroot/css/highlight\"",
    "copy:all": "npm-run-all --parallel copy:static copy:highlight",
    "copy:watch": "cpx \"src/css/{raleway,easymde-overrides}.css\" \"wwwroot/css/dist\" --watch & cpx \"src/css/highlight/*.min.css\" \"wwwroot/css/highlight\" --watch",
    "tw:dev": "postcss ./src/css/main.css -o ./wwwroot/css/dist/main.css",
    "tw:prod": "postcss ./src/css/main.css -o ./wwwroot/css/dist/main.css --no-map --verbose",
    "tw:watch": "postcss ./src/css/main.css -o ./wwwroot/css/dist/main.css --watch",
    "js:dev": "webpack --env development",
    "js:prod": "webpack --mode production",
    "js:watch": "webpack watch --mode development",
    "dev": "npm-run-all clean --parallel copy:all tw:dev js:dev",
    "watch": "npm-run-all clean copy:all --parallel copy:watch tw:watch js:watch",
    "build": "npm-run-all clean copy:all --parallel tw:prod js:prod"
  }
}
```

هذا الضبط يُميّز ما يلي:

- **لا شيء**: يحذف مخرجات بناء سابقة باستخدام `rimraf`
- **ما يلي:*** انسخ ملفات CSS الساكنة التي لا تحتاج إلى معالجة (وهذه إما 'الاستيراد') لغرض خاص مثل تحميل خط Ralleway أو مكيف مثل تعزيزات تغيير الموضوع.
- **t tw:*** (أ) العمليات التي تقوم بها Tayilwind CSS من خلال نظام SSS
- **- - - - - - - - - - - - - - - - - - - - - - -*** الملحقات جافاScrbr مِن خلال وب
- **dv/atch/ching**: أوركستراتيرات خط الأنابيب بأكمله

الـ `npm-run-all` يُمَكِّن الحزمة التنفيذ المتوازي للبناء الأسرع.

يمكنك وضع `npm run build` لتشغيل السيارات أثناء بنائه المحلي لكنه جزء من الألم لـ CI (يجب أن تتأكد من أنه معطل وما إلى ذلك).

```xml
<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <SpaRoot>ClientApp\</SpaRoot>
  </PropertyGroup>

  <!-- Run npm install only when package.json changes -->
  <Target Name="NpmInstall" Inputs="$(SpaRoot)package.json" Outputs="$(SpaRoot)node_modules" BeforeTargets="Build">
    <Message Importance="high" Text="Running npm install in $(SpaRoot)" />
    <Exec WorkingDirectory="$(SpaRoot)" Command="npm ci" />
  </Target>

  <!-- Run npm build before the .NET build -->
  <Target Name="NpmBuild" DependsOnTargets="NpmInstall" BeforeTargets="Build">
    <Message Importance="high" Text="Running npm run build in $(SpaRoot)" />
    <Exec WorkingDirectory="$(SpaRoot)" Command="npm run build" />
  </Target>

</Project>

```

للقيام بذلك فقط اضف هذا إلى csproj... يمكنك حتى أن تقول فقط تشغيل أثناء dibuk' etc... ولكن أجده فوضوياً. أفضل فقط تشغيله يدوياً. `npm run watch` يقوم بذلك فقط، عندما يقوم أي من css / js في الملف بتغييره، يقوم بتشغيل المبنى تلقائياً.

ملاحظة: في عالم الـ "برنس" تقريباً يستعملون أجهزة الـ "دي ف" التي هي بقعة جميلة وتجعلك نوعاً ما تكرهين "أس بي إنت" محاولة ضعيفة

## جاري

إعدادات حزمة الويب هي حيث يحدث السحر  وحيث قضيت معظم وقتي في حل المشاكل. هذا لم ينبثق إلى الوجود بالكامل. إنها نتيجة لتكرارات لا تحصى، عمليات البحث لـ (ستاك) الزائدة عن الحاجة، وقراءة وثائق ويب باك في الساعة 2 صباحاً لمحاولة فهم لماذا بنيتي كانت تولد 47 قطعة من الملفات.

هذه هي التشكيلة الحالية (من [`webpack.config.js`](https://github.com/scottgal/mostlylucidweb/blob/main/Mostlylucid/webpack.config.js)) مع تفسيرات مفصلة لما يفعله كل جزء وسبب وجوده هناك:

```javascript
const TerserPlugin = require('terser-webpack-plugin');
const path = require('path');

module.exports = (env, argv) => {
    const isProduction = argv.mode === 'production';

    return {
        mode: isProduction ? 'production' : 'development',
        entry: {
            main: './src/js/main.js',
        },
        output: {
            filename: '[name].js',
            chunkFilename: '[name].[contenthash].js',
            path: path.resolve(__dirname, 'wwwroot/js/dist'),
            publicPath: '/js/dist/',
            module: true,
            clean: true,
        },
        experiments: {
            outputModule: true,
        },
        module: {
            rules: [
                {
                    test: /\.css$/i,
                    use: ['style-loader', 'css-loader'],
                },
                {
                    test: /\.js$/,
                    exclude: /node_modules/,
                    use: {
                        loader: 'babel-loader',
                        options: {
                            presets: [
                                ['@babel/preset-env', {
                                    targets: '> 0.25%, not dead',
                                    modules: false,
                                    useBuiltIns: 'usage',
                                    corejs: 3,
                                }],
                            ],
                        },
                    },
                },
            ],
        },
        resolve: {
            extensions: ['.js', '.mjs'],
            alias: {
                '@mostlylucid/mermaid-enhancements$': path.resolve(__dirname, 'node_modules/@mostlylucid/mermaid-enhancements/dist/index.min.js')
            }
        },
        optimization: {
            splitChunks: {
                chunks: 'all',
                minSize: 20000,
                maxSize: 100000,
                name: false,
            },
            runtimeChunk: {
                name: 'runtime',
            },
            minimize: isProduction,
            minimizer: isProduction ? [
                new TerserPlugin({
                    terserOptions: {
                        ecma: 2020,
                        compress: {
                            drop_console: true,
                            passes: 3,
                            toplevel: true,
                            pure_funcs: ['console.info', 'console.debug'],
                        },
                        mangle: {
                            toplevel: true,
                        },
                        format: {
                            comments: false,
                        },
                    },
                    extractComments: true,
                }),
            ] : [],
        },
        devtool: isProduction ? false : 'eval-source-map',
        performance: {
            hints: isProduction ? 'warning' : false,
        }
    };
};
```

### مفتاح مفتاح مفتاح

#### & تشفير و تشكّك

```javascript
optimization: {
    splitChunks: {
        chunks: 'all',
        minSize: 20000,
        maxSize: 100000,
        name: false,
    },
    runtimeChunk: {
        name: 'runtime',
    },
}
```

هذا الإعداد يقسم كودك إلى قطع صغيرة:

- **القطعات: ' ' الكل '**:: تحليل كلا من الواردات المتزامنة والمتزامنة
- **عدد العين: 20000**فقط إنشاء لـ وحدات أكبر من 20KB
- **التوصس: 100000**محاولات تقسيم أجزاء أكبر من 100 كيلوبايت
- **وقت التنفيذ**EXs outs Webpack تشغيل الوقت في ملف منفصل لتحسين الكعب الطويل الأجل

في الممارسة العملية، هذا يولد ملفات متعددة في `wwwroot/js/dist/`:

- `main.js` - نقطة دخول طلبك
- `runtime.js` - منطق تحميل وحدة ويب
- `[vendor].[contenthash].js` - بصورة تلقائية قسمة قطع البضائع
- قطع إضافية لطرق مشفرات ومواصفات التموين بالكسل

#### تَنْطِيج

```javascript
{
    test: /\.js$/,
    exclude: /node_modules/,
    use: {
        loader: 'babel-loader',
        options: {
            presets: [
                ['@babel/preset-env', {
                    targets: '> 0.25%, not dead',
                    modules: false,
                    useBuiltIns: 'usage',
                    corejs: 3,
                }],
            ],
        },
    },
}
```

Pabel spiples JavaScript الحديثة لدعم المتصفحات القديمة:

- **باء - برنامج الأمم المتحدة المشترك المعني بفيروس نقص المناعة البشرية/متلازمة نقص المناعة المكتسب (الإيدز)**دعم المتصفحات بحصة في السوق لا تزال قائمة لا تقل عن 0.25 في المائة
- **الدالة الوحدة:**:الاحتياطيات ES6 وحدات لضرب الأشجار بعلبة ويب
- **&: ' usage'**: بصورة تلقائية تشتمل على البوليفيلات فقط للمميزات التي تستخدمها
- **الموانيجز: 3**استخدامات للنسخة 3 للبوليفيلز

هذا يعني أنه يمكنني كتابة JavaScript الحديثة (async/await، سلسلة اختيارية، فحم لامحي) مع الحفاظ على دعم واسع للمتصفح.

#### الإنتاج

```javascript
minimizer: isProduction ? [
    new TerserPlugin({
        terserOptions: {
            ecma: 2020,
            compress: {
                drop_console: true,
                passes: 3,
                toplevel: true,
                pure_funcs: ['console.info', 'console.debug'],
            },
            mangle: {
                toplevel: true,
            },
            format: {
                comments: false,
            },
        },
        extractComments: true,
    }),
] : []
```

(أ) يقوم المطهر بتمييز بنـاءات الإنتاج بصورة عدوانية:

- **& conso**:: يزيل كل `console.log()` البيانات البيانية
- **الرخصة: 3**: عمليات ضغط ثلاثة أضعاف الحد الأقصى للحجم
- **المستوى الرفيع: حقيقي**مَنْززز أعلى أعلى مستوى مُرْفِث
- **مُنتقِر_ ununccs**يحذف معيّن مُنتمِز إلى مُجَرّر
- **تعليقاً خاطئاً**كل التعليقات من الناتج:

على هذه المدونة، هذا يقلل عادةً من أحجام حزمة جافاسكربت بنسبة 60 إلى 70% مقارنةً بالرمز غير المميّز.

## خط أنابيب لـ تيليد

تُجهّز CSSS من خلال PostSSS مع عدة ملحقات. هذا التشكيل (من [`postcss.config.js`](https://github.com/scottgal/mostlylucidweb/blob/main/Mostlylucid/postcss.config.js)) هو بسيط بشكل رحيم بالمقارنة مع Webpack:

```javascript
// postcss.config.js
module.exports = {
    plugins: {
        'postcss-import': {},
        tailwindcss: {},
        autoprefixer: {},
        cssnano: { preset: 'default' }
    }
}
```

ويعمل خط الأنابيب على النحو التالي:

1. **مخص - مستورد**:: الحملان: `@import` في ملفات CSSS
2. **ثني**:: العمليات: العمليات التي تُسيِّر التوجيهات وتولِّد فئات المرافق
3. **مُسند**إضافة الباعة البادئة لتوافق المتصفح
4. **Csssnnano**: يقلص الناتج النهائي لدائرة خدمات الدعم المركزية

### تنفيذ

الـ [`tailwind.config.js`](https://github.com/scottgal/mostlylucidweb/blob/main/Mostlylucid/tailwind.config.js) حدّد الملف حيث يجب أن يبحث التايل وون ويندد عن أسماء الفئات. `content` المسارات الصحيحة كانت حرجة - في حالة عدم وجود مسار و تايلويند لن يولّد فصولاً لتلك الملفات:

```javascript
module.exports = {
    content: ["./Views/**/*.cshtml", "./EmailSubscription/**/*.cshtml"],
    safelist: ["dark", "light"],
    darkMode: "class",
    theme: {
        fontFamily: {
            body: ["Raleway", "sans-serif"],
        },
        extend: {
            colors: {
                "custom-light-bg": "#ffffff",
                "custom-dark-bg": "#1d232a",
                primary: "#072344",
                secondary: "#00aaa1",
                // ... more custom colours
            },
        },
    },
    plugins: [
        require("@tailwindcss/aspect-ratio"),
        require("@tailwindcss/typography"),
        require("daisyui"),
    ],
};
```

السمات الرئيسية:

- **المحتوى**:: المسوح: `.cshtml` لملفات لأسماء الفئات (بما في ذلك آراء الموازِزِزِر و قو
- **مُسِكِل: مُسْكِل**دائماً يتضمن هذه الفئات حتى وإن لم توجد في المسح
- **المود: "الطبقة"**مُمْكِن
- **مُوجَز مُنْجِزًا**يُضاف إلى اللّونات المقدّرة وقيم المباعدة
- ****بما في ذلك ملحقات تايلويد ومكتبة عنصر ديزي أوي

يقوم تايلويد بمسح هذه الملفات في وقت البناء، يستخرج فقط فئات المرافق التي تستخدمها بالفعل. هذا هو السبب في أن ملف CSS النهائي أصغر بكثير من مكتبة Tailwind الكاملة.

## جافSciSciS مفك نقطة مدخل نقطة

الـ [`main.js`](https://github.com/scottgal/mostlylucidweb/blob/main/Mostlylucid/src/js/main.js) الملف هو حيث كل شيء يتجمع معاً. هذا الملف يستورد ويفتح كل الاعتماديات، وهو ينمو عضوياً كما أضفت الميزات إلى المدونة:

```javascript
// src/js/main.js
import hljsRazor from "highlightjs-cshtml-razor";
import mermaid from "mermaid";
import Alpine from 'alpinejs';
import htmx from "htmx.org";
import hljs from "highlight.js";
import EasyMDE from "easymde";
import 'easymde/dist/easymde.min.css';
import flatpickr from "flatpickr";
import 'flatpickr/dist/flatpickr.min.css';

// Expose libraries globally for Razor views
window.Alpine = Alpine;
window.hljs = hljs;
window.htmx = htmx;
window.mermaid = mermaid;
window.EasyMDE = EasyMDE;
window.flatpickr = flatpickr;

// Import custom modules
import { typeahead } from "./typeahead";
import { submitTranslation, viewTranslation } from "./translations";
import { codeeditor } from "./simplemde_editor";
import { globalSetup } from "./global";
import { comments } from "./comments";

// Attach to namespace
window.mostlylucid = window.mostlylucid || {};
window.mostlylucid.typeahead = typeahead;
window.mostlylucid.comments = comments();
window.mostlylucid.translations = {
    submitTranslation: submitTranslation,
    viewTranslation: viewTranslation
};

// Initialise Alpine
Alpine.start();
```

ويتيح هذا النهج عدة فوائد:

1. **نقطة الاستيراد الوحيد**كلّ المعتمدات مُحمّل من خلال ملفّ إدخال واحد
2. **النسخات**محفوظات محدّدة
3. **مُهَزَزَجُ الأشجار**: حزمة ويب تزيل الشفرة غير المستخدمة من المكتبات
4. **ألف - الأنشطة التي يضطلع بها على الصعيد العالمي**● المكتبات العامة المتاحة للاطلاع على آراء المُحَزِّز عن طريق: `window`
5. **نمزززات**: رمز تطبيقي محدد تم تنظيمه في وحدات قابلة للاستيراد

### دعم

داخل `_Layout.cshtml`لا أشير الآن إلا إلى الأصول المجمعة:

```html
<head>
    <link href="/css/dist/main.css" asp-append-version="true" rel="stylesheet" />
</head>
<body>
    <!-- Content -->
    <script src="~/js/dist/main.js" type="module" asp-append-version="true"></script>
</body>
```

ملاحظة `module` إلى أعلم المتصفح ما هو نوع ملفّ هذا `asp-append-version` مساعد بطاقة ASP.Net الذي يُلحق نسخة مهتزة من محتويات الملف بمحتويات الإستفسارات؛ فعّال مُلَفِّت المخبأ عند تغيير الملف.

والمواصفات الوحيدة المتبقية المتبقية من نظام CDN هي:

- **موجود جو جو جوجل**مُتطلبات خارجي CDN لـ الدالة OAuth
- **عدد الكركونات**أيقونة الخط (يمكن تجميعها، ولكن الحد الأدنى من المنفعة)
- **أميت أمي**نص تحليلي من طرف ثالث - استضافة ذاتية حتى أتمكن من رؤية زيارتك وليس جوجل؟

## إنشاء خط التنفيذ المرئي

هذه هي الطريقة التي تتدفق بها عملية البناء بأكملها من ملفات المصدر إلى أصول الإنتاج:

```mermaid
graph TD
    A[Source Files] --> B[npm run build]
    B --> C[Clean Task]
    C --> D[rimraf wwwroot/css/dist wwwroot/js/dist]

    B --> E[Copy Task]
    E --> F[cpx: Copy static CSS files]
    F --> G[wwwroot/css/dist/raleway.css]
    F --> H[wwwroot/css/dist/easymde-overrides.css]

    B --> I[Tailwind Task]
    I --> J[PostCSS Pipeline]
    J --> K[postcss-import]
    K --> L[Tailwind CSS]
    L --> M[Autoprefixer]
    M --> N[cssnano]
    N --> O[wwwroot/css/dist/main.css]

    B --> P[Webpack Task]
    P --> Q[Entry: src/js/main.js]
    Q --> R[Babel Transpilation]
    R --> S[Module Resolution]
    S --> T[Tree Shaking]
    T --> U[Code Splitting]
    U --> V[Terser Minification]
    V --> W[wwwroot/js/dist/main.js]
    V --> X[wwwroot/js/dist/runtime.js]
    V --> Y[wwwroot/js/dist/vendor chunks]

    O --> Z[ASP.NET Core Static Files]
    W --> Z
    X --> Z
    Y --> Z
    G --> Z
    H --> Z

    Z --> AA[Browser]

    style A stroke:#0ea5e9,stroke-width:3px
    style Z stroke:#f59e0b,stroke-width:3px
    style AA stroke:#10b981,stroke-width:3px
```

يبدو معقداً جداً لكن في الواقع هذا ما يفعله ويبباك، إنه يتعامل مع معظم هذا نفسه (بطريقة معقدة لا يحتاجها مثل فيتي لكن).

## مُحِسْسِن الأداء

مرة أخرى، ليس في الواقع نقطة. لكنها تحمل مثل رصاصة مطلقة الآن. يمكنك الحصول على مشاكل مع سحاب فلار 'حمولة Rocket' (التي تقوم بتحميل صفحة الأحداث إلى حد ما) ولكن هذا هو الإضاءة.

## الفوائد المكتسبة من الخبرة المكتسبة في مرحلة التطوير

وبالإضافة إلى الأداء، يؤدي خط أنابيب البناء الحديث إلى تحسين تدفق العمل الإنمائي إلى حد كبير:

### السلامة والسلامة من حيث النوع

يتمكّن التثبيت السليم لوحدة جافاسكرتمبت من خلال حزمة ويب مما يعني:

- **في رمز VS**:: الاستكمال التلقائي للنمائط المستوردة
- **إلى اقفز إلى تعريف**: الانتقال مباشرة إلى رمز مصدر المكتبة
- **داخل تحرير تحرير الوثائق**التعليقات الواردة من المكتبات في التقرير المشترك للورقة المشتركة لتعليقات المكتبات تظهر في المعهد

متى ينكسر البنيان ( مع) `npm run watch`أنا أعرف بشكل غير مباشر، بالإضافة إلى اختبارات وحدة الـ " يون جيه " التي تعني حلقات تغذية مرتدة أكثر إحكاماً.

### المجموعة المستبدلة

أثناء التنمية، `npm run watch` يُمكّن من التغذية المرتدة شبه الفورية:

```bash
npm run watch
```

هذا يُدير حزمة ويب في نمط المراقبة، إعادة بناء فقط الوحدات المُغيّرة:

- **زمن إعادة إعادة التدوير النمطي**الأمم المتحدة: 200-4000 متر
- **متصفح المتصفح**: استخدام المتصفح أو ما يماثله من أدوات
- **تطبيق الحالة**: HMR يُبقي على حالة الطلب أثناء التحديثات (حيثما يدعم)

### إدارة

قفل ملف قفل NPm (`package-lock.json`(ب) كفالة الاتساق بين البيئات:

```bash
npm ci  # Clean install from lock file
```

وهذا يضمن نفس أشكال الاعتماد في التنمية، وفي المؤتمر الدولي المعني بالبيئة والتنمية، وفي بيئات الإنتاج.

يُشار إلى هذه باسم "الركن" في عالم الـ JS، حيث يتم وضع التبعية في حجر. يمكنك القيام بذلك بدون قفل حزمة ولكن بعد ذلك تعتمد على المؤلفين Pacakge؛ غالباً HUNDREDS من أطقم مختلفة من الناس لا تفسد بعض الإصدارات البسيطة.

### 

الـ مُنظّم npm مُوَزّع المهام:

```bash
npm run dev      # One-time development build
npm run watch    # Continuous development builds
npm run build    # Production build with optimisations
npm run clean    # Remove build artefacts
```

## الأنماط والحلول المشتركة

### المكتبات المُدْمِلَة على الصعيد العالمي

وتحتاج بعض المكتبات إلى تعرض عالمي لاستخدامها في مشاهدات المزال أو النصوص الخطية:

```javascript
// Make library available globally
import Alpine from 'alpinejs';
window.Alpine = Alpine;
Alpine.start();
```

ثم في `.cshtml` 

```html
<div x-data="{ open: false }">
    <!-- Alpine.js works because it's on window -->
</div>
```

### جاري المناولة CSSS من

وتشمل بعض المكتبات الدائرة التي تحتاج إلى استيراد:

```javascript
import EasyMDE from "easymde";
import 'easymde/dist/easymde.min.css';  // Imported CSS is processed by Webpack
```

مجموعة ويب (Webcus) `css-loader` وقد عقد مؤتمراً بشأن `style-loader` ويتعامل مع هذه الواردات:

- يتم استخراج SSSS أثناء البناء
- حُقق في الصفحة عن طريق `<style>` أو مُفكِض
- مُسبقاً ومُؤثماً

### تحميل حمل ثقيل محفوظات

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

```javascript
// Only load Mermaid when needed
async function initMermaid() {
    const mermaid = await import('mermaid');
    mermaid.default.initialize({ startOnLoad: true });
}

// Call when needed
if (document.querySelector('.mermaid')) {
    initMermaid();
}
```

وتُحمَّل تلقائياً على مجموعة ويب الواردات الديناميكية المرمزة والمجزأة إلى قطع منفصلة، عند الطلب.

### التعامل مع الوحدات المشتركة JJS

وتستخدم بعض المكتبات الأقدم عهدا نظام UniversityJS بدلا من نماذج SES6:

```javascript
// CommonJS require syntax
const hljs = require('highlight.js');

// Or use dynamic import
import('highlight.js').then(hljs => {
    // Use hljs
});
```

ويب عواصف كلا نظامي الوحدات بشفافية، تحويل المشترك JS إلى ES6 عند الحاجة.

## 

### خرائط المصدر المفقودة في التنمية

(كُرِج) `devtool` هذا `webpack.config.js`:

```javascript
devtool: isProduction ? false : 'eval-source-map',
```

وهذا يولّد خرائط مرجعية قيد التطوير لتيسير عملية التصحيح.

### CSS S CS S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S C قضايا التالود

إذا قام تايل ويند بإزالة الفئات التي تستخدمها، فتحقق من `content` & لام :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

```javascript
// tailwind.config.js
module.exports = {
    content: [
        './Views/**/*.cshtml',
        './Components/**/*.cshtml',
        // Add any other paths where classes are used
    ],
}
```

الخيار البديل، استخدام `safelist` بالنسبة للطبقات الدينامية:

```javascript
safelist: [
    'bg-blue-500',
    'text-red-600',
    {
        pattern: /bg-(red|green|blue)-(400|500|600)/,
    }
]
```

### لا

إذا كان Webpack لا يستطيع حل وحدة، تحقق:

1. **المسار**المصدر النسبي لبدء العمل مع: `./` أو `../`
2. **تمديدات ملف**:: إضافة `.mjs` ثالثاً - `resolve.extensions` (ج) في حاجة
3. **&**الاستخدام: الاستخدام: `resolve.alias` للمسارات المعقدة

```javascript
resolve: {
    extensions: ['.js', '.mjs', '.json'],
    alias: {
        '@components': path.resolve(__dirname, 'src/js/components/'),
    }
}
```

### قضايا الأداء

إذا يُثبّت ببطئ:

1. ***** مخبأ علكة ويب (ويبك) إلى حد كبير
2. **حُدِّل نطاق**: استثناء المزيد من الأدلة من عملية التجهيز
3. **أدوات الارتقاء إلى مستوى أعلى**: نسختان جديدتان من علب ويب وبابل أسرع
4. **الدالة**الاستخدام: الاستخدام: `thread-loader` للتنقال المتعدد الأبعاد

```javascript
// Enable Webpack caching
cache: {
    type: 'filesystem',
},
```

## الدروس المستفادة من الطريق الصعب

اسمحوا لي أن أشارككم بعض الأخطاء التي ارتكبتها خلال هذه الرحلة، حتى تتمكن من تجنبها:

### الخطأ # 1: محاولة وضع كل شيء في وقت واحد

محاولتي الأولى تضمنت تمزيق جميع إشارات CDN في واحد ذهاب ومحاولة تجميع كل شيء من خلال حزمة ويب. تعطل البناء بشكل مذهل. تعلمت أن الهجرة التدريجية هي

### الخطأ # 2: لم يُفِف أنواع الوحدة

لقد أضعت ساعات في محاولة لمعرفة لماذا بعض المكتبات لا تقوم بالتحميل`require()`(أ) واقتطاعات`import`) بدون فهم كيفية تعاملات (ويب باك) معها تؤدي إلى أخطاء مشفرة. `experiments: { outputModule: true }` التشكيل لم يكن في إعدادي الأولي، ولم أستطع أن أعرف لماذا وحداتي لم تكن تحميل.

جاء الحل من قراءة من خلال قضايا جيت هوب على مستودع ويب باك في منتصف الليل.

### الخطأ # 3: تقسيم شفّز أكثر من عدواني

في نقطة ما، كان تشكيلي يولّد قطع من كل شيء. `minSize: 10000` (10KB) الذي يعني أن حزمة ويب كانت تنشئ ملفات منفصلة لأداء وظائف صغيرة جداً. تم تصحيحها أكثر من اللازم وذهبت إلى 2MB. أصبح تحميل الصفحة شلالاً لـ 50+ جزء صغير من الطلبات أو معلقة في انتظار قطع ضخمة لتنزيلها. علمت أن تقسيم الرموز جيد، ولكن تحتاج إلى عتبات معقولة. لهذا السبب يستخدم تشكيلي الحالي. `minSize: 20000` (20KB).

### الخطأ # 4: نسيان `.mjs` المُذْكِر مِم مِم مِ مِ مِ مِ مِ مِ مِ مِ مِ مِ مِ مِ مِ مِ مِ م م م م م م م م م م م م مِ م م م م م م م م م م م م م م م م م م م م م م م م م مِ م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م

بعض حزم Nnppm توزّع ES6 مع `.mjs` حزمة ويب لن تحل هذه بالفشل لقد قضيت وقتاً طويلاً مُحرجاً في تصحيح لماذا `@mostlring I needed to add `مَجْسِس`to`"تُقرّر." "تُقرّر." "تُقرّر." "تُقرّر. تُقرّر."

إصلاح بسيط بمجرد أن تعرف ذلك، لكن معرفة ذلك؟

### الخطأ # # 5: لا يُلف ليس Cch CchGQ

أخذت عمليات البناء المبكرة 30-60 ثانية لأنني لم أكن قد تمكنت من تخزين نظام ملفات ويباكس. بمجرد أن أضفت كاشنغ، إعادة بناء الأوقات انخفضت إلى 2-3 ثانية. هذا كان تغيير لعبة لتدفق العمل الإنمائي ولكن استغرق مني أسابيع للاكتشاف.

### الخطأ # 6: الفئات التي استخدمتها في الواقع

تجربتي المفضلة في التصحيح: فصول CSS التي عملت بشكل جيد في التنمية فجأة اختفت في الإنتاج. التيلويند كانت تطهرهم لأنهم تم توليدهم بشكل ديناميكي في جافاسكربت. كان الحل هو `safelist` لكن فقط بعد أن أضعت ساعات أتسائل إن كنت سأجن
أيضاً (على سبيل المثال في `mostlylucid.pagingtaghelper` project) استخدم 'كتل معدنية' حيث أقوم بتبسيط التغييرات في filewind من خلال مجرد وجود كتلة مخفية.

```html

<!--
    Preserve Tailwind & DaisyUI classes used in embedded pager views.
    Without this, TailwindCSS tree-shaking removes classes from the embedded library views.
-->
<span class="hidden
    btn btn-sm btn-active btn-disabled join join-item badge badge-sm select select-bordered select-sm label label-text
    px-3 py-2 py-1 text-sm font-medium border rounded whitespace-nowrap cursor-not-allowed
    text-gray-700 text-gray-600 text-gray-400 text-white bg-white bg-gray-100 bg-blue-600 border-gray-300 border-blue-600
    hover:bg-gray-50 hover:bg-blue-700
    dark:bg-gray-800 dark:bg-gray-700 dark:bg-blue-500 dark:text-gray-300 dark:text-gray-500 dark:text-gray-200
    dark:border-gray-600 dark:border-blue-500 dark:hover:bg-gray-700 dark:hover:bg-blue-600">
</span>

```

عندما يقوم تايلويد ببناءه فإنه يقوم بمسح ملفات cshtml للفصول ، إذا لم يرها فإنه يزيلها. لذلك فإن وجود هذا الخانة المخفية يضمن أنها ترى الفصول التي أحتاجها حتى لو كانت فقط مستخدمة في مشاهدات المكتبة المضمنة. الـ JS المطورون عادة ما يقومون بمسح ملفات JS/TS للسماح باستخدام أساليب CSS التي من الممكن أن يخطئها تايل ويند بطريقة أخرى.

```json

module.exports = {
content: ["./Views/**/*.cshtml", "./EmailSubscription/**/*.cshtml"],
-//^^^^ This is what Tailwing knows where to look for classes duritng the tree Shake. 
+//^^^^ This is what Tailwind knows where to look for classes during the tree-shake.
  
  ...

future: {...}

}

```

كما ذُكِرَ، نهج 'Tailwind conception' هو لإضافة الفصول إلى قائمة آمنة... لكنّي رجل ASP.net، لذا فاز HTML.

### ما حصلت عليه الحق : الثبات

الشيء الوحيد الذي فعلته كان رفض الاستسلام. كل رسالة خطأ كانت فرصة تعلم. كل مبنى مكسور علمني شيئا جديدا عن كيفية عمل هذه الأدوات. كان لدي رؤية واضحة

## ثالثاً - استنتاج

الانتقال من المعتمدات المعتمدة على CDN إلى خط أنابيب حديث كان تحويلا لهذه المدونة، لكنه لم يكن سهلا. التحسينات في الأداء كبيرة، تجربة المطور أفضل بكثير، ولدي سيطرة أكبر بكثير على التكميل - ولكن استغرق الأمر شهورا من التجربة والخطأ للوصول إلى هنا.

(أ) الخيارات الرئيسية:

1. **التراكم الذي يقلّص حجم الحمولة**: هدم الأشجار وتزييفها: إلغاء الرمز غير المستخدم
2. **يُحسِّن الأداء**المتصفحات تحمل فقط ما هو مطلوب
3. **دعم دعم أدوات تمكين تحديث جافScrbr**: BBabel spepspectipation دعم أحدث الميزات اللغوية مع الحفاظ على توافق المتصفح
4. **(أ) اختيار خط أنابيب POS SS SS**:Tailwind's Tailwind's JIT تجميع و csssnanano تسليم صحائف اساليب صغيرة
5. **التكامل مع الشبكة هو سلسة**: أهداف MSSBWild التشغيل التلقائي لواجهة الإنشاءات
6. **الثبات يتغلب على الكمال**لا تحتاج إلى أن تكون خبيراً في الواجهة الأمامية - تحتاج فقط إلى رؤية واضحة والرغبة في تكرار

لـ ASP.net الأساسي المطورون عادة إلى بسيط CDN بما في ذلك، هذا سيشعر بالإثارة في البداية. هذا طبيعي. شعرت بنفس الطريقة. ابدأ بالصغير ، هاجر تبعية واحدة في وقت واحد ، ولا تخاف من كسر الأشياء في التطوير. سوف تتعلم من تصحيح البنيات المكسورة أكثر مما سوف تتعلم من قراءة الوثائق.

التشكيلة التي شاركتها هنا تمثل أشهر من التكرار. رحلتك ستكون مختلفة، وهذا جيد. استخدم هذه كنقطة بداية، لا انقلاب. تكيفها مع احتياجاتك، تجربتك، ولا تثبط من الفشل  إنها جزء من العملية.

**هل هذه مدونه معماريه؟** بالطبع. هل يمكنني الاستمرار في استخدام الأقراص المدمجة وقضاء وقتي في أمور أخرى؟ بالطبع. لكنني لم أكن لأتعلم نصف ما تعلمته، ولم يكن لي هذا المقال لأشارككم به. في بعض الأحيان الهندسة المفرطة لا تتعلق بالوجهة - بل بما تتعلمه على طول الطريق وتكون قادراً على تعليم الآخرين من تلك التجربة.

إذا كنت لا تزال تعتمد على CDNs من أجل الاعتمادات الأمامية الخاصة بك، وأنا أشجعك على إعطاء التأطير تجربة. ابدأ مع مكتبة واحدة. انظر كيف تشعر. قم من هناك. النظام البيئي قد نضج بشكل ملحوظ، وبينما منحنى التعلم حقيقي، فإن العائد يستحق ذلك.

## & ما

- [DSSWS](https://webpack.js.org/)
- [Tailwind CSS: الاستخدام الأمثل للإنتاج](https://tailwindcss.com/docs/optimizing-for-production)
- [بِثَبْبِل : الدليل المطبوع](https://babeljs.io/docs/en/usage)
- [الوثائق الصادرة من مركز](https://postcss.org/)
- [مقالتي السابقة: Tailwind for ASP.net المطورون الأساسيون](/blog/tailwind)