Протягом минулого року я спіткнувся, експериментував, ламав речі, і поступово модернізував трубопровод для побудови цього блогу. що я хотів досягти.
Я не гуру-оболонка і не майстер веб-пакування. Я розробник .NET, який розчарувався через обмеження заснованих на CDN залежностей і вирішив, що має бути кращий спосіб. <script> Теґи, що вказують на unpkg на сучасний трубопровод для бундінгів, насправді працюють.
Я зробив багато помилок на шляху (які я буду документувати), витратив години для усування таємних помилок Webpack, і, ймовірно, знову встановлено node_modules Але наполегливість була винагороджена, і я навчився багато чого завдяки твердій рішучості отримати це право.
Якщо ви - розробник .NET дивлячись на файли конфігурації Webpack, цікаво, що на землі ви отримали себе в цю статтю для вас.
Я старий нутер в інтернеті, на початку першого сайту, який я збудував за гроші (це не був сервер порно в Perl... Іншого разу) був сайт "Сніжні умови" з автоматизованої телефонної лінії.
У ті часи занепокоєння були цілком різними. JavaScript практично не було вказано. На мою думку, навіть диски були RARE (в ті дні IE був div і Netscape layer) і ви будете LUCKY, якщо ваші користувачі мають з' єднання 56K. Отже, я навчився оптимізувати (навіть такі елементи, як плиткове тло зображень для пунктів меню) тощо...
Пізніше у Microsoft I навіть працював над автоматичним інструментом спрайта зображень у веб- формах - це було чудово: він дістав маленькі зображення зі сторінки, створив CSS і зображення спрайта автоматично. Але, на жаль, як і мій час у Microsoft, це не мало бути.
Одним словом, це одержимість, що я переніс усю свою кар'єру.
Чесність., 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">
У нього є кілька недоліків, які дедалі більше розчаровували:
Отже, я хотів контролювати, що саме використовує мій сайт і який був потрібен для роботи, я хотів зв'язати тільки те, що мені потрібно, забезпечити надійне завантаження і оптимізувати швидкодію.
Для багатьох проектів, особливо малих, або прототипів, CDN залишається абсолютно розсудливим вибором.
Мої поточні налаштування містять всі залежності JavaScript за допомогою Webpack і оброблюють CSS за допомогою PostCSS.
Перед тим як зануритися в технічні деталі, я маю звернутися до слона у кімнаті: чому я використовую веб-папер, коли 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"
}
}
За допомогою цього пункту можна розподілити параметри:
rimrafThe 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.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',
},
}
За допомогою цього налаштування можна автоматично розділити ваш код на менші шматки:
На практиці, за допомогою цього пункту можна створити декілька файлів у 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 підтримують старіші переглядачі:
Це означає, що я можу писати сучасний 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,
}),
] : []
Терсер наполегливо знижує кількість виробу:
console.log() командиУ цьому блозі це, зазвичай, зменшує розмір у вузлі JavaScript на 60- 70% порівняно з незміненим кодом.
CSS Tailwind оброблюється за допомогою PostCSS з декількома додатками. Це налаштування (з postcss.config.js) милосердно можна порівняти до Webpack:
// postcss.config.js
module.exports = {
plugins: {
'postcss-import': {},
tailwindcss: {},
autoprefixer: {},
cssnano: { preset: 'default' }
}
}
Провідна труба працює так:
@import заяви у файлах CSSThe 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 і шаблони електронної пошти)Bailwind сканує ці файли під час збирання, видобування лише класів програм, якими ви користуєтеся. Ось чому кінцевий файл CSS набагато менший за повну бібліотеку Tailwin.
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();
Такий підхід може принести багато переваг.
windowВхід _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:
Ось як весь процес збирання проходить від початкових файлів до виробничих активів:
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, що означає:
КОЛИ будівництво розпадається (з npm run watch) Я ЗНАЮ INSTANTANT, а також (кількість, але зростаюча) тести JS, це означає, що зворотний зв'язок стає тіснішим.
Під час розробки npm run watch вмикає майже будь- який зворотній зв' язок:
npm run watch
Цей пункт запускає webpack у режимі спостереження, з' єднання лише змінених модулів:
Файл блокування 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, який потребує імпортування:
import EasyMDE from "easymde";
import 'easymde/dist/easymde.min.css'; // Imported CSS is processed by Webpack
Webpack's css-loader і style-loader працює з цими імпортами:
<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.
Деякі старіші бібліотеки використовують 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',
Ця програма створює карти джерела у розробці для спрощення усування вад.
Якщо Хитхий вітер усуває класи, які ви використовуєте, позначте 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 не вдалося визначити модуль, перевірте:
./ або ../.mjs до resolve.extensions якщо потрібноresolve.alias для складних шляхівresolve: {
extensions: ['.js', '.mjs', '.json'],
alias: {
'@components': path.resolve(__dirname, 'src/js/components/'),
}
}
Якщо збирання стане повільним:
thread-loader для багатопоширеного трансподілення// Enable Webpack caching
cache: {
type: 'filesystem',
},
Дозвольте мені поділитись деякими помилками, які я зробив під час цієї подорожі, щоб ви могли уникнути їх:
Моя перша спроба полягала в тому, щоб видобути всі посилання CDN одним ходом і спробувати зв'язати все через Веб-пакування. Поламана споруда приголомшливо. Я дізнався, що додаткова міграція - це ваш друг, дві бібліотеки за один раз, тест ретельно, а потім перейти до наступного.
Я змарнував години, намагаючись зрозуміти, чому деякі бібліотеки не завантажуються.require()) і модулі ES6 (import) не розуміючи, як Webpack керує ними, можна дійти до таємних помилок. experiments: { outputModule: true } конфігурації не було у моїх початкових налаштуваннях, і я не міг зрозуміти, чому мої модулі не завантажуються.
Щоб розв'язати цю проблему, о півночі було прочитано у сховищі веб-пакування через " GitHub ."
В один момент моя конфігурація генерувала шматки для всього. minSize: 10000 (10KB) це означало, що Webpack створює окремі файли для невеличких функціональних можливостей програми. Перенавантаження і перехід на 2MB... Пейдж стало водоспадом з 50+ крихітних запитів до шматків або очікування на те, що буде звантажено величезні шматки. Я дізнався, що розбиття коду є корисним, але вам потрібні розумні пороги. Ось чому моє поточне налаштування використовує minSize: 20000 (20 КБ).
.mjs Суфікс назвиДеякі пакунки npm розповсюджують модулі ES6 за допомогою .mjs webpack не вирішить їх типово. @mostlring I needed to add . mjstoconsole.exposs}
Простая исправа, когда ты это знаешь, но это заняло время.
Перші збирання зайняли 30- 60 секунд, оскільки я не увімкнув кеш файлової системи Webpack. Після додавання кешу час збирання зменшився до 2- 3 секунд. Це був інструмент зміни гри для роботи з розробки, але мені знадобилось кілька тижнів часу на відкриття.
Мій улюблений досвід у усування вад: класи 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, до сучасного трубопроводу була трансформацією для цього блогу, але це не було легко. Покращення вистави істотно, досвід розробника значно кращий, і я маю набагато кращий контроль над оптимізацією, але це зайняло місяці випробувань і помилок, щоб дістатися сюди.
Захоплення ключів:
Для розробників ядра ASP. NET, які звикли до простих CDN, це спочатку буде дуже пригнічувати. Це нормально. Я так само почувався. Почніть з маленької, мігруйте одну залежність за раз і не бійтеся розбивати речі у розробці. Ви навчитеся більше з усування пошкоджених структур, ніж ви коли- небудь зможете прочитати документацію.
Налаштування, які я тут поділив, представляє місяці ітерацій. Ваша подорож буде іншою, і це добре. Використовуйте це як початкову точку, а не євангелію. Пристосовуйте її до ваших потреб, експерименту, і не знеохочуйтеся через тяжку, це частина процесу.
Цей блог переобладнано? Але я б не вивчив половину, і я б не мав цієї статті, щоб поділитися з вами. Іноді перебудування - це не те, що ви вивчаєте на шляху, а можливість навчати інших з цього досвіду.
Якщо ви все ще покладаєтесь на CDN для залежностей вашої оболонки, я раджу вам спробувати пов' язати її з програмою. Почніть з однієї бібліотеки. Подивіться, як це почувається. Зановте там. Екосистема значно дозріла, і, поки крива навчання є реальною, винагорода варта того.
© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.