Back to "Εκσυγχρονισμός μπροστά σας Build αγωγός: από CDNs σε Bundling"

This is a viewer only at the moment see the article on how this works.

To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk

This is a preview from the server running through my markdig pipeline

ASP.NET Frontend Tailwind Webpack

Εκσυγχρονισμός μπροστά σας Build αγωγός: από CDNs σε Bundling

Tuesday, 11 November 2025

Εισαγωγή

Κατά τη διάρκεια του περασμένου έτους, έχω σκοντάψει, πειραματιστεί, σπάσει πράγματα, και σταδιακά εκσυγχρόνισα τον αγωγό κατασκευής του frontend για αυτό το blog. Αυτό δεν ήταν κάποιο masterplan που εκτελέστηκε άψογα ήταν δοκιμή και λάθος, πολλά λάθη, και πεισματάρης επιμονή οδηγείται από ένα σαφές όραμα του τι ήθελα να πετύχω.

Δεν είμαι γκουρού του frontend ή ένας μάγος Webpack. Είμαι ένας προγραμματιστής .NET που απογοητεύτηκε με τους περιορισμούς των εξαρτήσεων με βάση το CDN και αποφάσισε ότι έπρεπε να υπάρξει ένας καλύτερος τρόπος. <script> Ετικέτες που δείχνουν να αποσυνδέονται σε ένα σύγχρονο αγωγό που λειτουργεί πραγματικά.

Έκανα πολλά λάθη κατά μήκος του τρόπου (το οποίο θα τεκμηριώσω), ξόδεψα ώρες αποσφαλμάτωσης κρυπτογραφημένα λάθη Webpack, και πιθανώς να επανεγκαθιστηθεί node_modules Αλλά η επιμονή απέδωσε, και έμαθα ένα τεράστιο ποσό μέσω απόλυτης αποφασιστικότητας για να το κάνω σωστά.

Αν είστε ένας προγραμματιστής .NET κοιτάζοντας τα αρχεία ρυθμίσεων Webpack αναρωτιούνται τι στο καλό έχετε πάρει τον εαυτό σας σε αυτό το άρθρο είναι για σας.

Είμαι ένα παλιό web performance doutter, τις πρώτες ημέρες της κλήσης-up το πρώτο site που έφτιαξα για τα χρήματα (που δεν ήταν ένα πορνό backend στο Perl... μια ιστορία για μια άλλη φορά) ήταν ένα site Snow Conditions που έφυγε από μια αυτόματη τηλεφωνική γραμμή.

Εκείνες τις μέρες οι ανησυχίες ήταν αρκετά διαφορετικές. JavaScript ήταν πρακτικά άγνωστο. div Και Netscape ήταν layer) και θα ήσασταν τυχεροί αν οι χρήστες σας είχαν μια σύνδεση 56K. Έτσι έμαθα να βελτιστοποιεί (ακόμη και πράγματα όπως πλακάκια φόντο εικόνας για τα στοιχεία του μενού) κλπ...

Αργότερα στη Microsoft δούλεψα ακόμη και σε ένα αυτοματοποιημένο εργαλείο sprite εικόνας σε Web Forms - ήταν φοβερό: έβγαλε μικρές εικόνες από μια σελίδα, δημιούργησε CSS και μια εικόνα sprite αυτόματα. Αλλά αλίμονο, όπως ο χρόνος μου στη Microsoft, δεν ήταν να είναι.

Εν ολίγοις, είναι μια εμμονή που έχω κουβαλήσει όλη μου την καριέρα.

Για να είμαστε δίκαιοι., CDNs δεν είναι εγγενώς κακό.

Πλήρης γνωστοποίηση: Αυτό το blog είναι σκόπιμα υπερσχεδιασμένη. ανάγκη Αλλά το να το οικοδομήσουμε με αυτόν τον τρόπο επιτρέψτε μου να μάθω το σύγχρονο frontend εργαλεία σωστά και, κρίσιμα, μου έδωσε κάτι πραγματικό για να γράψω και να διδάξω. Μερικές φορές ο καλύτερος τρόπος για να μάθω είναι να χτίσω κάτι ελαφρώς γελοίο και να τεκμηριώσω το ταξίδι.

Το πρόβλημα με τα 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">

