# Сучасне збирання трубопроводів: від CDN до bundling

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

<!-- category -- ASP.NET, Tailwind, Webpack, Frontend -->
## Вступ

Протягом минулого року я спіткнувся, експериментував, ламав речі, і поступово модернізував трубопровод для побудови цього блогу. що я хотів досягти.

Я не гуру-оболонка і не майстер веб-пакування. Я розробник .NET, який розчарувався через обмеження заснованих на CDN залежностей і вирішив, що має бути кращий спосіб. `<script>` Теґи, що вказують на unpkg на сучасний трубопровод для бундінгів, насправді працюють.

Я зробив багато помилок на шляху (які я буду документувати), витратив години для усування таємних помилок Webpack, і, ймовірно, знову встановлено `node_modules` Але наполегливість була винагороджена, і я навчився багато чого завдяки твердій рішучості отримати це право.

Якщо ви - розробник .NET дивлячись на файли конфігурації Webpack, цікаво, що на землі ви отримали себе в цю статтю для вас.

Я старий нутер в інтернеті, на початку першого сайту, який я збудував за гроші (це не був сервер порно в Perl... Іншого разу) був сайт "Сніжні умови" з автоматизованої телефонної лінії.

У ті часи занепокоєння були цілком різними. JavaScript практично не було вказано. На мою думку, навіть диски були RARE (в ті дні IE був `div` і Netscape `layer`) і ви будете LUCKY, якщо ваші користувачі мають з' єднання 56K. Отже, я навчився оптимізувати (навіть такі елементи, як плиткове тло зображень для пунктів меню) тощо...

Пізніше у Microsoft I навіть працював над автоматичним інструментом спрайта зображень у веб- формах - це було чудово: він дістав маленькі зображення зі сторінки, створив CSS і зображення спрайта автоматично. Але, на жаль, як і мій час у Microsoft, це не мало бути.

Одним словом, це одержимість, що я переніс усю свою кар'єру.

**Чесність.**, CDN не є природним злом.

**Повне виявлення**: цей блог спеціально переобладнано. *потреба* Але така складність. і, істотно, дала мені щось справжнє для написання і навчання. і іноді найкращий спосіб навчитися - це побудувати щось трохи смішне і задокументувати подорож.

[TOC]

## Проблема з CDN

Спочатку мій підхід був прямим:

```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">
```

У нього є кілька недоліків, які дедалі більше розчаровували:

- **Проблеми надійності**Я бачив, як у unpkg і jselivr були перебої.
- **Непередбачена поведінка завантаження**: відповідь CDN може бути різною, іноді швидко, іноді повільною, іноді вони вводять у умови раси, де бібліотеки завантажуються у неправильному порядку.
- **Проблеми з розкладом часу**: Ви не можете контролювати завантаження скриптів відносно один одного. Це призвело до нескінченного головного болю з ініціалізації бібліотеки
- **Декілька запитів HTTP**: Для кожної бібліотеки потрібен окремий запит, навіть з HTTP/ 2
- **Не кататися деревами**: Ви завантажуєте цілу бібліотеку, навіть якщо ви використовуєте лише частину її
- **Відтінок версії**: CDN може використовувати різні версії, якщо ви їх явно не приклеюєте, і навіть закріплені версії можуть поводити себе по-різному у провайдерах CDN
- **Непевність щодо часу збирання**: Неможливо спіймати несумісність, аж поки не почнеться час, коли користувач повідомляє про це.
- **Обмежена оптимізація**: Не можна обмінювати через межі або вилучати мертві коди
- **Автономний розвиток**: Вимагає зв'язку з інтернетом, що дратує під час роботи над поїздами або літаками.

Отже, я хотів контролювати, що саме використовує мій сайт і який був потрібен для роботи, я хотів зв'язати тільки те, що мені потрібно, забезпечити надійне завантаження і оптимізувати швидкодію.

Для багатьох проектів, особливо малих, або прототипів, CDN залишається абсолютно розсудливим вибором.

## Сучасний захоплюючий приступ

Мої поточні налаштування містять всі залежності JavaScript за допомогою Webpack і оброблюють CSS за допомогою PostCSS.

### Чому Webpack замість Vite?

Перед тим як зануритися в технічні деталі, я маю звернутися до слона у кімнаті: чому я використовую веб-папер, коли Vite, Rollup, esbuild та інші сучасні снопи?

Чесна відповідь проста: **Я уже знала Веб-пап**.

