Bundle optimization to kompleksowy proces analizy i optymalizacji pakietów JavaScript, mający na celu maksymalne zmniejszenie rozmiaru, poprawę wydajności ładowania oraz efektywne wykorzystanie zasobów przeglądarki. To ostatni krok w procesie optymalizacji aplikacji webowych - gdy mamy już code splitting, tree shaking i lazy loading, przychodzi czas na głęboką analizę i fine-tuning całego bundle.
1// Metryki wydajności przed optymalizacją
2const beforeOptimization = {
3 bundleSize: '2.8MB',
4 firstContentfulPaint: '3.4s',
5 largestContentfulPaint: '5.1s',
6 timeToInteractive: '6.2s',
7 cumulativeLayoutShift: 0.25
8};
9
10// Metryki wydajności po optymalizacji
11const afterOptimization = {
12 bundleSize: '420KB',
13 firstContentfulPaint: '0.9s',
14 largestContentfulPaint: '1.6s',
15 timeToInteractive: '2.1s',
16 cumulativeLayoutShift: 0.05
17};
18
19// Poprawa wydajności
20const improvement = {
21 bundleSizeReduction: '85%',
22 fcpImprovement: '73%',
23 lcpImprovement: '69%',
24 ttiImprovement: '66%',
25 clsImprovement: '80%'
26};1// Analiza kosztów business impact
2const businessImpact = {
3 conversionRate: {
4 before: '2.1%',
5 after: '3.8%',
6 improvement: '+81%'
7 },
8 bounceRate: {
9 before: '68%',
10 after: '42%',
11 improvement: '-38%'
12 },
13 mobileUsersLost: {
14 before: '35%', // Przez wolne ładowanie
15 after: '12%',
16 improvement: '-66%'
17 },
18 revenueImpact: {
19 monthlyLoss: '$24,000', // Z powodu wolnych bundli
20 monthlyGain: '$45,000' // Po optymalizacji
21 }
22};1# Instalacja i użycie
2npm install --save-dev webpack-bundle-analyzer
3
4# Generowanie raportu
5npx webpack-bundle-analyzer dist/static/js/*.js
6
7# Integracja z webpack config
8const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
9
10module.exports = {
11 plugins: [
12 new BundleAnalyzerPlugin({
13 analyzerMode: 'static',
14 openAnalyzer: false,
15 generateStatsFile: true,
16 reportFilename: 'bundle-report.html'
17 })
18 ]
19};1# .github/workflows/bundle-analysis.yml
2name: Bundle Analysis
3on: [push, pull_request]
4
5jobs:
6 analyze:
7 runs-on: ubuntu-latest
8 steps:
9 - uses: actions/checkout@v2
10
11 - name: Setup Node.js
12 uses: actions/setup-node@v2
13 with:
14 node-version: '18'
15
16 - name: Install dependencies
17 run: npm ci
18
19 - name: Build and analyze
20 run: |
21 npm run build
22 npx bundlesize
23
24 - name: Upload bundle stats
25 uses: actions/upload-artifact@v2
26 with:
27 name: bundle-stats
28 path: dist/bundle-stats.json1# Analiza source map
2npm install --global source-map-explorer
3
4# Analiza konkretnego bundle
5source-map-explorer bundle.js bundle.js.map
6
7# Porównanie dwóch wersji
8source-map-explorer bundle.js --compare-to=bundle-old.js1// Interaktywny dashboard podczas development
2const DashboardPlugin = require('webpack-dashboard/plugin');
3
4module.exports = {
5 plugins: [
6 new DashboardPlugin()
7 ]
8};1// webpack.config.js - Zaawansowana konfiguracja chunks
2module.exports = {
3 optimization: {
4 splitChunks: {
5 chunks: 'all',
6 minSize: 20000,
7 maxSize: 244000,
8 cacheGroups: {
9 // Framework chunks (React, Angular, Vue)
10 framework: {
11 test: /[\/]node_modules[\/](react|react-dom|@angular|vue)[\/]/,
12 name: 'framework',
13 priority: 40,
14 enforce: true
15 },
16
17 // UI Libraries chunk
18 ui: {
19 test: /[\/]node_modules[\/](@mui|antd|bootstrap|@chakra-ui)[\/]/,
20 name: 'ui-libs',
21 priority: 30,
22 enforce: true
23 },
24
25 // Utility libraries chunk
26 utils: {
27 test: /[\/]node_modules[\/](lodash|ramda|date-fns|moment)[\/]/,
28 name: 'utils',
29 priority: 20,
30 enforce: true
31 },
32
33 // Common application code
34 common: {
35 name: 'common',
36 minChunks: 2,
37 priority: 10,
38 reuseExistingChunk: true
39 },
40
41 // Large libraries w osobnych chunkach
42 largeDeps: {
43 test: /[\/]node_modules[\/](monaco-editor|three|d3)[\/]/,
44 name(module) {
45 const packageName = module.context.match(/[\/]node_modules[\/](.*?)([\/]|$)/)[1];
46 return `lib.${packageName.replace('@', '')}`;
47 },
48 priority: 35,
49 enforce: true
50 }
51 }
52 }
53 }
54};1// webpack.config.js - Host application
2const ModuleFederationPlugin = require('@module-federation/webpack');
3
4module.exports = {
5 plugins: [
6 new ModuleFederationPlugin({
7 name: 'host',
8 filename: 'remoteEntry.js',
9 remotes: {
10 userDashboard: 'userDashboard@http://localhost:3001/remoteEntry.js',
11 productCatalog: 'productCatalog@http://localhost:3002/remoteEntry.js',
12 paymentSystem: 'paymentSystem@http://localhost:3003/remoteEntry.js'
13 },
14 shared: {
15 react: {
16 singleton: true,
17 requiredVersion: '^18.0.0',
18 eager: true
19 },
20 'react-dom': {
21 singleton: true,
22 requiredVersion: '^18.0.0',
23 eager: true
24 }
25 }
26 })
27 ]
28};
29
30// Micro-frontend - User Dashboard
31const ModuleFederationPlugin = require('@module-federation/webpack');
32
33module.exports = {
34 plugins: [
35 new ModuleFederationPlugin({
36 name: 'userDashboard',
37 filename: 'remoteEntry.js',
38 exposes: {
39 './UserProfile': './src/components/UserProfile',
40 './UserSettings': './src/components/UserSettings'
41 },
42 shared: {
43 react: { singleton: true },
44 'react-dom': { singleton: true }
45 }
46 })
47 ]
48};1// Webpack cache configuration dla szybszych rebuildów
2module.exports = {
3 cache: {
4 type: 'filesystem',
5 cacheDirectory: path.resolve(__dirname, '.webpack-cache'),
6 buildDependencies: {
7 config: [__filename],
8 tsconfig: [path.resolve(__dirname, 'tsconfig.json')],
9 packagejson: [path.resolve(__dirname, 'package.json')]
10 },
11 version: '1.0',
12 store: 'pack',
13 compression: 'gzip'
14 },
15
16 // Persistent cache dla node_modules
17 snapshot: {
18 managedPaths: [path.resolve(__dirname, 'node_modules')],
19 buildDependencies: {
20 hash: true,
21 timestamp: true
22 },
23 module: {
24 timestamp: true
25 },
26 resolve: {
27 timestamp: true
28 }
29 }
30};1// bundlesize configuration w package.json
2{
3 "bundlesize": [
4 {
5 "path": "./dist/js/main.*.js",
6 "maxSize": "170 kB",
7 "compression": "gzip"
8 },
9 {
10 "path": "./dist/js/vendor.*.js",
11 "maxSize": "300 kB",
12 "compression": "gzip"
13 },
14 {
15 "path": "./dist/css/*.css",
16 "maxSize": "50 kB",
17 "compression": "gzip"
18 },
19 {
20 "path": "./dist/js/runtime.*.js",
21 "maxSize": "10 kB",
22 "compression": "gzip"
23 }
24 ]
25}1// Performance monitoring service
2class BundlePerformanceMonitor {
3 constructor() {
4 this.metrics = {
5 bundleSizes: new Map(),
6 loadTimes: new Map(),
7 cacheHitRates: new Map()
8 };
9
10 this.thresholds = {
11 maxBundleSize: 250000, // 250KB
12 maxLoadTime: 3000, // 3s
13 minCacheHitRate: 80 // 80%
14 };
15 }
16
17 async measureBundlePerformance() {
18 const performanceData = {
19 timestamp: Date.now(),
20 metrics: await this.collectMetrics(),
21 violations: this.checkViolations(),
22 recommendations: this.generateRecommendations()
23 };
24
25 await this.sendToMonitoring(performanceData);
26 return performanceData;
27 }
28
29 async collectMetrics() {
30 const bundleEntries = performance.getEntriesByType('navigation');
31 const resourceEntries = performance.getEntriesByType('resource');
32
33 return {
34 pageLoadTime: bundleEntries[0]?.loadEventEnd - bundleEntries[0]?.fetchStart,
35 resourceLoadTimes: resourceEntries
36 .filter(entry => entry.name.includes('.js') || entry.name.includes('.css'))
37 .map(entry => ({
38 name: entry.name,
39 size: entry.transferSize,
40 loadTime: entry.responseEnd - entry.fetchStart,
41 cached: entry.transferSize === 0
42 })),
43 memoryUsage: performance.memory ? {
44 used: performance.memory.usedJSHeapSize,
45 total: performance.memory.totalJSHeapSize,
46 limit: performance.memory.jsHeapSizeLimit
47 } : null
48 };
49 }
50
51 checkViolations() {
52 const violations = [];
53
54 // Sprawdzenie rozmiarów bundli
55 this.metrics.bundleSizes.forEach((size, bundleName) => {
56 if (size > this.thresholds.maxBundleSize) {
57 violations.push({
58 type: 'SIZE_VIOLATION',
59 bundle: bundleName,
60 current: size,
61 threshold: this.thresholds.maxBundleSize,
62 severity: 'HIGH'
63 });
64 }
65 });
66
67 return violations;
68 }
69
70 generateRecommendations() {
71 const recommendations = [];
72
73 // Analiza duplikatów w bundlach
74 const duplicatedModules = this.findDuplicatedModules();
75 if (duplicatedModules.length > 0) {
76 recommendations.push({
77 type: 'REMOVE_DUPLICATES',
78 modules: duplicatedModules,
79 estimatedSavings: this.calculateDuplicateSavings(duplicatedModules)
80 });
81 }
82
83 // Analiza nieużywanych dependencji
84 const unusedDeps = this.findUnusedDependencies();
85 if (unusedDeps.length > 0) {
86 recommendations.push({
87 type: 'REMOVE_UNUSED_DEPS',
88 dependencies: unusedDeps,
89 estimatedSavings: this.calculateUnusedDepsSavings(unusedDeps)
90 });
91 }
92
93 return recommendations;
94 }
95}
96
97// Automatyczne monitorowanie w production
98if (typeof window !== 'undefined' && process.env.NODE_ENV === 'production') {
99 const monitor = new BundlePerformanceMonitor();
100
101 // Pomiar po załadowaniu strony
102 window.addEventListener('load', () => {
103 setTimeout(() => {
104 monitor.measureBundlePerformance();
105 }, 2000);
106 });
107}1// RUM dla bundle performance
2class RealUserBundleMonitoring {
3 constructor() {
4 this.sessionId = this.generateSessionId();
5 this.startTime = performance.now();
6 this.setupObservers();
7 }
8
9 setupObservers() {
10 // Performance Observer dla resource timing
11 if ('PerformanceObserver' in window) {
12 const observer = new PerformanceObserver((list) => {
13 list.getEntries().forEach(entry => {
14 if (entry.name.includes('.js') || entry.name.includes('.css')) {
15 this.trackResourceLoad(entry);
16 }
17 });
18 });
19
20 observer.observe({ entryTypes: ['resource'] });
21 }
22
23 // Long task observer
24 if ('PerformanceObserver' in window) {
25 const longTaskObserver = new PerformanceObserver((list) => {
26 list.getEntries().forEach(entry => {
27 this.trackLongTask(entry);
28 });
29 });
30
31 longTaskObserver.observe({ entryTypes: ['longtask'] });
32 }
33 }
34
35 trackResourceLoad(entry) {
36 const resourceData = {
37 sessionId: this.sessionId,
38 timestamp: Date.now(),
39 resourceType: entry.name.includes('.js') ? 'javascript' : 'css',
40 resourceName: entry.name,
41 loadTime: entry.responseEnd - entry.fetchStart,
42 transferSize: entry.transferSize,
43 cached: entry.transferSize === 0,
44 userAgent: navigator.userAgent,
45 connectionType: navigator.connection?.effectiveType
46 };
47
48 this.sendMetric('resource_load', resourceData);
49 }
50
51 trackLongTask(entry) {
52 const longTaskData = {
53 sessionId: this.sessionId,
54 timestamp: Date.now(),
55 duration: entry.duration,
56 startTime: entry.startTime,
57 attribution: entry.attribution
58 };
59
60 this.sendMetric('long_task', longTaskData);
61 }
62
63 async sendMetric(type, data) {
64 // Wysyłanie do systemu analytics
65 if (navigator.sendBeacon) {
66 navigator.sendBeacon('/api/metrics', JSON.stringify({
67 type,
68 data,
69 metadata: {
70 url: window.location.href,
71 timestamp: Date.now(),
72 sessionId: this.sessionId
73 }
74 }));
75 }
76 }
77}1# Analiza użycia Lodash
2npx lodash-cli modularize exports=node
3
4# Babel plugin dla automatycznej optymalizacji
5npm install --save-dev babel-plugin-lodash1// babel.config.js
2module.exports = {
3 plugins: [
4 'lodash' // Automatycznie zamienia importy na cherry-picked
5 ]
6};
7
8// Przed optymalizacją (70KB)
9import _ from 'lodash';
10
11// Po optymalizacji (2KB)
12import debounce from 'lodash/debounce';
13import throttle from 'lodash/throttle';1// Porównanie rozmiarów bibliotek dat
2const dateLibraryComparison = {
3 'moment': {
4 size: '230KB',
5 gzipped: '67KB',
6 treeshaking: false
7 },
8 'date-fns': {
9 size: '78KB',
10 gzipped: '13KB',
11 treeshaking: true
12 },
13 'dayjs': {
14 size: '9KB',
15 gzipped: '3KB',
16 treeshaking: true
17 },
18 'luxon': {
19 size: '67KB',
20 gzipped: '19KB',
21 treeshaking: true
22 }
23};
24
25// Migration guide z Moment.js do date-fns
26const migrationExamples = {
27 before: `
28 import moment from 'moment';
29
30 const formatted = moment().format('DD/MM/YYYY');
31 const isAfter = moment(date1).isAfter(date2);
32 `,
33 after: `
34 import { format, isAfter } from 'date-fns';
35 import { pl } from 'date-fns/locale';
36
37 const formatted = format(new Date(), 'dd/MM/yyyy', { locale: pl });
38 const isAfterResult = isAfter(date1, date2);
39 `
40};1// PurgeCSS dla usuwania nieużywanych styli
2const purgecss = require('@fullhuman/postcss-purgecss');
3
4module.exports = {
5 plugins: [
6 purgecss({
7 content: [
8 './src/**/*.html',
9 './src/**/*.js',
10 './src/**/*.tsx'
11 ],
12 safelist: [
13 /^tooltip-/,
14 /^modal-/,
15 /^dropdown-/
16 ],
17 defaultExtractor: content => content.match(/[\w-/:]+(?<!:)/g) || []
18 })
19 ]
20};
21
22// Bundle size reduction examples
23const cssOptimizationResults = {
24 bootstrap: {
25 before: '150KB',
26 after: '23KB',
27 reduction: '85%'
28 },
29 tailwind: {
30 before: '3.2MB',
31 after: '12KB',
32 reduction: '99.6%'
33 },
34 materialUI: {
35 before: '890KB',
36 after: '145KB',
37 reduction: '84%'
38 }
39};1// Plugin do analizy i optymalizacji bundle
2class BundleOptimizationPlugin {
3 constructor(options = {}) {
4 this.options = {
5 reportPath: 'bundle-optimization-report.json',
6 thresholds: {
7 maxChunkSize: 250000,
8 maxModuleSize: 50000
9 },
10 ...options
11 };
12 }
13
14 apply(compiler) {
15 compiler.hooks.emit.tapAsync('BundleOptimizationPlugin', (compilation, callback) => {
16 const report = this.generateOptimizationReport(compilation);
17
18 // Zapisz raport
19 compilation.assets[this.options.reportPath] = {
20 source: () => JSON.stringify(report, null, 2),
21 size: () => JSON.stringify(report, null, 2).length
22 };
23
24 // Loguj ostrzeżenia
25 this.logWarnings(compilation, report);
26
27 callback();
28 });
29 }
30
31 generateOptimizationReport(compilation) {
32 const chunks = Array.from(compilation.chunks);
33 const modules = Array.from(compilation.modules);
34
35 return {
36 timestamp: new Date().toISOString(),
37 summary: {
38 totalChunks: chunks.length,
39 totalModules: modules.length,
40 totalSize: this.calculateTotalSize(chunks)
41 },
42 chunks: chunks.map(chunk => ({
43 id: chunk.id,
44 name: chunk.name,
45 size: chunk.size,
46 modules: chunk.getModules().length,
47 parents: Array.from(chunk.groupsIterable, g => g.getParents()),
48 children: Array.from(chunk.groupsIterable, g => g.getChildren())
49 })),
50 optimizationOpportunities: this.findOptimizationOpportunities(chunks, modules),
51 recommendations: this.generateRecommendations(chunks, modules)
52 };
53 }
54
55 findOptimizationOpportunities(chunks, modules) {
56 const opportunities = [];
57
58 // Znajdź duże moduły
59 const largeModules = modules.filter(module =>
60 module.size() > this.options.thresholds.maxModuleSize
61 );
62
63 if (largeModules.length > 0) {
64 opportunities.push({
65 type: 'LARGE_MODULES',
66 modules: largeModules.map(m => ({
67 identifier: m.identifier(),
68 size: m.size(),
69 reasons: m.reasons.map(r => r.module?.identifier())
70 }))
71 });
72 }
73
74 // Znajdź duplikaty
75 const moduleDuplicates = this.findModuleDuplicates(modules);
76 if (moduleDuplicates.length > 0) {
77 opportunities.push({
78 type: 'MODULE_DUPLICATES',
79 duplicates: moduleDuplicates
80 });
81 }
82
83 return opportunities;
84 }
85}
86
87// Użycie plugin
88module.exports = {
89 plugins: [
90 new BundleOptimizationPlugin({
91 thresholds: {
92 maxChunkSize: 200000,
93 maxModuleSize: 30000
94 }
95 })
96 ]
97};1// Optymalizacja runtime chunk
2module.exports = {
3 optimization: {
4 runtimeChunk: {
5 name: entrypoint => `runtime-${entrypoint.name}`
6 },
7
8 // Minimizer configuration
9 minimizer: [
10 new TerserPlugin({
11 parallel: true,
12 terserOptions: {
13 parse: {
14 ecma: 2020
15 },
16 compress: {
17 ecma: 2020,
18 comparisons: false,
19 inline: 2,
20 drop_console: process.env.NODE_ENV === 'production',
21 drop_debugger: process.env.NODE_ENV === 'production',
22 pure_funcs: ['console.log', 'console.info', 'console.debug']
23 },
24 mangle: {
25 safari10: true
26 },
27 output: {
28 ecma: 2020,
29 comments: false,
30 ascii_only: true
31 }
32 }
33 }),
34
35 new CssMinimizerPlugin({
36 parallel: true,
37 minimizerOptions: {
38 preset: [
39 'default',
40 {
41 discardComments: { removeAll: true },
42 normalizeWhitespace: true,
43 colormin: true,
44 convertValues: true,
45 discardDuplicates: true,
46 discardEmpty: true,
47 mergeRules: true,
48 minifyFontValues: true,
49 minifyGradients: true,
50 minifyParams: true,
51 minifySelectors: true,
52 normalizeCharset: true,
53 normalizeDisplayValues: true,
54 normalizePositions: true,
55 normalizeRepeatStyle: true,
56 normalizeString: true,
57 normalizeTimingFunctions: true,
58 normalizeUnicode: true,
59 normalizeUrl: true,
60 orderedValues: true,
61 reduceIdents: true,
62 reduceInitial: true,
63 reduceTransforms: true,
64 svgo: true,
65 uniqueSelectors: true
66 }
67 ]
68 }
69 })
70 ]
71 }
72};1// Test suite dla bundle optimization
2const bundleTests = {
3 // Test rozmiaru bundle
4 async testBundleSize() {
5 const bundleStats = await getBundleStats();
6 const maxSize = 500000; // 500KB
7
8 expect(bundleStats.totalSize).toBeLessThan(maxSize);
9 expect(bundleStats.mainChunk).toBeLessThan(200000);
10 expect(bundleStats.vendorChunk).toBeLessThan(300000);
11 },
12
13 // Test duplikatów
14 async testNoDuplicateModules() {
15 const bundleAnalysis = await analyzeBundleContent();
16 const duplicates = bundleAnalysis.duplicatedModules;
17
18 expect(duplicates).toHaveLength(0);
19 },
20
21 // Test tree shaking effectiveness
22 async testTreeShakingEffectiveness() {
23 const unusedExports = await findUnusedExports();
24 const deadCode = await findDeadCode();
25
26 expect(unusedExports.length).toBeLessThan(5);
27 expect(deadCode.totalSize).toBeLessThan(10000);
28 },
29
30 // Test cache effectiveness
31 async testCacheability() {
32 const chunks = await getChunkManifest();
33
34 chunks.forEach(chunk => {
35 if (chunk.type === 'vendor') {
36 expect(chunk.hash).toMatch(/^[a-f0-9]{8}$/);
37 expect(chunk.cacheable).toBe(true);
38 }
39 });
40 }
41};
42
43// Integration z CI/CD
44describe('Bundle Optimization Tests', () => {
45 test('Bundle size within limits', bundleTests.testBundleSize);
46 test('No duplicate modules', bundleTests.testNoDuplicateModules);
47 test('Tree shaking effective', bundleTests.testTreeShakingEffectiveness);
48 test('Chunks properly cacheable', bundleTests.testCacheability);
49});1// System do wykrywania regresji wydajności
2class PerformanceRegressionDetector {
3 constructor() {
4 this.baselineMetrics = null;
5 this.threshold = 0.1; // 10% wzrost = regresja
6 }
7
8 async setBaseline() {
9 this.baselineMetrics = await this.measureCurrentMetrics();
10 await this.saveBaseline(this.baselineMetrics);
11 }
12
13 async detectRegressions() {
14 const currentMetrics = await this.measureCurrentMetrics();
15 const regressions = [];
16
17 if (!this.baselineMetrics) {
18 this.baselineMetrics = await this.loadBaseline();
19 }
20
21 // Sprawdź rozmiary bundli
22 const sizeRegression = this.detectSizeRegression(
23 this.baselineMetrics.bundleSize,
24 currentMetrics.bundleSize
25 );
26
27 if (sizeRegression) {
28 regressions.push(sizeRegression);
29 }
30
31 // Sprawdź czasy ładowania
32 const loadTimeRegression = this.detectLoadTimeRegression(
33 this.baselineMetrics.loadTime,
34 currentMetrics.loadTime
35 );
36
37 if (loadTimeRegression) {
38 regressions.push(loadTimeRegression);
39 }
40
41 return regressions;
42 }
43
44 detectSizeRegression(baseline, current) {
45 const increase = (current - baseline) / baseline;
46
47 if (increase > this.threshold) {
48 return {
49 type: 'SIZE_REGRESSION',
50 baseline: baseline,
51 current: current,
52 increase: `${(increase * 100).toFixed(1)}%`,
53 severity: increase > 0.25 ? 'HIGH' : 'MEDIUM'
54 };
55 }
56
57 return null;
58 }
59
60 async generateRegressionReport(regressions) {
61 return {
62 timestamp: new Date().toISOString(),
63 regressions: regressions,
64 recommendations: await this.generateRecommendations(regressions),
65 actionItems: this.generateActionItems(regressions)
66 };
67 }
68}1// Strategia progressive deployment dla bundli
2class ProgressiveBundleDeployment {
3 constructor() {
4 this.deploymentStages = [
5 { name: 'canary', percentage: 5 },
6 { name: 'blue-green', percentage: 50 },
7 { name: 'full', percentage: 100 }
8 ];
9 }
10
11 async deployNewBundle(bundleVersion) {
12 for (const stage of this.deploymentStages) {
13 console.log(`Deploying to ${stage.name} (${stage.percentage}% traffic)`);
14
15 await this.deployToStage(bundleVersion, stage);
16
17 const metrics = await this.monitorStageMetrics(stage, 300000); // 5 min
18
19 if (!this.validateStageSuccess(metrics)) {
20 await this.rollbackStage(stage);
21 throw new Error(`Deployment failed at ${stage.name} stage`);
22 }
23
24 console.log(`Stage ${stage.name} successful`);
25 }
26 }
27
28 async monitorStageMetrics(stage, duration) {
29 const startTime = Date.now();
30 const metrics = {
31 errorRate: [],
32 loadTimes: [],
33 bundleErrors: []
34 };
35
36 while (Date.now() - startTime < duration) {
37 const sample = await this.collectMetricsSample(stage);
38 metrics.errorRate.push(sample.errorRate);
39 metrics.loadTimes.push(sample.avgLoadTime);
40 metrics.bundleErrors.push(sample.bundleErrors);
41
42 await new Promise(resolve => setTimeout(resolve, 30000)); // 30s intervals
43 }
44
45 return metrics;
46 }
47
48 validateStageSuccess(metrics) {
49 const avgErrorRate = metrics.errorRate.reduce((a, b) => a + b) / metrics.errorRate.length;
50 const avgLoadTime = metrics.loadTimes.reduce((a, b) => a + b) / metrics.loadTimes.length;
51 const totalBundleErrors = metrics.bundleErrors.reduce((a, b) => a + b);
52
53 return avgErrorRate < 0.01 && // < 1% error rate
54 avgLoadTime < 3000 && // < 3s load time
55 totalBundleErrors < 5; // < 5 bundle errors
56 }
57}1// A/B testing różnych strategii bundle optimization
2class BundleOptimizationABTest {
3 constructor() {
4 this.variants = {
5 control: {
6 strategy: 'current',
7 splitChunks: 'default',
8 compression: 'gzip'
9 },
10 optimized: {
11 strategy: 'aggressive_splitting',
12 splitChunks: 'custom',
13 compression: 'brotli'
14 },
15 experimental: {
16 strategy: 'module_federation',
17 splitChunks: 'micro_chunks',
18 compression: 'zstd'
19 }
20 };
21 }
22
23 async runABTest(duration = 7 * 24 * 60 * 60 * 1000) { // 7 days
24 const testId = this.generateTestId();
25
26 // Rozpocznij test
27 await this.startTest(testId);
28
29 // Monituj metryki
30 const results = await this.monitorTest(testId, duration);
31
32 // Analizuj wyniki
33 const analysis = await this.analyzeResults(results);
34
35 // Wybierz zwycięzcę
36 const winner = this.selectWinner(analysis);
37
38 return {
39 testId,
40 duration,
41 results,
42 analysis,
43 winner,
44 recommendation: this.generateRecommendation(analysis, winner)
45 };
46 }
47
48 async monitorTest(testId, duration) {
49 const results = {
50 control: { metrics: [], samples: 0 },
51 optimized: { metrics: [], samples: 0 },
52 experimental: { metrics: [], samples: 0 }
53 };
54
55 const startTime = Date.now();
56
57 while (Date.now() - startTime < duration) {
58 for (const variant of Object.keys(this.variants)) {
59 const metrics = await this.collectVariantMetrics(variant);
60 results[variant].metrics.push(metrics);
61 results[variant].samples++;
62 }
63
64 await new Promise(resolve => setTimeout(resolve, 3600000)); // 1 hour
65 }
66
67 return results;
68 }
69
70 selectWinner(analysis) {
71 const scores = {};
72
73 for (const variant of Object.keys(analysis)) {
74 const data = analysis[variant];
75
76 scores[variant] = (
77 (1 / data.avgLoadTime) * 0.4 + // 40% weight
78 (1 / data.avgBundleSize) * 0.3 + // 30% weight
79 data.conversionRate * 0.2 + // 20% weight
80 (1 - data.errorRate) * 0.1 // 10% weight
81 );
82 }
83
84 return Object.keys(scores).reduce((a, b) =>
85 scores[a] > scores[b] ? a : b
86 );
87 }
88}1const bundleOptimizationChecklist = {
2 analysis: [
3 '✓ Webpack Bundle Analyzer setup',
4 '✓ Source map analysis',
5 '✓ Performance budget configured',
6 '✓ CI/CD bundle size monitoring',
7 '✓ Duplicate detection'
8 ],
9
10 codeOptimization: [
11 '✓ Tree shaking enabled',
12 '✓ Dead code elimination',
13 '✓ Unused dependencies removed',
14 '✓ Code splitting implemented',
15 '✓ Lazy loading configured'
16 ],
17
18 bundleStrategy: [
19 '✓ Chunk splitting optimized',
20 '✓ Vendor chunks separated',
21 '✓ Runtime chunk extracted',
22 '✓ Common chunks identified',
23 '✓ Dynamic imports utilized'
24 ],
25
26 compression: [
27 '✓ Gzip/Brotli enabled',
28 '✓ Minification configured',
29 '✓ CSS optimization',
30 '✓ Image optimization',
31 '✓ Font optimization'
32 ],
33
34 caching: [
35 '✓ Long-term caching setup',
36 '✓ Content-based hashing',
37 '✓ Cache invalidation strategy',
38 '✓ Service worker implemented',
39 '✓ CDN optimization'
40 ],
41
42 monitoring: [
43 '✓ Real user monitoring',
44 '✓ Performance metrics tracking',
45 '✓ Regression detection',
46 '✓ A/B testing capability',
47 '✓ Alert system configured'
48 ]
49};
50
51// ROI calculation
52const optimizationROI = {
53 investment: {
54 developmentTime: '2-3 weeks',
55 toolingSetup: '$5,000',
56 ongoingMaintenance: '$2,000/month'
57 },
58
59 returns: {
60 conversionRateIncrease: '+25-40%',
61 bounceRateDecrease: '-30-50%',
62 serverCostReduction: '-20-30%',
63 mobileUserRetention: '+35-60%',
64 seoRankingImprovement: '+15-25%'
65 },
66
67 businessImpact: {
68 revenueIncrease: '$50,000-150,000/year',
69 costSavings: '$20,000-40,000/year',
70 paybackPeriod: '2-4 months',
71 totalROI: '400-800%'
72 }
73};Bundle optimization to inwestycja, która zwraca się bardzo szybko poprzez poprawę wszystkich kluczowych metryk biznesowych. Systematyczne podejście, ciągły monitoring i iteracyjne ulepszenia pozwalają osiągnąć i utrzymać optymalną wydajność aplikacji.