Ενώ αυτό λειτουργεί, έχει αρκετά μειονεκτήματα που γίνονται όλο και πιο απογοητευτικά:

  • Θέματα αξιοπιστίαςΈχω δει unpkg και jsdelivr έχουν διακοπές. Όταν είναι κάτω, το site σας είναι σπασμένο
  • Απρόβλεπτη συμπεριφορά φόρτωσηςΜερικές φορές γρήγορα, μερικές φορές αργά, μερικές φορές εισάγουν συνθήκες αγώνα όπου βιβλιοθήκες φορτώνουν σε λάθος σειρά
  • Θέματα συγχρονισμούΑυτό μου προκάλεσε ατελείωτους πονοκεφάλους με αρχικοποίηση βιβλιοθήκης
  • Πολλαπλές αιτήσεις HTTP: Κάθε βιβλιοθήκη απαιτεί ξεχωριστό αίτημα, ακόμη και με HTTP/2
  • Χωρίς αναταράξεις δέντρων: Μπορείτε να κατεβάσετε ολόκληρη τη βιβλιοθήκη, ακόμη και αν χρησιμοποιείτε μόνο ένα κλάσμα της
  • Μετατόπιση εκδοχής: Τα CDNs μπορεί να εξυπηρετούν διαφορετικές εκδόσεις εκτός αν τις καρφώσετε ρητά, και ακόμη και καρφιτσωμένες εκδόσεις μπορούν να συμπεριφέρονται διαφορετικά σε όλους τους παρόχους CDN
  • Αβεβαιότητα χρόνου κατασκευής: Δεν υπάρχει τρόπος να πιάσει ασυμβατότητες μέχρι runtime συχνά όταν ένας χρήστης το αναφέρει.
  • Περιορισμένη βελτιστοποίηση: Δεν μπορεί να εξουδετερώσει πέρα από τα όρια ή να αφαιρέσει τον νεκρό κώδικα
  • Offline ανάπτυξη: Απαιτεί σύνδεση στο διαδίκτυο, η οποία είναι ενοχλητική κατά την εργασία σε τρένα ή αεροπλάνα

Έτσι ήθελα τον έλεγχο... τον έλεγχο του τι ακριβώς χρησιμοποιούσε η ιστοσελίδα μου και έπρεπε να λειτουργήσει. Ήθελα να πακετάρω μόνο αυτό που χρειαζόμουν, να εξασφαλίσω αξιόπιστη εντολή φόρτωσης και να βελτιστοποιήσω την απόδοση.

Είναι απλούστερο να συσταθεί, και με HTTP/2 και HTTP/3 πολλαπλοποίηση, το πολλαπλάσιο αίτημα πάνω από το κεφάλι είναι πολύ λιγότερο κρίσιμη από ό, τι ήταν. Για πολλά έργα, ειδικά μικρά ή πρωτότυπα, CDNs παραμένουν μια απολύτως λογική επιλογή.

Η Σύγχρονη Πλησιά

Το τρέχον πακέτο ρυθμίσεων μου περιλαμβάνει όλες τις εξαρτήσεις JavaScript μέσω του Webpack και επεξεργάζεται CSS μέσω PostCSS.

Γιατί το Webpack αντί για Vite;

Πριν βουτήξω στις τεχνικές λεπτομέρειες, πρέπει να απευθυνθώ στον ελέφαντα στο δωμάτιο: γιατί χρησιμοποιώ Webpack όταν υπάρχουν Vite, Rollup, esbuilders και άλλοι σύγχρονοι πακέτοι;

Η ειλικρινής απάντηση είναι απλή: Ήξερα ήδη Webpack.

Όταν αποφάσισα να εκσυγχρονίσω τον αγωγό κατασκευής αυτού του blog, ήδη αντιμετώπιζα μια απότομη καμπύλη μάθησης που έβλεπα την ταλάντωση των δέντρων, τον διαχωρισμό των κωδικών, τα συστήματα ενότητας, τους αγωγούς PostCSS, και πώς να τα ενσωματώσω όλα αυτά με το ASP.NET Core.

Είναι γενικά μια καλή πρακτική κατά την εργασία σε έργα; περιορίζουν το "νέο" στο "χειριστήριο." Είναι τρόπος πιο εύκολο να περιορίσει την αβεβαιότητα.