Коли я вирішив модернізувати трубопровод цього блогу, я вже зіткнувся з поворотною точкою навчання, що вказує на дерево-захід, розподіл коду, системи модуля, трубопроводи PostCSS, і як інтегрувати все це з ядром ASP.NET.

**Зазвичай, це добра практика під час роботи над проектами; обмежте " нове " до " керування ." Незабаром простіше обмежити невизначеність.**

Додавати "навчити абсолютно новий інструмент для збирання" на додаток до цього, здавалося б непотрібним, веб-пакун працює, він зрілий, він має чудову документацію і масивну екосистему. головне, я міг би зосередитися на своїй енергії навчання на *концепції* сучасного бундлінгу, а не ідеосинхронності окремого інструмента.

**Віте швидший?** Безумовно. Сервер розробки Vite з природними модулями ES і утилітою для esbuild значно швидший за веб- пакет. Для великих проектів з сотнями модулів різниця значною.

**Чи варто використовувати Vite для нового проекту?** Можливо, так. Якщо ви починаєте все заново і не маєте існуючих знань у Webpack, Vite, ймовірно, є кращим вибором. Це швидше, простіше налаштувати і представляє сучасний підхід до роботи з графічною оболонкою.

**Мне жаль, что я использовал Веб-папку?** Ні, це привело мене туди, де мені потрібно було бути. і оптимізація передана будь-якому інструменту для збирання. ми говоримо мілісекунди в час відбудови.

Це ключовий аспект того, як я будую речі; почніть з того, що є EASY і збудуйте з цієї бази.

І ширший урок полягає в тому, що **поступ працює з максималізмом**Я міг би витрачати кілька тижнів на дослідження "найкращого" квитка, порівнюючи п'ятизначні знаки, читати статті для порівняння і агонізувати через це рішення.

У будь-якому разі, цей блог має сучасний оптимізований трубопровод, який працює повторно і це те, що важливо.

### Структура пакунка

The [`package.json`](https://github.com/scottgal/mostlylucidweb/blob/main/Mostlylucid/package.json) зараз підтримує дві окремі групи залежностей. але nppm набагато менш дружелюбний при додаванні pacakges.

```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"
  }
}
```

**Залежності** є бібліотеками, потрібні під час запуску (Alpine.js, HTMX, EasyMDE тощо) **devDependance** є інструментами для збирання (Webpack, Babel, PostCSS- процесори тощо).

### Зібрати скрипти

The `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, які не потребують обробки (це або " imports ") для особливої мети, як завантаження мого шрифту funky Raleway або адаптивних параметрів, на зразок покращення тем.
- **tw:*** Завдання: Процеси Railwind CSS через PostCSS
- **js:*** Завдання: Bundles JavaScript через webpack
- **dev/watch/ build**: Окуштувати весь трубопровод

The `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..." Ви навіть можете сказати "запустити" лише під час зневадження тощо... але я вважаю, що все заплутано. Я волію запустити вручну. `npm run watch` робить це, коли будь- який файл css / js змінює його автоматичний запуск збирання.

ЗАУВАЖЕННЯ: у світі JS майже використовують розвантажувальну програму dev, яка досить підступна і змушує вас ненавидіти слабку спробу ядра ASP.NET.

## Налаштування Webpack глубоке Діво

Конфігурація Webpack - це те, де відбувається магія, і де я провів більшу частину свого часу знімання проблем.

Ось поточне налаштування (з [`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',
    },
}
```

За допомогою цього налаштування можна автоматично розділити ваш код на менші шматки:

- **Шматки: "всі"**: Аналізує синхронний і асинхронний імпорт
- **minSize: 20000**: створювати лише шматки для модулів, більших за 20 КБ
- **maxSize: 100000**: Спроби розділити шматки більші за 100 КБ
- **runtimeChunk**: Видобути час запуску 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,
                }],
            ],
        },
    },
}
```

Вавельські трансферти з JavaScript підтримують старіші переглядачі:

- **цілі**: Підтримує браузери з > 0. 25% ринкового спільного ресурсу, який все ще підтримується
- **модулі: false**: Містить модулі ES6 для Webpack дерева- shaking
- **useBuiltIns: 'usage'**: Автоматично додавати поля лише для можливостей, які ви використовуєте
- **corejs: 3**: Використовує версію core- j 3 для поліфилів

Це означає, що я можу писати сучасний JavaScript (синхронізатор/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,
    }),
] : []
```

Терсер наполегливо знижує кількість виробу:

- **pop_ Console**: Вилучає всі `console.log()` команди
- **прохід: 3**: Запустити стискання три рази для максимального зменшення розміру
- **верхній рівень: true**: Назви змінних верхнього рівня Mangles
- **sure_ funccs**: Вилучає специфічні консольні методи, навіть якщо їх прив' язано до змінних
- **коментарі: ні**: Скидає всі коментарі з виводу

У цьому блозі це, зазвичай, зменшує розмір у вузлі JavaScript на 60- 70% порівняно з незміненим кодом.

## ПостКС трубна лінія для зворотнього вітру

CSS Tailwind оброблюється за допомогою PostCSS з декількома додатками. Це налаштування (з [`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. **postcs-import**: Вирішує `@import` заяви у файлах CSS
2. **tailcswinds**: Обробки директиви Thailshot і створення класів допоміжних засобів
3. **autoprefixer**: Додає префікси виробників для сумісності навігатора
4. **csnano**: Змінює остаточний вивід CSS

### Налаштування " Хвилища "

The [`tailwind.config.js`](https://github.com/scottgal/mostlylucidweb/blob/main/Mostlylucid/tailwind.config.js) file визначає, де слід шукати назви класів tailwind. Отримання назви `content` Шляхи праворуч є критичними} mis - шлях і Tailwind не буде створювати класи для цих файлів:

```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` файли для назв класів (зокрема перегляди Razor і шаблони електронної пошти)
- **securelist**: Завжди включати ці класи, навіть якщо їх не знайдено у скануванні
- **темний модер: "клас"**: Вмикає перемикання темного режиму на основі класів
- **тема. extend**: Додає нетипові кольори і значення інтервалу
- **додатки**: включити додатки Tailwind і бібліотеку компонентів DaisUI

Bailwind сканує ці файли під час збирання, видобування лише класів програм, якими ви користуєтеся. Ось чому кінцевий файл CSS набагато менший за повну бібліотеку Tailwin.

## Точка запису JavaScript

The [`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. **Одна точка імпортування**: Всі залежності, завантажені в одному файліXLIFF mark type
2. **Явні версії**: Pack.json заблоковує специфічні версії
3. **Торгівля деревами**: Webpack вилучає невикористаний код з бібліотек
4. **Загальна експозиція**: Бібліотеки доступні для переглядів Razor через `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` Щоб надати навігатору знати, який тип файла JS це & `asp-append-version` акуратний допоміжний засіб для роботи з теґами ASP. NET, за допомогою якого можна додати хешовану версію вмісту файла до рядка діалогу; ефективно кешувати під час зміни файла.

Єдине, що залишається залежностями CDN:

- **Підписати в Google**: Потрібний зовнішній CDN для функціональних можливостей OAuth
- **Ікони з рамками**: Шрифт піктограм (можна бути прив' язаним, але мінімальною перевагою)
- **Аналітичні аналітики у Умамі**: По-третє, аналітичний сценарій самого себе, щоб я побачив вас, а не Google?

## Створити візуалізацію трубкової лінії

Ось як весь процес збирання проходить від початкових файлів до виробничих активів:

```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
```

Він виглядає досить складним, але насправді це те, що робить WebPack, він керує більшістю з них (складним способом, в який смаки ваміту не потрібні, але...)

## Покращення швидкодії

Знову ж таки, не POINT. Але програма завантажуватиме його як абсолютну кульку. Ви все ще можете мати проблеми з використанням команди Harflare' 'Rocket Backer' (яка завантажує події на сторінці mangles), але має значення TIGE.

## Користь від досвіду розробника

Крім того, сучасні трубопроводи для збирання значно покращують процес розробки:

### Захисність до друку і нерозпізнаність

Bundling за допомогою Webpack вмикає належну роздільну здатність модулів JavaScript, що означає:

- **IntellySense in VS Code**: Автозавершення імпортованих модулів
- **Перейти до визначення**: перейти безпосередньо до початкових кодів бібліотеки
- **Вбудована документація**: У IDE з'являються коментарі JSDoc з бібліотек

КОЛИ будівництво розпадається (з `npm run watch`) Я ЗНАЮ INSTANTANT, а також (кількість, але зростаюча) тести JS, це означає, що зворотний зв'язок стає тіснішим.

### Заміна модуля " гарячий "

Під час розробки `npm run watch` вмикає майже будь- який зворотній зв' язок:

```bash
npm run watch
```

Цей пункт запускає webpack у режимі спостереження, з' єднання лише змінених модулів:

- **Типовий час поновлення**: 200- 400мс
- **Автовідновлення навігатора**: Використання синхронізації навігатора або подібного інструмента
- **Зберігся стан програми**: HMR підтримує стан програми під час оновлень (де підтримується)

### Керування залежностями

Файл блокування npm (`package-lock.json`) забезпечує послідовне збирання у середовищах:

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

Це гарантує ті самі версії залежностей у розробці, CI/CD та середовищах виробництва.

Їх називають " minning " у світі JS, де залежності встановлюються у камені. Ви можете обійтися без пакування, але потім ви залежитьте від авторів пакування; часто HUNDRDDES данних tags не перемішуючи деяких дрібних випусків.

### Зібрати скрипти

Організовані скрипти 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
```

## Загальні приклади й рішення

### Викриття пошкоджених бібліотек по цілому світі

Деякі бібліотеки потребують глобального експозиції у переглядах Razor або вбудованих скриптах:

```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>
```

### Обробка CSS з модулів JavaScript

Деякі бібліотеки включають CSS, який потребує імпортування:

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

Webpack's `css-loader` і `style-loader` працює з цими імпортами:

- CSS видобувається під час збирання
- Введення на сторінку через `<style>` мітки або окремі файли CSS
- Автоматично префікси і мінізовані

### Широке завантаження важких бібліотек

Для бібліотек, які потрібні лише на окремих сторінках, скористайтеся динамічним імпортом:

```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();
}
```

У Webpack динамічні імпорти з кодами на окремі шматки, завантажені за допомогою команди in- demand.

### Як поводитися з модулями SmonJS

Деякі старіші бібліотеки використовують CommonJS замість модулів ES6:

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

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

Webpack керує обома модульними системами прозоро, перетворюючи CommonJS на ES6 за потреби.

## Вирішення загальних питань

### У розробці не вистачає вихідних карт

Забезпечення `devtool` налаштовано у `webpack.config.js`:

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

Ця програма створює карти джерела у розробці для спрощення усування вад.

### Клас CSS, що очищує проблеми з попутним вітром

Якщо Хитхий вітер усуває класи, які ви використовуєте, позначте `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. **Увімкнути кешування**: Кеш Webpack значно пришвидшує перебудову.
2. **Зменшити Вавилон**: Виключити більше каталогів з обробки Babel
3. **Додаткові інструменти**: Новіші версії Webpack і Babel швидші
4. **Паралельні збирання**: Використання `thread-loader` для багатопоширеного трансподілення

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

## Уроки з важкого життя

Дозвольте мені поділитись деякими помилками, які я зробив під час цієї подорожі, щоб ви могли уникнути їх:

### Помилка # 1: спроба зробити все одночасно

Моя перша спроба полягала в тому, щоб видобути всі посилання CDN одним ходом і спробувати зв'язати все через Веб-пакування. Поламана споруда приголомшливо. Я дізнався, що додаткова міграція - це ваш друг, дві бібліотеки за один раз, тест ретельно, а потім перейти до наступного.

### Помилка # 2: Не розуміння типів модулів

Я змарнував години, намагаючись зрозуміти, чому деякі бібліотеки не завантажуються.`require()`) і модулі ES6 (`import`) не розуміючи, як Webpack керує ними, можна дійти до таємних помилок. `experiments: { outputModule: true }` конфігурації не було у моїх початкових налаштуваннях, і я не міг зрозуміти, чому мої модулі не завантажуються.

Щоб розв'язати цю проблему, о півночі було прочитано у сховищі веб-пакування через " GitHub ."

### Помилка # 3: Агресивне розділення коду

В один момент моя конфігурація генерувала шматки для всього. `minSize: 10000` (10KB) це означало, що Webpack створює окремі файли для невеличких функціональних можливостей програми. Перенавантаження і перехід на 2MB... Пейдж стало водоспадом з 50+ крихітних запитів до шматків або очікування на те, що буде звантажено величезні шматки. Я дізнався, що розбиття коду є корисним, але вам потрібні розумні пороги. Ось чому моє поточне налаштування використовує `minSize: 20000` (20 КБ).

### Помилка # 4: Забуваючи про `.mjs` Суфікс назви

Деякі пакунки npm розповсюджують модулі ES6 за допомогою `.mjs` webpack не вирішить їх типово. `@mostlring I needed to add `. mjs`to`console.exposs}

