خلال العام الماضي، تعثرت، وجربت، وكسرت الأشياء، وطوّرت تدريجياً خط بناء الواجهة الأمامية لهذه المدونة. لم تكن هذه بعض الخطط الرئيسية التي نُفّذت بشكل خاطئ - لقد كانت تجربة وخطأ، الكثير من الأخطاء، والمثابرة العنيدة مدفوعة برؤية واضحة لما أردت تحقيقه.
أنا لست مُعلم واجهة أو مُطوراً أو مُطوراً على شبكة الإنترنت الذي أُحبط من قيود الاعتمادات القائمة على نظام CDN وقرر أنه يجب أن يكون هناك طريقة أفضل. هذه المقالة تُوثق الرحلة الفوضوية التكرارية من بسيطة <script> علامات تشير إلى unpgg إلى خط أنابيب عصري يعمل فعلاً.
لقد قمت بالكثير من الأخطاء على طول الطريق (والذي سأوثقه) قضيت ساعات في تصحيح الأخطاء المشفرة في ويب باقة أخطاء، وربما أعيد تركيبها node_modules لكنَّ المثابرة تؤتي ثمارها ، وتعلَّمت مبلغا هائلا من خلال تصميمي على تصحيح هذا الوضع .
إذا كنت مطوراً لشبكة الإنترنت يحدق في ملفات تشكيلات ويبباك يتساءل ما على الأرض الذي وضعت نفسك فيه - هذه المقالة هي لك.
في الأيام الأولى من الاتصال بالموقع الأول الذي بنيته من أجل المال (الذي لم يكن منبأ إباحي في بيرل... قصة لوقت آخر) كان موقعاً لشرطة الثلج الذي انحرف عن خط الهاتف الآلي.
في تلك الأيام كانت المخاوف مختلفة تماماً. جافاسكربت كان غير معروف عملياً. أعتقد أن حتى الـ Divs كانت RARE (في تلك الأيام IE كان div (و) كان layerو ستكون محظوظاً لو كان لمستخدميك اتصال بـ 56K. لذا تعلمت أن أفضل (حتى أشياء مثل خلفيات الصورة المملوءة للعناصر المدرجة في القائمة) وما إلى ذلك...
في وقت لاحق في مايكروسوفت عملت حتى على صورة آلية أداة spritter في أشكال ويب -- كان رهيبا: لقد استخرجت صورا صغيرة من صفحة، ولدت CSS و صورة sprite تلقائيا. ولكن للأسف، مثل وقتي في مايكروسوفت، لم يكن من المفترض أن يكون.
باختصار، إنّه هاجس أنّي تحملتُ كامل مسيرتي المهنيّة، لا أحد يحبّ الموقع البطيء!
أن تكون عادلة، CDNs لَيستْ شرّاً أصيلاً.
الكشف عن شيء: هذه المدونات مُهَدَّسَةُ عمداً. الحاجة لكن بناءه بهذه الطريقة، دعوني أتعلم أدوات الواجهة الحديثة بشكل صحيح، وبشكل حاسم أعطاني شيئاً حقيقياً لأكتب عنه وأدرسه. أحياناً أفضل طريقة للتعلم هي بناء شيء سخيف قليلاً وتوثيق الرحلة.
وفي البداية، كان نهجي واضحاً:
<!-- Old CDN-based approach -->
<script src="https://unpkg.com/[email protected]/dist/cdn.min.js"></script>
<script src="https://unpkg.com/[email protected]"></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 وصوب نهج تجميعي.
إنها أبسط لإنشاء، ومع HTTTP/2 وHTTTP/3 التعدد التكسين، فإن العرض العام متعدد الطلبات أقل أهمية بكثير مما كان عليه. بالنسبة للعديد من المشاريع، وخاصة المشاريع الصغيرة أو النماذج، يبقى CDNs خياراً معقولاً تماماً. ولكن بالنسبة لموقع إنتاج حيث أردت التحكم، الموثوقية، والتعظيم،
وضعي الحالي يحزم كل مُعتمدات جافاسكربت من خلال حزمة ويب والعمليات CSS من خلال PostSSS.
قبل الغوص في التفاصيل التقنية، ينبغي لي أن أخاطب الفيل في الغرفة: لماذا أستخدم ويبباك عندما يكون هناك فيتي، رولوب، إسبنال، وغيرها من الحزم الحديثة؟
الاجابة الصادقة بسيطة : كنت أعرف مسبقاً على ويب باك.
كنت قد استخدمت حزمة ويب على نطاق واسع عند تدريسي دورة "بدء تطوير شبكة الإنترنت"، وكانت الأداة التي فهمتها. عندما قررت تحديث خط أنابيب بناء هذه المدونة، كنت بالفعل في مواجهة منحنى تعليمي حاد - فهم كسر الأشجار، تقسيم الرموز، أنظمة الوحدات، خطوط أنابيب ما بعد CSSS، وكيفية دمج كل هذا مع ASP.NET الأساسية.
عادة ما تكون ممارسة جيدة عند العمل على المشاريع؛ الحد من 'الجديد' إلى 'القابل للادارة'. إنها طريقة أسهل للحد من عدم اليقين.
إضافة "تعلّم أداة بناء جديدة تماماً" فوق ذلك بدا غير ضروري. حزمة ويب تعمل. إنها ناضجة. لديها وثائق ممتازة ونظام إيكولوجي هائل. والأهم من ذلك، يمكنني أن أركز طاقتي التعليمية على * على سبيل المثال:* من الحشوة الحديثة بدلاً من الحيل الخفية لأداة معينة.
هل فايت أسرع؟ بالتأكيد، خادوم تطوير فيتي مع وحدات محلية لـ ESS والربط الذي يعمل بـ espany هو أسرع بكثير من حزمة ويب. بالنسبة للمشاريع الكبيرة مع المئات من الوحدات، الفرق كبير.
هل يجب أن تستخدم فيتي لمشروع جديد؟ على الأرجح، نعم. إذا كنت تبدأ من جديد وليس لديك معرفة قائمة لـ Webpack، Vite على الأرجح الخيار الأفضل. هو أسرع، أسهل للتهيئة، ويمثل النهج الحديث لـ واجهة الأداة.
هل أندم على استخدام حزمة ويب؟ ليس على الإطلاق. لقد أوصلني إلى حيث كنت بحاجة إلى أن أكون. الدروس التي تعلمتها عن التجميع، تقسيم الرموز، والتعظيم هي قابلة للتحويل إلى أي أداة بناء. وبصراحة، بالنسبة لمقياس هذه المدونة، الفرق في الأداء بين ويبباك وفيت هو لا يُذكر - نحن نتحدث عن ملي ثانية في التنمية إعادة بناء الأوقات.
وهو جانب رئيسي لكيفية بناء الأشياء، تبدأ مع ما هو EASY والبناء من ذلك الأساس.
والدرس الأعمى هنا هو أنه: التقدم المحرز في النجاح والكمالكان من الممكن أن أمضي أسابيع في البحث عن "أفضل" حزمة، مقارنة النقاط المرجعية، قراءة مقالات المقارنة، والتلميح على القرار. بدلاً من ذلك، اخترت الأداة التي أعرفها، جعلتها تعمل، وتحركت إلى الأمام. لقد أبقتني هذه العملية على النقل البحري بدلاً من التداول إلى ما لا نهاية.
ربما في يوم ما قد أهاجر إلى فيت. ربما لن أفعل. على أي حال، هذه المدونة لديها خط أنابيب بناء حديث، متفائل يعمل بشكل موثوق - وهذا ما يهم.
الـ package.json هذا الهيكل أخذ مني عدة محاولات للحصول على الحق - في البداية، نوغيت و npm هما KINDA متشابهان ولكن nppm أقل ودية بكثير عند إضافة pacakugs.
{
"dependencies": {
//NOTE - This is a pre-release of these enhancements, the 1.0.0 release is OUT NOW!
"@mostlylucid/mermaid-enhancements": "^1.0.0-alpha0",
"alpinejs": "^3.14.1",
"codemirror": "5.65.13",
"core-js": "^3.39.0",
"easymde": "2.20.0",
"flatpickr": "^4.6.13",
"highlight.js": "^11.10.0",
"highlightjs-cshtml-razor": "^2.1.1",
"html-to-image": "^1.11.13",
"htmx.org": "^2.0.1",
"mermaid": "^11.0.2",
"regenerator-runtime": "^0.14.1",
"svg-pan-zoom": "^3.6.2"
},
//These are just used for build; not needed to RUN the app so we have a separate place for 'em
"devDependencies": {
"@babel/core": "7.26.9",
"@babel/preset-env": "7.26.9",
"@tailwindcss/aspect-ratio": "^0.4.2",
"@tailwindcss/forms": "^0.5.7",
"@tailwindcss/typography": "^0.5.12",
"autoprefixer": "10.4.21",
"babel-loader": "10.0.0",
"cpx": "1.5.0",
"css-loader": "7.1.2",
"cssnano": "7.0.6",
"daisyui": "^4.12.10",
"mini-css-extract-plugin": "^2.9.4",
"npm-run-all": "4.1.5",
"postcss": "8.5.3",
"postcss-cli": "11.0.1",
"postcss-import": "^16.1.0",
"rimraf": "6.0.1",
"style-loader": "4.0.0",
"tailwindcss": "3.4.17",
"terser-webpack-plugin": "^5.3.10",
"webpack": "^5.91.0",
"webpack-cli": "^5.1.4"
}
}
المُمْكِلَات (Alpin.js, HTMX, EaseMDE, et al., et al.,) وثائق التفويض أدوات البناء (Webpack، Babel، معالجات PostCSS، وما إلى ذلك).
الـ package.json تم تطوير الخادوم إلى حد كبير:
{
"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الـ 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... يمكنك حتى أن تقول فقط تشغيل أثناء dibuk' etc... ولكن أجده فوضوياً. أفضل فقط تشغيله يدوياً. npm run watch يقوم بذلك فقط، عندما يقوم أي من css / js في الملف بتغييره، يقوم بتشغيل المبنى تلقائياً.
ملاحظة: في عالم الـ "برنس" تقريباً يستعملون أجهزة الـ "دي ف" التي هي بقعة جميلة وتجعلك نوعاً ما تكرهين "أس بي إنت" محاولة ضعيفة
إعدادات حزمة الويب هي حيث يحدث السحر وحيث قضيت معظم وقتي في حل المشاكل. هذا لم ينبثق إلى الوجود بالكامل. إنها نتيجة لتكرارات لا تحصى، عمليات البحث لـ (ستاك) الزائدة عن الحاجة، وقراءة وثائق ويب باك في الساعة 2 صباحاً لمحاولة فهم لماذا بنيتي كانت تولد 47 قطعة من الملفات.
هذه هي التشكيلة الحالية (من 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,
}],
],
},
},
}
Pabel spiples JavaScript الحديثة لدعم المتصفحات القديمة:
هذا يعني أنه يمكنني كتابة JavaScript الحديثة (async/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() البيانات البيانيةعلى هذه المدونة، هذا يقلل عادةً من أحجام حزمة جافاسكربت بنسبة 60 إلى 70% مقارنةً بالرمز غير المميّز.
تُجهّز CSSS من خلال PostSSS مع عدة ملحقات. هذا التشكيل (من postcss.config.js) هو بسيط بشكل رحيم بالمقارنة مع Webpack:
// postcss.config.js
module.exports = {
plugins: {
'postcss-import': {},
tailwindcss: {},
autoprefixer: {},
cssnano: { preset: 'default' }
}
}
ويعمل خط الأنابيب على النحو التالي:
@import في ملفات CSSSالـ tailwind.config.js حدّد الملف حيث يجب أن يبحث التايل وون ويندد عن أسماء الفئات. content المسارات الصحيحة كانت حرجة - في حالة عدم وجود مسار و تايلويند لن يولّد فصولاً لتلك الملفات:
module.exports = {
content: ["./Views/**/*.cshtml", "./EmailSubscription/**/*.cshtml"],
safelist: ["dark", "light"],
darkMode: "class",
theme: {
fontFamily: {
body: ["Raleway", "sans-serif"],
},
extend: {
colors: {
"custom-light-bg": "#ffffff",
"custom-dark-bg": "#1d232a",
primary: "#072344",
secondary: "#00aaa1",
// ... more custom colours
},
},
},
plugins: [
require("@tailwindcss/aspect-ratio"),
require("@tailwindcss/typography"),
require("daisyui"),
],
};
السمات الرئيسية:
.cshtml لملفات لأسماء الفئات (بما في ذلك آراء الموازِزِزِر و قويقوم تايلويد بمسح هذه الملفات في وقت البناء، يستخرج فقط فئات المرافق التي تستخدمها بالفعل. هذا هو السبب في أن ملف CSS النهائي أصغر بكثير من مكتبة Tailwind الكاملة.
الـ 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 إلى أعلم المتصفح ما هو نوع ملفّ هذا 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
يبدو معقداً جداً لكن في الواقع هذا ما يفعله ويبباك، إنه يتعامل مع معظم هذا نفسه (بطريقة معقدة لا يحتاجها مثل فيتي لكن).
مرة أخرى، ليس في الواقع نقطة. لكنها تحمل مثل رصاصة مطلقة الآن. يمكنك الحصول على مشاكل مع سحاب فلار 'حمولة Rocket' (التي تقوم بتحميل صفحة الأحداث إلى حد ما) ولكن هذا هو الإضاءة.
وبالإضافة إلى الأداء، يؤدي خط أنابيب البناء الحديث إلى تحسين تدفق العمل الإنمائي إلى حد كبير:
يتمكّن التثبيت السليم لوحدة جافاسكرتمبت من خلال حزمة ويب مما يعني:
متى ينكسر البنيان ( مع) npm run watchأنا أعرف بشكل غير مباشر، بالإضافة إلى اختبارات وحدة الـ " يون جيه " التي تعني حلقات تغذية مرتدة أكثر إحكاماً.
أثناء التنمية، npm run watch يُمكّن من التغذية المرتدة شبه الفورية:
npm run watch
هذا يُدير حزمة ويب في نمط المراقبة، إعادة بناء فقط الوحدات المُغيّرة:
قفل ملف قفل NPm (package-lock.json(ب) كفالة الاتساق بين البيئات:
npm ci # Clean install from lock file
وهذا يضمن نفس أشكال الاعتماد في التنمية، وفي المؤتمر الدولي المعني بالبيئة والتنمية، وفي بيئات الإنتاج.
يُشار إلى هذه باسم "الركن" في عالم الـ JS، حيث يتم وضع التبعية في حجر. يمكنك القيام بذلك بدون قفل حزمة ولكن بعد ذلك تعتمد على المؤلفين Pacakge؛ غالباً HUNDREDS من أطقم مختلفة من الناس لا تفسد بعض الإصدارات البسيطة.
الـ مُنظّم 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
وتحتاج بعض المكتبات إلى تعرض عالمي لاستخدامها في مشاهدات المزال أو النصوص الخطية:
// 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>
وتشمل بعض المكتبات الدائرة التي تحتاج إلى استيراد:
import EasyMDE from "easymde";
import 'easymde/dist/easymde.min.css'; // Imported CSS is processed by Webpack
مجموعة ويب (Webcus) css-loader وقد عقد مؤتمراً بشأن style-loader ويتعامل مع هذه الواردات:
<style> أو مُفكِضبالنسبة للمكتبات التي لا تحتاج إلا إلى صفحات محددة، استخدم الواردات الدينامية:
// 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();
}
وتُحمَّل تلقائياً على مجموعة ويب الواردات الديناميكية المرمزة والمجزأة إلى قطع منفصلة، عند الطلب.
وتستخدم بعض المكتبات الأقدم عهدا نظام UniversityJS بدلا من نماذج SES6:
// CommonJS require syntax
const hljs = require('highlight.js');
// Or use dynamic import
import('highlight.js').then(hljs => {
// Use hljs
});
ويب عواصف كلا نظامي الوحدات بشفافية، تحويل المشترك JS إلى ES6 عند الحاجة.
(كُرِج) devtool هذا webpack.config.js:
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()(أ) واقتطاعاتimport) بدون فهم كيفية تعاملات (ويب باك) معها تؤدي إلى أخطاء مشفرة. experiments: { outputModule: true } التشكيل لم يكن في إعدادي الأولي، ولم أستطع أن أعرف لماذا وحداتي لم تكن تحميل.
جاء الحل من قراءة من خلال قضايا جيت هوب على مستودع ويب باك في منتصف الليل.
في نقطة ما، كان تشكيلي يولّد قطع من كل شيء. minSize: 10000 (10KB) الذي يعني أن حزمة ويب كانت تنشئ ملفات منفصلة لأداء وظائف صغيرة جداً. تم تصحيحها أكثر من اللازم وذهبت إلى 2MB. أصبح تحميل الصفحة شلالاً لـ 50+ جزء صغير من الطلبات أو معلقة في انتظار قطع ضخمة لتنزيلها. علمت أن تقسيم الرموز جيد، ولكن تحتاج إلى عتبات معقولة. لهذا السبب يستخدم تشكيلي الحالي. minSize: 20000 (20KB).
.mjs المُذْكِر مِم مِم مِ مِ مِ مِ مِ مِ مِ مِ مِ مِ مِ مِ مِ مِ مِ م م م م م م م م م م م م مِ م م م م م م م م م م م م م م م م م م م م م م م م م مِ م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م م مبعض حزم Nnppm توزّع ES6 مع .mjs حزمة ويب لن تحل هذه بالفشل لقد قضيت وقتاً طويلاً مُحرجاً في تصحيح لماذا @mostlring I needed to add مَجْسِسto"تُقرّر." "تُقرّر." "تُقرّر." "تُقرّر. تُقرّر."
إصلاح بسيط بمجرد أن تعرف ذلك، لكن معرفة ذلك؟
أخذت عمليات البناء المبكرة 30-60 ثانية لأنني لم أكن قد تمكنت من تخزين نظام ملفات ويباكس. بمجرد أن أضفت كاشنغ، إعادة بناء الأوقات انخفضت إلى 2-3 ثانية. هذا كان تغيير لعبة لتدفق العمل الإنمائي ولكن استغرق مني أسابيع للاكتشاف.
تجربتي المفضلة في التصحيح: فصول CSS التي عملت بشكل جيد في التنمية فجأة اختفت في الإنتاج. التيلويند كانت تطهرهم لأنهم تم توليدهم بشكل ديناميكي في جافاسكربت. كان الحل هو safelist لكن فقط بعد أن أضعت ساعات أتسائل إن كنت سأجن
أيضاً (على سبيل المثال في mostlylucid.pagingtaghelper project) استخدم 'كتل معدنية' حيث أقوم بتبسيط التغييرات في filewind من خلال مجرد وجود كتلة مخفية.
<!--
Preserve Tailwind & DaisyUI classes used in embedded pager views.
Without this, TailwindCSS tree-shaking removes classes from the embedded library views.
-->
<span class="hidden
btn btn-sm btn-active btn-disabled join join-item badge badge-sm select select-bordered select-sm label label-text
px-3 py-2 py-1 text-sm font-medium border rounded whitespace-nowrap cursor-not-allowed
text-gray-700 text-gray-600 text-gray-400 text-white bg-white bg-gray-100 bg-blue-600 border-gray-300 border-blue-600
hover:bg-gray-50 hover:bg-blue-700
dark:bg-gray-800 dark:bg-gray-700 dark:bg-blue-500 dark:text-gray-300 dark:text-gray-500 dark:text-gray-200
dark:border-gray-600 dark:border-blue-500 dark:hover:bg-gray-700 dark:hover:bg-blue-600">
</span>
عندما يقوم تايلويد ببناءه فإنه يقوم بمسح ملفات cshtml للفصول ، إذا لم يرها فإنه يزيلها. لذلك فإن وجود هذا الخانة المخفية يضمن أنها ترى الفصول التي أحتاجها حتى لو كانت فقط مستخدمة في مشاهدات المكتبة المضمنة. الـ JS المطورون عادة ما يقومون بمسح ملفات JS/TS للسماح باستخدام أساليب CSS التي من الممكن أن يخطئها تايل ويند بطريقة أخرى.
module.exports = {
content: ["./Views/**/*.cshtml", "./EmailSubscription/**/*.cshtml"],
-//^^^^ This is what Tailwing knows where to look for classes duritng the tree Shake.
+//^^^^ This is what Tailwind knows where to look for classes during the tree-shake.
...
future: {...}
}
كما ذُكِرَ، نهج 'Tailwind conception' هو لإضافة الفصول إلى قائمة آمنة... لكنّي رجل ASP.net، لذا فاز HTML.
الشيء الوحيد الذي فعلته كان رفض الاستسلام. كل رسالة خطأ كانت فرصة تعلم. كل مبنى مكسور علمني شيئا جديدا عن كيفية عمل هذه الأدوات. كان لدي رؤية واضحة
الانتقال من المعتمدات المعتمدة على CDN إلى خط أنابيب حديث كان تحويلا لهذه المدونة، لكنه لم يكن سهلا. التحسينات في الأداء كبيرة، تجربة المطور أفضل بكثير، ولدي سيطرة أكبر بكثير على التكميل - ولكن استغرق الأمر شهورا من التجربة والخطأ للوصول إلى هنا.
(أ) الخيارات الرئيسية:
لـ ASP.net الأساسي المطورون عادة إلى بسيط CDN بما في ذلك، هذا سيشعر بالإثارة في البداية. هذا طبيعي. شعرت بنفس الطريقة. ابدأ بالصغير ، هاجر تبعية واحدة في وقت واحد ، ولا تخاف من كسر الأشياء في التطوير. سوف تتعلم من تصحيح البنيات المكسورة أكثر مما سوف تتعلم من قراءة الوثائق.
التشكيلة التي شاركتها هنا تمثل أشهر من التكرار. رحلتك ستكون مختلفة، وهذا جيد. استخدم هذه كنقطة بداية، لا انقلاب. تكيفها مع احتياجاتك، تجربتك، ولا تثبط من الفشل إنها جزء من العملية.
هل هذه مدونه معماريه؟ بالطبع. هل يمكنني الاستمرار في استخدام الأقراص المدمجة وقضاء وقتي في أمور أخرى؟ بالطبع. لكنني لم أكن لأتعلم نصف ما تعلمته، ولم يكن لي هذا المقال لأشارككم به. في بعض الأحيان الهندسة المفرطة لا تتعلق بالوجهة - بل بما تتعلمه على طول الطريق وتكون قادراً على تعليم الآخرين من تلك التجربة.
إذا كنت لا تزال تعتمد على CDNs من أجل الاعتمادات الأمامية الخاصة بك، وأنا أشجعك على إعطاء التأطير تجربة. ابدأ مع مكتبة واحدة. انظر كيف تشعر. قم من هناك. النظام البيئي قد نضج بشكل ملحوظ، وبينما منحنى التعلم حقيقي، فإن العائد يستحق ذلك.
© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.