Προσθέτοντας "μαθαίνω ένα εντελώς νέο εργαλείο κατασκευής" πάνω από αυτό φαινόταν περιττό. Webpack λειτουργεί. Είναι ώριμο. Έχει εξαιρετική τεκμηρίωση και ένα τεράστιο οικοσύστημα. έννοιες της σύγχρονης συσσώρευσης και όχι των ιδιοσυγκρασιών ενός συγκεκριμένου εργαλείου.

Ο Βάιτ είναι πιο γρήγορος; Απόλυτα. Διακομιστής ανάπτυξης Vite με εγγενείς μονάδες ES και esbuild-powered bundling είναι σημαντικά ταχύτερη από Webpack. Για μεγάλα έργα με εκατοντάδες ενότητες, η διαφορά είναι δραματική.

Να χρησιμοποιήσεις το Βάιτ για ένα νέο έργο; Πιθανότατα, ναι. Αν ξεκινάτε φρέσκα και δεν έχετε υπάρχουσες γνώσεις Webpack, το Vite είναι πιθανότατα η καλύτερη επιλογή. Είναι πιο γρήγορο, απλούστερο να ρυθμίσετε, και αντιπροσωπεύει τη σύγχρονη προσέγγιση για τα εργαλεία frontend.

Μετανιώνω που χρησιμοποιώ το Webpack; Και ειλικρινά, για την κλίμακα αυτού του blog, η διαφορά απόδοσης μεταξύ του Webpack και του Vite είναι αμελητέα. Μιλάμε για χιλιοστά του δευτερολέπτου στην ανάπτυξη ξαναχτίσει χρόνους.

Είναι μια βασική πτυχή του πώς φτιάχνω πράγματα; Ξεκινήστε με αυτό που είναι εύκολο και χτίστε από αυτό το ίδρυμα.

Το ευρύτερο μάθημα εδώ είναι ότι Η πρόοδος κερδίζει την τελειότητα. Θα μπορούσα να είχα περάσει εβδομάδες ερευνώντας το "καλύτερο" πακέτο, συγκρίνοντας σημεία αναφοράς, διαβάζοντας άρθρα σύγκρισης, και αγωνίζονται πάνω από την απόφαση. Αντ 'αυτού, διάλεξα το εργαλείο που ήξερα, πήρα το έργο, και προχώρησε προς τα εμπρός.

Όπως και να 'χει, αυτό το blog έχει έναν σύγχρονο, βελτιστοποιημένο αγωγό κατασκευής που λειτουργεί αξιόπιστα και αυτό είναι που έχει σημασία.

Δομή συσκευασίας

Η package.json Τώρα διατηρεί δύο διαφορετικές ομάδες εξάρτησης. Αυτή η δομή μου πήρε αρκετές προσπάθειες για να πάρετε το δικαίωμα από την αρχή, Nuget και npm είναι παρόμοια kinda αλλά nppm είναι πολύ λιγότερο φιλικό κατά την προσθήκη packges.

{
  "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 κ.λπ.), ενώ devD εξαρτήσεις είναι εργαλεία κατασκευής (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
  • αντίγραφο:* εργασίες: Αντίγραφα στατικά αρχεία CSS που δεν χρειάζονται επεξεργασία (αυτά είναι είτε "εισαγωγές") για έναν ειδικό σκοπό όπως η φόρτωση funky γραμματοσειρά Raleway μου ή προσαρμοστικά αυτά, όπως η αλλαγή θέματος βελτιώσεις.
  • tw:* εργασίες: Διαδικασίες Tailwind CSS μέσω PostCSS
  • js:* εργασίες: Bundles JavaScript μέσω του Webpack
  • dev/watch/building: Ορχήστρα ολόκληρος ο αγωγός

Η 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 σας...Μπορείτε ακόμη και να πείτε τρέξει μόνο κατά τη διάρκεια debug' κ.λπ...αλλά το βρίσκω βρώμικο. Προτιμώ απλά να το τρέξει χειροκίνητα. npm run watch κάνει ακριβώς αυτό, όταν οποιοδήποτε css / js αρχείο αλλάζει το auto-runs την κατασκευή.

ΣΗΜΕΙΩΣΗ: Στον κόσμο των JS σχεδόν χρησιμοποιούν το hot-reload dev runs το οποίο είναι αρκετά επιδέξιο και σας κάνει να μισείτε κάπως την αδύναμη προσπάθεια του ASP.NET Core.

Webpack Ρύθμιση Βαθιά κατάδυση

Η ρύθμιση του Webpack είναι όπου η μαγεία συμβαίνει και όπου πέρασα το μεγαλύτερο μέρος του χρόνου μου αντιμετώπιση προβλημάτων. Αυτό δεν έρχεται σε ύπαρξη πλήρως σχηματισμένο. Είναι το αποτέλεσμα αμέτρητες λειτουργίες, Stack Overflow αναζητήσεις, και διαβάζοντας μέσω της τεκμηρίωσης Webpack στις 2πμ προσπαθώντας να καταλάβουν γιατί η κατασκευή μου παράγει 47 αρχεία chunk.

Εδώ είναι η τρέχουσα διαμόρφωση (από 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,
        }
    };
};

