# 从 CDNs 到 Bunbling 更新您的前端建设管道@ : @%

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

<!-- category -- ASP.NET, Tailwind, Webpack, Frontend -->
## 一. 导言导言 导言 导言 导言

在过去的一年里, “, I' 跌倒”“, 实验 , 破碎了东西’, 并逐渐将这个博客的前端建设管道“ MS K5 ” 现代化。 这是' 一些执行的总计划完美无缺地— 它是一个试验和错误\, 许多差错", 以及顽固不化的坚持力 驱动着一个清晰的愿景:我想要实现什么?.

I'm 不是一个前端导师或Webpack 向导@. I @'m a {.}NET开发商,他对基于 CDN}-的附属关系的限制感到沮丧,决定必须有一个更好的方法_.}这篇文章记录了从简单开始的乱七八糟的 , 迭接旅程。 `<script>` 指向一个能实际工作的现代捆绑管道的无千克标记@. @%

在“(”的路上,我犯了许多错误。 IQ'll Document “),花了几个小时去解答加密网页卡误差@,,可能还会重整 `node_modules` 超过我愿意承认的几倍... ...但坚持有回报...",...

如果您是 're a {.\}NET 开发者, 盯着 Webpack 配置文件看... ...想知道你在地球上得到了什么?

I'm 是一个老式的网络性能螺旋管 , 拨号@-\上第一个我为赚钱而建的站点 @(}这是在Perlç...a故事中的色情后端

在那段时间里,人们所担心的与众不同的是 @. JavaScript几乎无人知晓$.}我想即使是Divs也是在IE那年代 `div` 网景 `layer`)和您'dluckY 如果您的用户有一个 @56K连接@.}所以我学会了最优化的 @MS K4_even things like tilled imageformation front for m菜单项 @)}等等#...}

后来在微软,我甚至工作了一个自动图像图示工具 在Web Front 表格 @-} 它从一个页面“,”中提取了小的图片。它生成了CSS和一幅图象 自动地\.}但是就像我在 Microsoft@,}那样

简而言之,没有人喜欢一个缓慢的网页@! @

**公平**@,}CDNs aren't neither disality #. * CDN是天生邪恶的#'*

**充分披露**@:_#这个博客是故意设计过度的@.}它确实如此 't *需要* 虽然如此复杂 但这样建起来 让我好好学习现代前端工具 关键地说 , 和\ MSSK2 给了我一些真实的东西 可以写作并教书

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

虽然这工作有好几种缺点 越来越令人沮丧

- **可靠性问题**:CDNs do gon down@.~I' 看过unpkg和jsdelivr有误差 当他们"'re dolown}♪,你的网站被打破了
- **难以预测的装载行为**: @ CDN 回应可能变幻无常, 有时变快@._ 有时变慢#,}有时减慢 @,}有时候它们引入种族环境, 让图书馆按错误的顺序负荷
- **时间安排问题**: 你无法控制脚本在彼此相邻时的负荷@. 这让我对图书馆初始化感到无尽头痛
- **多重HTTP请求**“: ” 每个图书馆都要求单独申请“, ” 即使有 HTTP ” “MSSK2 ”
- **无树-摇晃**: 你下载整个库 *, 即使你只使用其中的一小部分
- **版本流动**: CDNs 可能提供不同的版本, 除非您明确指定它们@,, 甚至被钉住的版本也可以在 CCDN 提供者中表现不同 。
- **构建-时间不确定性**@:_# 无法在运行前发现不兼容性@—}经常当用户报告它的时候
- **有限优化**:Can't 破解或移除死代码
- **离线发展**在火车或飞机上工作时很烦人。

所以我想要控制我的网站使用和需要的正确位置 来控制我的工作。 我想只捆绑 我需要的东西 确保可靠的装货订单\MSK

他们更简单地设置了“,”和“HTTP/2 ”及“ HTTP/3 +多倍驱动”、“, + ”,对于许多项目来说,多重请求的间接费用比以前 “.”要重要得多。

## 现代捆包办法

我目前设置的套件包通过 Webpack 将所有 JavaScript 依赖性都包含在网上, 通过 PostCSS @ . 处理 CSS

### 为什么Webpack 取代 Vite? @

在潜入技术细节前 ",#我应该先谈谈房间里的大象 ':> '为什么我在使用Webpack 当Vite',\#rollup #,_Esbuilding,}和其他现代包装机存在时

诚实的回答很简单, **我早就知道网包**.

