Сучасне збирання трубопроводів: від CDN до bundling (Українська (Ukrainian))

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

Tuesday, 11 November 2025

//

25 minute read

Вступ

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

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

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

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

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

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

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

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

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

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

Проблема з CDN

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

<!-- 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 зараз підтримує дві окремі групи залежностей. але nppm набагато менш дружелюбний при додаванні pacakges.

{
  "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 Значно розвинулись скрипти:

{
  "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 (вам потрібно переконатися, що його вимкнено тощо).

<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) детальним поясненням того, що робить кожна частина і чому вона існує:

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,
        }
    };
};

Розділи налаштування ключів роз' єднані

Розділення і розбиття коду

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 - Автоматично розділяти шматки постачальника
  • Додаткові шматки для маршрутів з кодуванням або лінивих модулів

Поспоріднення вавілонян

{
    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, необов' язкове ланцюгове, нульове вугілля) під час підтримки широкого переглядача.

Мінифікація виробництва

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) милосердно можна порівняти до Webpack:

// 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 file визначає, де слід шукати назви класів tailwind. Отримання назви content Шляхи праворуч є критичними} mis - шлях і Tailwind не буде створювати класи для цих файлів:

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 файл, у якому всі файли з' єднуються разом. Цей файл імпортує і ініціалізує всі залежності, і він стає органічним, оскільки я додав до блогу такі можливості:

// 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Тепер я маю на увазі лише прив'язки активів:

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

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

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

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 вмикає майже будь- який зворотній зв' язок:

npm run watch

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

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

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

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

npm ci  # Clean install from lock file

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

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

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

Організовані скрипти npm спрощують виконання типових завдань:

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 або вбудованих скриптах:

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

Потім в .cshtml файлів:

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

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

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

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
  • Автоматично префікси і мінізовані

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

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

// 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:

// 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:

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

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

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

Якщо Хитхий вітер усуває класи, які ви використовуєте, позначте content налаштування:

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

Крім того, використовувати safelist для динамічних класів:

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

Модуль не знайдено помилок

Якщо Webpack не вдалося визначити модуль, перевірте:

  1. Виправити шлях до імпортування: Відносні шляхи починаються з ./ або ../
  2. Суфікси назв файлів: Додати .mjs до resolve.extensions якщо потрібно
  3. Псевдоніми: Використання resolve.alias для складних шляхів
resolve: {
    extensions: ['.js', '.mjs', '.json'],
    alias: {
        '@components': path.resolve(__dirname, 'src/js/components/'),
    }
}

Спірні питання щодо швидкодії збирання

Якщо збирання стане повільним:

  1. Увімкнути кешування: Кеш Webpack значно пришвидшує перебудову.
  2. Зменшити Вавилон: Виключити більше каталогів з обробки Babel
  3. Додаткові інструменти: Новіші версії Webpack і Babel швидші
  4. Паралельні збирання: Використання thread-loader для багатопоширеного трансподілення
// 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 . mjstoconsole.exposs}

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

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

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

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

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


<!--
    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 було б пропущено.


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 для залежностей вашої оболонки, я раджу вам спробувати пов' язати її з програмою. Почніть з однієї бібліотеки. Подивіться, як це почувається. Зановте там. Екосистема значно дозріла, і, поки крива навчання є реальною, винагорода варта того.

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

Finding related posts...
logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.