Εξηγήθηκαν τα βασικά τμήματα ρύθμισης

Κωδικός Splitting και Chunking

optimization: {
    splitChunks: {
        chunks: 'all',
        minSize: 20000,
        maxSize: 100000,
        name: false,
    },
    runtimeChunk: {
        name: 'runtime',
    },
}

Αυτή η διαμόρφωση διαχωρίζει αυτόματα τον κωδικό σας σε μικρότερα κομμάτια:

  • Κομμάτια: 'όλα': Αναλύει τόσο τις σύγχρονες όσο και τις ασύγχρονες εισαγωγές
  • minΜέγεθος: 20000: Δημιουργεί μόνο κομμάτια για ενότητες μεγαλύτερες από 20KB
  • maxSize: 100000: Προσπαθεί να χωρίσει κομμάτια μεγαλύτερα από 100KB
  • runtimeChunk: Απόσπασμα Webpack runtime σε ένα ξεχωριστό αρχείο για τη βελτίωση της μακροπρόθεσμης caching

Στην πράξη, αυτό δημιουργεί πολλαπλά αρχεία σε wwwroot/js/dist/:

  • main.js - Το σημείο εισόδου της αίτησης σας
  • runtime.js - Webpack module loading logic
  • [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,
                }],
            ],
        },
    },
}

Babel transpiles modern JavaScript to support παλαιότερα προγράμματα περιήγησης:

  • Στόχοι: Υποστηρίζει προγράμματα περιήγησης με ποσοστό > 0,25% της αγοράς που εξακολουθούν να διατηρούνται
  • ενότητες: ψευδείς: Διατηρεί μονάδες ES6 για Webpack δέντρο-ταλάντευση
  • useBuiltIns: 'usage': Αυτόματη περιλαμβάνει πολυπλήρεις μόνο για χαρακτηριστικά που χρησιμοποιείτε
  • corejs: 3: Χρησιμοποιεί την έκδοση 3 core-js για πολυπλήρεις

Αυτό σημαίνει ότι μπορώ να γράψω το σύγχρονο JavaScript (async/wait, προαιρετικό αλυσοπρίονο, nullish coalescing) διατηρώντας παράλληλα την ευρεία υποστήριξη του προγράμματος περιήγησης.

Ελαχιστοποίηση της παραγωγής

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

Ο Τέρσερ εξορύσσει επιθετικά την παραγωγή:

  • drop_console: Απομακρύνει όλα console.log() δηλώσεις
  • περάσει: 3: Λειτουργεί συμπίεση τρεις φορές για μέγιστη μείωση μεγέθους
  • top level: true: Mangles top-level variable names
  • pure_funcs: Απομακρύνει συγκεκριμένες μεθόδους κονσόλας ακόμα και αν έχουν αποδοθεί σε μεταβλητές
  • Σχόλια: ψευδείς: Απογυμνώνει όλα τα σχόλια από την έξοδο

Σε αυτό το blog, αυτό συνήθως μειώνει τα μεγέθη των πακέτων JavaScript κατά 60-70% σε σύγκριση με τον ανίκητο κώδικα.

PostCSS αγωγός για τον ανεμοστρόβιλο