I'd在教授我的“"Beginning Web Development”时广泛使用Webpack@"课程,}这是我理解的.的工具 当我决定把这个博客现代化的时候..."'s building proper*,I 已经面对着一个陡峭的学习曲线\—{Odeaddocation tree+-shakingMS K9代码分割 “,模块系统_, PostCSS输油管,和如何将所有这一切与 ASPQ.NET Core{.}整合

**在项目工作时,它通常是一种良好做法。 将“'”限制在 “'”管理@'.”的范围内。**

添加一个全新的建筑工具@" @% 除此之外,我还可以把学习的能量集中到 *概念概念* 而不是某种工具的特异性....

**是Vite 更快@? @** “绝对”.Vite's开发服务器,使用本地的ES模块和 esbuilding-phown 捆绑比WebpackMPK3要快得多。对于有数百个模块的大型项目来说,“,”差别非常大。

**您应该使用 Vite 进行新工程@? @%** 可能是,@ 是 @ . @ 如果您在开始更新, 而且没有现有的网页卡知识 @ I, @ Vite 很可能是更好的选择@ MS K5 @ It' @ server@ MPK7_ 比较简单来配置\ ,} 并代表着前端工具的现代方法 @. @

**我后悔使用Webpack@?** 我学到的关于Bunbling, 代码分裂的教训 和优化可以转移到任何建筑工具\. 而诚实地说\, 对于这个博客来说 这个网络软件包与维特之间的性能差异微乎其微

它是我如何建造东西的关键方面 ;,以什么为起点 从这个基础出发

这里更广泛的教训是 **进度胜于完美**我本可以花几周时间研究“"Best}"Baglyr,}比较基准@,阅读比较文章, 和对决定的煎熬

或许有一天我可能会移居到Vite., 也许我能赢'tMS K2 任何一种方式,这个博客有一个现代的#,优化建材管道,可以可靠地工作\—,而且这“'}什么重要?.

### 软件包结构

缩略 [`package.json`](https://github.com/scottgal/mostlylucidweb/blob/main/Mostlylucid/package.json) 这个结构花了我几次尝试去右转\ — 最初 , Nuget和 npm是 KINDA 相似的, 但是 nppm 在添加 pacakkes\ MPK3\ 时则不太友好

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

**依赖性** 运行时需要图书馆@( @Allpine.}{js @,}HTMX},}EasyMD_,}等{.),} **发展依赖性** 正在构建工具@( @Webpack_,}babyl,}邮局CSS处理器{,}等 *.).*

### 构建脚本

缩略 `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 文件, 它们需要处理 MS K1} @ ( @ 这些要么是 @ I'_ imports @ MPK4} 用于某种特殊目的, 比如装入我的滑稽兰威字体, 要么是适应性字型, 如主题切换增强. Name
- **{\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}我*** 通过 PostCSS 处理尾风 CSS 任务
- **{\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}我*** 通过Webpack 完成的任务@ : bundles JavaScript
- **Dev / 观察 MS K1 建设**: 管弦化整个管道

缩略 `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... 中添加此内容。 您甚至可以说只在调试期间运行@ ' 等 @ MS K2 @ 但我发现它乱七八糟的 @ MPK3 @ 我宁愿手动运行@. @ 所以 `npm run watch` 当任何 cs /}js 文件更改为自动\ I-}* 正在运行构建 *\ MPK3}

在JS世界里,他们几乎使用热的-reload dev runs 这是相当滑稽并让你有点讨厌ASP}.NET Core{'}这些微弱的尝试 *.}

## Webpack 配置深潜水

网络软件包的配置是魔法发生的地方 @—#和大部分时间我花在排除故障上的地方@.}这曾经是'_t春天完全成型的_.} It'}由于无数次迭代的结果\,}Stack overflow search>,}和通过网页软件包文件读取, 在“2+AM”试图理解为什么我的建筑正在生成 &47}块文件{.}

这里“ '” 是当前配置@ MS K1 from [`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',
    },
}
```

此配置自动将您的代码分割成小块@ : @ {}

- **块@: @ @ @ '} 全部 @ MPK2 @**“:”分析同步进口和非同步进口
- **秒=:} @ 20000**: 仅为大于 @20}KB 的模块创建块
- **最大Size=:=100000=**“:”试图分割大于“100”的块
- **运行时间 chunk**: 提取 Webpack 时间运行到一个单独的文件, 用于改进 longQ-term caching

在 @ ,} 这将生成多个文件 `wwwroot/js/dist/`:

- `main.js` -您的申请入境点
- `runtime.js` -Webpack 模块装入逻辑
- `[vendor].[contenthash].js` - 自动分割销售商块
- 代码% - @split 路径或懒惰@-}装入模块的额外块块

#### 婴儿出生

```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%
- **模組@: 假**:_PreservesES6_BARBAR_Webpack tree_-_shaking 的模块
- **使用 InbuiltIns@ : @\ MS K1_ usage @ '**: 自动包含只针对您使用的特性的多填充量
- **核心Js: 3**:使用核心@-js 版本 @3用于多填充