Простая исправа, когда ты это знаешь, но это заняло время.

### Помилка # 5: не збирання Caching Webpack

Перші збирання зайняли 30- 60 секунд, оскільки я не увімкнув кеш файлової системи Webpack. Після додавання кешу час збирання зменшився до 2- 3 секунд. Це був інструмент зміни гри для роботи з розробки, але мені знадобилось кілька тижнів часу на відкриття.

### Помилка # 6: Порожні класи, якими я насправді користувався

Мій улюблений досвід у усування вад: класи CSS, які добре працювали у розробці, раптово зникли у процесі розробки. Вихровий вітер витер їх, оскільки вони були створені динамічно на JavaScript. Розв' язанням було `safelist` Устройство, но только после того, как я напугал часы, удивляясь, не сошел с ума.
IMIP (наприклад, у розділі) `mostlylucid.pagingtaghelper` project) скористатися " dummy blocks," де я спрощую зміни налаштування Tailwindwind, просто маючи прихований блок.

```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>

```

Під час збирання Tailwind програма шукатиме файли cshtml для класів, якщо програма не побачить їх, і вилучає їх. Отже, якщо цей прихований блок забезпечить перегляд класів, які потрібні програмі, навіть якщо ці файли використовуються лише у вбудованих переглядах бібліотек. Розробники JS зазвичай сканують файли JS/TS для того, щоб дозволити використання стилів CSS, у яких Bailwindwind було б пропущено.