Tailwind CSS γίνεται επεξεργασία μέσω PostCSS με διάφορα πρόσθετα. Αυτή η ρύθμιση (από postcss.config.js) είναι ελεήμων απλό σε σύγκριση με Webpack:

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

Ο αγωγός λειτουργεί ως εξής:

  1. postcss-import: Επιλύει @import δηλώσεις στα αρχεία CSS
  2. wheelwinccs: Processes Tailwind οδηγίες και δημιουργεί κατηγορίες χρησιμότητας
  3. autoprofixerName: Προσθέτει τα προθέματα του προμηθευτή για τη συμβατότητα του προγράμματος περιήγησης
  4. csnano: Ελαχιστοποιεί την τελική έξοδο CSS

Ρύθμιση ανέμου ουράς

Η tailwind.config.js αρχείο προσδιορίζει πού Tailwind πρέπει να ψάξει για ονόματα κλάσης. content Τα μονοπάτια δεξιά ήταν κρίσιμης σημασίας χάνει ένα μονοπάτι και ο 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 προβολές και πρότυπα ηλεκτρονικού ταχυδρομείου)
  • ασφαλής λίστα: Πάντα περιλαμβάνει αυτές τις κατηγορίες ακόμα και αν δεν βρίσκονται σε σαρώσεις
  • darkMode: "class": Ενεργοποιεί μεταγωγής σκοτεινής λειτουργίας με βάση την τάξη
  • θέμα.extend: Προσθέτει προσαρμοσμένες τιμές χρωμάτων και διαπόστασης
  • Πρόσθετα: Περιλαμβάνει plugins Tailwind και βιβλιοθήκη συστατικών DaisyUI

Ο ανεμοστρόβιλος σαρώνει αυτά τα αρχεία κατά την οικοδόμηση του χρόνου, εξάγοντας μόνο τις κατηγορίες χρησιμότητας που πραγματικά χρησιμοποιείτε. Γι 'αυτό το τελικό αρχείο CSS είναι πολύ μικρότερο από την πλήρη βιβλιοθήκη Tailwind.

Σημείο εισόδου JavaScript

Η main.js Αυτό το αρχείο εισάγει και αρχικοποιεί όλες τις εξαρτήσεις, και καλλιεργείται οργανικά καθώς έχω προσθέσει χαρακτηριστικά στο blog:

// 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. Ζητητές εκδόσεις: Package.json κλειδώνει συγκεκριμένες εκδόσεις
  3. Ταλάντευση δέντρων: Webpack αφαιρεί τον αχρησιμοποίητο κώδικα από τις βιβλιοθήκες
  4. Συνολική έκθεση: Βιβλιοθήκες διαθέσιμες στο Razor views μέσω 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>

Σημείωση I ορίζει module για να ενημερώσετε το πρόγραμμα περιήγησης τι τύπο αρχείου JS αυτό είναι & asp-append-version ένα τακτοποιημένο μικρό ASP.NET tag helper που προσθέτει μια hashed έκδοση του περιεχομένου του αρχείου στο querystring; αποτελεσματικά cache-busting όταν το αρχείο αλλάζει.

Οι μόνες εξαρτήσεις CDN που απομένουν είναι:

  • Σύνδεση GoogleName: Απαιτεί εξωτερικό CDN για λειτουργικότητα OAuth
  • ΜπόξεικονςCity name (optional, probably does not need a translation): Γραμματοσειρά εικονιδίων (μπορεί να είναι δεμένη, αλλά ελάχιστη ωφέλεια)
  • Umami Analytics: Τρίτο-party analytics script - self hosted so I get to see your visit not 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, χειρίζεται το μεγαλύτερο μέρος αυτού του ίδιου (με έναν περίπλοκο τρόπο που οι όμοιοι του Vite δεν χρειάζονται αλλά...)

Βελτιώσεις των επιδόσεων

Μπορείτε ακόμα να πάρετε θέματα με Cloudflare 'Rocket Loader' (το οποίο mangles σελίδα φορτώνει γεγονότα κάπως) αλλά είναι σφιχτό.

Οφέλη από την εμπειρία του προγραμματιστή

Πέρα από τις επιδόσεις, ο σύγχρονος αγωγός κατασκευής βελτιώνει σημαντικά τη ροή των έργων ανάπτυξης:

Τύπος Ασφάλεια και IntelliSense

Bundling μέσω Webpack επιτρέπει την κατάλληλη ανάλυση ενότητας JavaScript, που σημαίνει:

  • IntelliSense in VS Code: Auto-completion for imported modules
  • Μετάβαση στον ορισμό: Πλοήγηση απευθείας στον πηγαίο κώδικα βιβλιοθήκης
  • Inline τεκμηρίωση: JSDoc σχόλια από βιβλιοθήκες εμφανίζονται στο IDE

ΟΤΑΝ η κατασκευή σπάσει (με npm run watch) I know INSTANTLY, combined with the (few but growning) unit tests it means higher feedback loops.

Αντικατάσταση θερμής ενότητας

Κατά τη διάρκεια της ανάπτυξης, npm run watch επιτρέπει την άμεση ανατροφοδότηση:

npm run watch

Αυτό τρέχει Webpack σε λειτουργία ρολόι, ανοικοδόμηση μόνο αλλάξει ενότητες:

  • Τυπικός χρόνος ανοικοδόμησης: 200-400ms
  • Browser auto-refresh: Χρήση του browser-sync ή παρόμοια εργαλεία
  • Διατηρημένη κατάσταση εφαρμογής: HMR διατηρεί κατάσταση εφαρμογής κατά τη διάρκεια των ενημερώσεων (όπου υποστηρίζεται)

Διαχείριση της εξάρτησης

Αρχείο κλειδώματος npm (package-lock.json) εξασφαλίζει συνεπή οικοδομήματα σε όλα τα περιβάλλοντα:

npm ci  # Clean install from lock file

Αυτό εγγυάται τις ίδιες εκδόσεις εξάρτησης σε περιβάλλοντα ανάπτυξης, CI/CD και παραγωγής.

Μπορείτε να κάνετε χωρίς κλειδαριά πακέτου, αλλά στη συνέχεια είστε ανάλογα με τους συγγραφείς pakakge; συχνά 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

Κοινά Πρότυπα και Λύσεις

Εκθέτοντας Bundled Βιβλιοθήκες σε παγκόσμιο επίπεδο

Ορισμένες βιβλιοθήκες χρειάζονται παγκόσμια έκθεση για χρήση σε Razor απόψεις ή inline σενάρια:

// 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 αυτόματα code-splits δυναμικές εισαγωγές σε ξεχωριστά κομμάτια, φορτωμένο κατά παραγγελία.

Αντιμετωπίζοντας τις Ενότητες του Κοινού JS

Ορισμένες παλαιότερες βιβλιοθήκες χρησιμοποιούν CommonJS αντί για μονάδες ES6:

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

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

Το Webpack χειρίζεται και τα δύο συστήματα module με διαφάνεια, μετατρέποντας το Common JS σε ES6 όπου χρειάζεται.

Αντιμετώπιση των κοινών ζητημάτων

Χάρτες Πηγής Λείπει στην Ανάπτυξη

Εξασφάλιση devtool είναι ρυθμισμένο σε webpack.config.js:

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

Αυτό δημιουργεί χάρτες πηγής στην ανάπτυξη για ευκολότερη αποσφαλμάτωση.

CSS Class Purting Issues with Tailwind

Αν ο Tailwind αφαιρεί τα μαθήματα που χρησιμοποιείτε, ελέγξτε το 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 apos? S cache σημαντικά επιταχύνει τις ανοικοδομήσεις
  2. Μείωση του πεδίου εφαρμογής της Βαβέλ: Αποκλείστε περισσότερους καταλόγους από την επεξεργασία Babel
  3. Εργαλεία αναβάθμισης: Οι νεότερες εκδόσεις του Webpack και Babel είναι γρηγορότερες
  4. Παράλληλες κατασκευές: Χρήση thread-loader για πολύστρωση με αλλεπάλληλες στρώσεις
// Enable Webpack caching
cache: {
    type: 'filesystem',
},

Μαθήματα Έμαθαν τον Δύσκολο Τρόπο

Επιτρέψτε μου να μοιραστώ μερικά από τα λάθη που έκανα κατά τη διάρκεια αυτού του ταξιδιού, ώστε να μπορείτε να τα αποφύγετε:

Λάθος #1: Προσπαθώντας να λυγίσει τα πάντα με τη μία

Η πρώτη μου προσπάθεια ήταν να ξεριζώσω όλες τις αναφορές CDN με τη μία και να προσπαθήσω να διασυνδέσω τα πάντα μέσω του Webpack. Η κατασκευή έσπασε θεαματικά. Έμαθα ότι η αυξανόμενη μετανάστευση είναι φίλος σας μετακινήστε τη μία βιβλιοθήκη τη φορά, δοκιμάστε προσεκτικά, και στη συνέχεια μετακινηθείτε στην επόμενη.

Λάθος #2: Μη κατανόηση τύπων ενότητας

Σπατάλησα ώρες προσπαθώντας να καταλάβω γιατί ορισμένες βιβλιοθήκες δεν θα φορτώσει.require()) και ενότητες ES6 (import) χωρίς να καταλαβαίνετε πώς Webpack τους χειρίζεται οδηγεί σε αινιγματικά λάθη. experiments: { outputModule: true } Η διαμόρφωση δεν ήταν στην αρχική μου εγκατάσταση, και δεν μπορούσα να καταλάβω γιατί οι ενότητες μου δεν φόρτωναν.

Η λύση ήρθε από την ανάγνωση μέσω των θεμάτων GitHub στο αρχείο Webpack τα μεσάνυχτα.

Λάθος #3: Υπερεπιθετική διαίρεση κώδικα

Σε κάποιο σημείο, η διαμόρφωσή μου δημιουργούσε κομμάτια για τα πάντα. minSize: 10000 (10KB) που σήμαινε Webpack ήταν η δημιουργία ξεχωριστών αρχείων για μικροσκοπικές λειτουργίες χρησιμότητας. Η υπερδιορθωμένη και πήγε σε 2MB... Page φορτίο έγινε καταρράκτης των 50+ μικροσκοπικά αιτήματα κομμάτια ή κρεμάστηκε περιμένοντας τεράστια κομμάτια για να κατεβάσετε. Έμαθα ότι ο διαχωρισμός κώδικα είναι καλός, αλλά χρειάζεστε λογικά όρια. minSize: 20000 (20KB).

Λάθος #4: Ξεχνάω για το .mjs Επέκταση

Ορισμένα πακέτα npm διανείμουν τις μονάδες ES6 με την .mjs επέκταση. Webpack δεν θα λύσει αυτά από προεπιλογή. Ξόδεψα ένα ντροπιαστικά μεγάλο χρονικό διάστημα αποσφαλμάτωσης γιατί @mostlring I needed to add .mjsunit synonyms for matching user inputtoΛύση. Επιμηκύνει.

Απλή διόρθωση μόλις το καταλάβεις, αλλά το ανακάλυψες;

Λάθος #5: Δεν Caching Webpack Builds

Οι πρώτες κατασκευές χρειάστηκαν 30-60 δευτερόλεπτα επειδή δεν είχα ενεργοποιήσει το αρχείο σύστημα μνήμης του Webpack. Μόλις πρόσθεσα caching, οι χρόνοι ανοικοδόμησης έπεσαν σε 2-3 δευτερόλεπτα.

Λάθος #6: Τάξη Εκτροπής Ανεμοστρόβιλου που Στην πραγματικότητα χρησιμοποίησα

Η αγαπημένη μου εμπειρία αποσφαλμάτωσης: τα μαθήματα CSS που δούλεψαν καλά στην ανάπτυξη ξαφνικά εξαφανίστηκαν στην παραγωγή. safelist διαμόρφωση, αλλά μόνο αφού έχασα ώρες αναρωτιόμουν αν τρελαινόμουν. I ΕΠΙΣΗΣ (π.χ. στην mostlylucid.pagingtaghelper project) χρησιμοποιήστε τα "κουτσομπολιά" όπου απλοποιώ τις αλλαγές μου με το να έχω μόνο ένα κρυφό μπλοκ.


<!--
    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 στυλ που Tailwind διαφορετικά θα αστοχήσει.


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 εγκριθεί" είναι να προσθέσετε τα μαθήματα σε μια ασφαλή λίστα... αλλά είμαι ένας τύπος ASP.NET, έτσι HTML κέρδισε.