这意味着我可以写现代 JavaScript @ ( @ async@ MS K1 @ wait,_ 任选链条 @ MPK3} 完全无效的联结=)}同时保持广泛的浏览器支持@.}

#### 生产最集中化

```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,
    }),
] : []
```

Terser 积极将生产规模放大,

- **左下方@__%Contol**: 全部删除 `console.log()` 报表报表
- **{\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}Massós: 3**: 运行压缩三次以最大尺寸减小
- **最上层@: @ 真实**: 曼格勒斯顶端 @-}等級變數名稱
- **纯_funcs**: 移除特定的控制台方法, 即使指定为变量
- **@:_# 虚假的评论**: 勾销输出的所有批注

与未校正的代码@.相比, 这通常会减少 JavaScript 捆绑大小 。

## PSCSS 尾风管道

尾风 CSS 由 PostCSCS 用多个插件处理, 此配置为 @ (} 来自 [`postcss.config.js`](https://github.com/scottgal/mostlylucidweb/blob/main/Mostlylucid/postcss.config.js)与Webpack和:相比,

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

管道工程如下:

1. **进口***:* 决心 `@import` CSS 文件中的语句
2. **尾风**: 处理尾风指令并生成工具类
3. **自动前缀器**: 添加用于浏览器兼容性的供应商前缀
4. **丙诺**: 最小化 CSS 最后输出

### 尾风配置

缩略 [`tailwind.config.js`](https://github.com/scottgal/mostlylucidweb/blob/main/Mostlylucid/tailwind.config.js) 查找类名@ . @ 获取 `content` 右侧是关键路径 @— miss a path and Tadwind won@'}%t 为这些文件生成分类 *\ :}

```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 视图和电子邮件模板@)}
- **安全列表**: @% 总是包含这些类, 即使扫描中找不到它们 。
- **黑暗的Mode: @\ "}=类@"#**: 启用类@-基暗模式切换
- **主题@. @% extend**添加自定义颜色和间距值
- **插件插件**: 包括尾风插件和 DisaiUI 组件库

在构建时间时反尾风扫描这些文件@ , @ 仅提取您实际使用的工具类 MS K1} 这就是为什么最终的 CSS 文件比完整的 Tailwind 库要小得多吗?

## JavaScript 进入点

缩略 [`main.js`](https://github.com/scottgal/mostlylucidweb/blob/main/Mostlylucid/src/js/main.js) 此文件的导入和初始化全部依赖性@ , @ 和它='$%s 有机地成长为 I'+# 已添加了博客功能\ MS K4}

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

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

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

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

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

这种方法可带来若干好处*:

1. **单一进口点**: 所有从一个条目文件装入的依附关系
2. **公开文本**@ :_ profile@ MS K1}json 锁定特定版本
3. **树木@-_shaking**:Webpack 从图书馆移除未使用的代码
4. **全球接触**由 Razor 浏览的 M:图书馆 `window`
5. **自定义模块**:應用程式@- 特定代码已整理成可导入模块

### 布局模板集成

内 `_Layout.cshtml`@,_i 现在只引用已捆绑的资产@:}

```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 标签助手, 该助手将文件内容的散列版本附加到查询中@ ; 有效缓存 @ MS K2busting

仅剩的CDN依赖关系是:

- **Google 签名@- @in**: 需要外部 CDN 的 OAUT 功能
- **反盒式**: 图标字体 @( 可以捆绑@, 但最小的收益\)
- **Umami分析器**@ : Third-party 分析脚本@-自我主机, 这样我就能看到你的访问而不是谷歌 @??

## 构建管道可视化

整个建设过程如何从源文件流向生产资产?

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

它看起来相当复杂,但实际上它处理大部分的网络 Pack does @,}这本身就是一种复杂的方式。 象Vite Don@'这样的人需要这种复杂的方式... ...但是.. ) *

## 业绩改进

重覆, 并不是真的 PONINT. 但它的确像一个绝对子弹一样载荷 现在 . 您仍然可以得到与云层的争议

## 开发者经验收益

现代建筑管道大大改进了发展工作流程 :

### 安全类型和 Intelli 感知

通过Webpack捆绑使适当的 JavaScript 模块解析 ,}意思是:

- **VS 代码中的 Intelli 感知**:Auto-完成导入模块的补全
- **跳转到定义**: 导航直接到图书馆源代码
- **内线文件**图书馆的JSDoc评论出现在IDE上。

当建筑破裂时 `npm run watch`“) ”我知道Instantly@, 与“(”相结合,

### 热模块替换

发展期间*, `npm run watch` 启用“ 近端”\ - @ instant 回馈@ MS K1} {

```bash
npm run watch
```

此功能在监视模式下运行 Webpack@ , @ 重建只更改了模块@ I:}

- **典型重建时间***: 200-400*
- **浏览器自动@ - @ refresh**使用浏览器@- @sync 或类似工具
- **预先保留的申请状态**@:_HMR维持申请状态,

### 扶养管理

npm's 锁定文件 @ (`package-lock.json`)+#确保环境的一致建设 #:+#

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

这保证了在开发中 , CI/CDMS K2和生产环境中的同一依赖性版本@.

在JSWorld_,}这些被称为“'pinning' ” JS WORLA中,依附关系设置在 Stone@.}您可以不用软件包锁做,但随后您也可以不使用软件包锁定。

### 构建脚本

组织起来的 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` {:}文件 *% 1}

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

### 从 JavaScript 模块处理 CSS

有些图书馆包括需要导入的 CSS@: @%

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

Webpack+' @%s `css-loader` 和 `style-loader` 处理这些导入的“% 1”

- 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 自动代码@ - @ splits 将动态导入为单独的块@ MPK1} 已装入 MS K2{ 点名} .} 。

### 处理共同JS模块

一些较老的图书馆使用共同JS, 而不是ES6+#模块@:+%

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

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

Webpack 以透明的方式操作两个模块系统@ , @ 将 CompicJS 转换为ES @ MS K1}( 需要时)\ .

## 解决共同问题

### 发展过程中缺少的原始地图

确保确保: `devtool` 被配置在 `webpack.config.js`:

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

这将生成源地图, 以便更容易调试@. @%

### CSS 与尾风的分类净化问题

如果尾风删除课程, 您将使用 @ , check `content` 配置@: @%

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

使用“Open,” `safelist` 用于动态类@: @%

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

### 模块未找到错误

如果 Webpack 能解答一个模块 @ , check@ MS K2 t

1. **校正导入路径**@ :_ 相对路径开始于 `./` 或 `../`
2. **文件扩展名**“: 添加” `.mjs` 至 `resolve.extensions` 必要时
3. **异式**: 使用 `resolve.alias` 复杂路径

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

### 建设绩效问题

如果建筑变慢了 :

1. **启用缓存**:+Webpack @'QS 缓存大大加快了重建的速度
2. **减少贝贝范围**\ :\ {} 在 Babel 处理中排除更多目录@ action
3. **升级工具**“:”新版本的网页包和 Babel 更快。
4. **平行建设**: 使用 `thread-loader` 用于多@ - @ threaded 移植

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

## A. 汲取的艰难经验教训

让我分享一下我在这段旅程中犯的一些错误,

### 试图同时包扎一切

我的第一个尝试是将所有CDN参考文件一一撕开, 并试图通过Webpack@.+#这栋建筑破碎得惊人地打破了.+_我了解到,递增迁移是你的朋友\—+%移动一个图书馆 一次彻底的,+测试,}然后移到下一个#.+#

### 不理解模块类型

我浪费了几个小时 试图弄清楚为什么某些图书馆 会"'t load.}结果发现"MS K2混合共同JS (`require()`“)”和“ESZ6”模块“(”`import`@)_# 不懂Webpack如何操作它们导致加密错误@.} `experiments: { outputModule: true }` 我初始设置中的配置@ ' @ title

解决之道来自午夜时分在Webpack仓库读取GitHub议题,

### 错误@#3: @ 超过% - @ 侵略性代码分割

在某一点, 我的配置正在为我拥有的一切 创造块子 `minSize: 10000` (10KB) 意思是Webpack正在为小型通用功能创建单独的文件 @. 校正过@- 并被修改到 @MS K4MBMZ}...Page 负载成为“50+小块请求”的瀑布或等待大块下载\.我学到了代码分割是好的@,,但你需要明智的阈值#.那"'}为什么我的当前配置使用 `minSize: 20000` # (20KB).#

### 忘记了 `.mjs` 延长

一些 npm 软件包配有 `.mjs` 扩展@. @ webpack won'}t 默认解答这些... @.}我花了很尴尬的很长时间调试为什么 `@mostlylucid/mermaid-enhancements` 在发现我需要添加前 't 加载 `.mjs` 至 `resolve.extensions`.

简单修正一旦您知道它@, @ but finding that out\"?}但发现它花费了时间#.}

### 错误% 1\ #5:\ {} 不是 Caching Webpack 构建

“30-60”秒后开始早期建造“MSK 0 秒”,因为我曾经启用过Webpack@' 文件系统缓存@MS K3 一旦我添加了 caching, 重建时间滴落到 @2-3 second_ . 这是用于开发工作流程的游戏‘- changerr, 但花了我几周才发现\.}

### 我实际使用的尾风净化类

我最喜欢的调试经验... ...:CSS课程,这些课程在发展中表现良好 在制作过程中突然消失 `safelist` 配置@,}但只是在 I' 浪费了时间之后... ...想知道我是否要疯了吗?
我同样也 ( 例如 `mostlylucid.pagingtaghelper` Project )使用 @'dommy block@'}我通过隐藏块来简化我的尾风配置更改

```html

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