```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: {...}

}

```

Як ми вже згадували, підходом до "Захований вітер" є додавання класів до безпечного списку... але я хлопець ASP.NET, тому HTML виграв.

### Що я зрозумів правильно: наполегливість

Кожне повідомлення про помилку було навчальною можливістю, і кожна зламана будівля навчила мене чогось нового про те, як працюють ці інструменти.

## Висновки

Міграція від залежностей, заснованих на CDN, до сучасного трубопроводу була трансформацією для цього блогу, але це не було легко. Покращення вистави істотно, досвід розробника значно кращий, і я маю набагато кращий контроль над оптимізацією, але це зайняло місяці випробувань і помилок, щоб дістатися сюди.

Захоплення ключів:

1. **Бордюр зменшує розмір вантажу.**:ing деревами і minification вилучає невикористаний код
2. **Розподіл коду покращує швидкодію**: Переглядачі завантажують лише те, що потрібно
3. **Інструменти для збирання увімкнути сучасний JavaScript**: Трансляція в Бабелі підтримує останні особливості мови під час підтримування сумісності навігатора.
4. **PostCSS трубопровід оптимізує CSS**: Компілятор JIT Effect і csnano доставляють маленькі таблиці стилів
5. **Інтеграція за допомогою .NET безперешкодно**: програма для автоматичного запуску оболонки MSBuild
6. **Наполегливість б'є досконалість.**Вам не обов'язково бути конкурентами, вам просто потрібен чіткий зір і готовність до переосмислення.

Для розробників ядра ASP. NET, які звикли до простих CDN, це спочатку буде дуже пригнічувати. Це нормально. Я так само почувався. Почніть з маленької, мігруйте одну залежність за раз і не бійтеся розбивати речі у розробці. Ви навчитеся більше з усування пошкоджених структур, ніж ви коли- небудь зможете прочитати документацію.

Налаштування, які я тут поділив, представляє місяці ітерацій. Ваша подорож буде іншою, і це добре. Використовуйте це як початкову точку, а не євангелію. Пристосовуйте її до ваших потреб, експерименту, і не знеохочуйтеся через тяжку, це частина процесу.

**Цей блог переобладнано?** Але я б не вивчив половину, і я б не мав цієї статті, щоб поділитися з вами. Іноді перебудування - це не те, що ви вивчаєте на шляху, а можливість навчати інших з цього досвіду.

Якщо ви все ще покладаєтесь на CDN для залежностей вашої оболонки, я раджу вам спробувати пов' язати її з програмою. Почніть з однієї бібліотеки. Подивіться, як це почувається. Зановте там. Екосистема значно дозріла, і, поки крива навчання є реальною, винагорода варта того.

## Подальше читання

- [Документація з webpackName](https://webpack.js.org/)
- [CSS з зворотним віхом: оптимізація для виробництва](https://tailwindcss.com/docs/optimizing-for-production)
- [Бабель: використання довідника](https://babeljs.io/docs/en/usage)
- [Документація PostCSS](https://postcss.org/)
- [Моя попередня стаття: Tailwind для розробників ядра ASP. NET](/blog/tailwind)