Αυτό που έχω δίκιο: επιμονή

Κάθε σπασμένο οικοδόμημα μου δίδαξε κάτι νέο σχετικά με το πώς λειτουργούν αυτά τα εργαλεία. Είχα ένα σαφές όραμα regulated, βελτιστοποιήστε τα περιουσιακά στοιχεία που φορτώνουν γρήγορα ? και συνέχισα να πραγματοποιείται μέχρι που έφτασα εκεί.

Συμπέρασμα

Οι βελτιώσεις των επιδόσεων είναι σημαντικές, η εμπειρία του προγραμματιστή είναι σημαντικά καλύτερη, και έχω πολύ μεγαλύτερο έλεγχο της βελτιστοποίησης του αγωγού, αλλά χρειάστηκαν μήνες δοκιμής και λάθους για να φτάσουμε εδώ.

Κλειδί των πακέτων:

  1. Η κάμψη μειώνει το μέγεθος του ωφέλιμου φορτίου: Η χειραγώγηση δέντρων και η αποτρίχωση εξαλείφουν τον αχρησιμοποίητο κωδικό
  2. Η διαίρεση κώδικα βελτιώνει τις επιδόσεις: Περιηγητές φορτώνουν μόνο ό, τι χρειάζεται
  3. Εργαλεία κατασκευής ενεργοποιούν το σύγχρονο JavaScript: Η μεταφορά Babel υποστηρίζει τα πιο πρόσφατα γλωσσικά χαρακτηριστικά διατηρώντας παράλληλα τη συμβατότητα του προγράμματος περιήγησης
  4. Ο αγωγός PostCSS βελτιστοποιεί την CSS: Tailwind's JIT μεταγλωττιστής και cssnano παραδίδει μικροσκοπικά φύλλα στυλ
  5. Η ενσωμάτωση με το NET είναι απρόσκοπτη: Οι στόχοι MSBbuild τρέχουν αυτόματα frontend χτίζει
  6. Η επιμονή κερδίζει την τελειότητα: You don't need to be a frontend expert

Για ASP.NET Core προγραμματιστές που είναι συνηθισμένοι σε απλά CDN περιλαμβάνει, αυτό θα αισθάνεται συντριπτική στην αρχή. Αυτό είναι φυσιολογικό. Ένιωσα με τον ίδιο τρόπο. Ξεκινήστε μικρά, μεταναστεύουν μία εξάρτηση κάθε φορά, και μην φοβάστε να σπάσει τα πράγματα στην ανάπτυξη. Θα μάθετε περισσότερα από αποσφαλμάτωση σπασμένο κτίρια από ό, τι ποτέ θα από την ανάγνωση τεκμηρίωσης.

Το ταξίδι σας θα είναι διαφορετικό, και αυτό είναι καλό. Χρησιμοποιήστε αυτό ως σημείο εκκίνησης, όχι το ευαγγέλιο. Προσαρμόστε το στις ανάγκες σας, πειραματιστείτε, και μην αποθαρρύνεστε από τις αποτυχίες είναι μέρος της διαδικασίας.

Αυτό το μπλογκ είναι υπερσχεδιασμένο; Φυσικά. Θα μπορούσα να έχω κρατήσει τη χρήση CDNs και ξόδεψα το χρόνο μου σε άλλα πράγματα; Σίγουρα. Αλλά δεν θα είχα μάθει το μισό τόσο πολύ, και δεν θα είχα αυτό το άρθρο για να μοιραστώ μαζί σας. Μερικές φορές η υπερμηχανική δεν είναι για τον προορισμό είναι για το τι μαθαίνετε κατά μήκος του τρόπου και να είναι σε θέση να διδάξει τους άλλους από αυτή την εμπειρία.

Αν εξακολουθείτε να βασίζεστε σε CDNs για τις εξαρτήσεις σας στο frontend, σας ενθαρρύνω να δώσετε bundling μια προσπάθεια. Ξεκινήστε με μια βιβλιοθήκη. Δείτε πώς αισθάνεται από εκεί. Το οικοσύστημα έχει ωριμάσει σημαντικά, και ενώ η καμπύλη μάθησης είναι πραγματική, η αποπληρωμή αξίζει τον κόπο.

Περαιτέρω Ανάγνωση

logo

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