```

当尾风建立时, 它扫描了类的 cshtml 文件@ ; @ 如果它不这样做的话, 它会删除它们 '} 所以如果这个隐藏区块可以确保它看到我所需要的类别 即使它们只在嵌入的库视图中使用 're.}JSP开发商通常也会扫描 JS/TS 文件以便使用 CSS 样式来让 Talwind 否则将会错过 &.}

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

}

```

@,_@ '_ 塔林风批准}'_ 策略是将课程添加到安全列表中, 但I'{ 是一个 ASP=.NET gate_ MS K6}所以 HTML won.}

### 我得到了正确的 : 持久性

我做对的一件事就是拒绝放弃. 所有错误信息都是一个学习机会 . 每一个破碎的建筑都教给我一些关于这些工具如何运作的新东西 '.我有一个清晰的愿景—bundled," 最佳资产,快速装载"MS K5" 直到我到达那里为止 我一直不停地重复

## 结论结论

从CDNMSMK0基依赖性向现代捆绑管道的移动已经对这个博客“,”产生了转变,但表现上的改进是巨大的 “'t easy”.。

密钥取走@: @%

1. **捆绑减少有效载荷体大小**@ : @ 树木@ MS K1_ shaking and minfication 取消未使用的代码
2. **代码分割会改善业绩**: 浏览器只装载需要的东西
3. **构建工具启用现代 JavaScript**:Babel传译支持最新的语言功能,同时保持浏览器兼容性。
4. **CSS 中央支助**: 尾风@' JIT 编译器和 csnano 提供小样式表
5. **与 @.NET的整合是无缝的**: MSBulild 目标自动运行前端建筑
6. **持久性胜过完美**{\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}你需要做前端专家

对于习惯于简单 CDN 的 ASP.NET核心开发商来说, 包括{,} 这在一开始会感到压倒性不已: <MSK23#s normal_.}我同样感觉到 . start small\MS K6 一次迁移一个受抚养人@, 并且不要害怕破坏发展过程中的东西〞%.你□' 会从调试破碎的建筑中学中学到更多东西 比你看文档时要多得多吗.

在此共享的配置 I' @ ve 表示几个月的循环@ . @ 您的旅行将会不同 @ MS K2 @ 并使用此选项作为起始点 @ MPK5 @ not gospel}.{ 调整它以适应您的需要 @ MEK7_ 实验=, 并且不要因为失败而感到沮丧\ — @ itemez@'}这是进程的一部分 @.}

**这个博客是否设计过周密 @?@** 绝对的.我能不能继续使用CDN(CDN)并花时间做其他事?

如果您仍然依赖 CDN 作为您的前端依附关系 我鼓励您用一个库来捆绑尝试. . 看看它从那里感觉到什么"MS K4" Iterate.}生态系统已经大大成熟了 虽然学习曲线是真实的, 但报酬值得

## 继续阅读

- [Webpack 文件](https://webpack.js.org/)
- [CSS: 优化生产](https://tailwindcss.com/docs/optimizing-for-production)
- [使用指南](https://babeljs.io/docs/en/usage)
- [PSCSS 文件](https://postcss.org/)
- [我前一篇文章的 ASP @ . NET 核心开发者的尾风](/blog/tailwind)