Nội dung

DaiPhan

DaiPhan

Full-Stack Developer

Full-stack developer passionate about modern web technologies, best practices, and sharing knowledge with the community.

Skills & Expertise

JavaScript TypeScript React Node.js DevOps
150+
Articles
50k+
Readers
4.9
Rating

Webpack Bundling: Tối Ưu Performance Cho Frontend

Hướng dẫn cấu hình Webpack cho production-ready applications

Webpack Bundling: Tối Ưu Performance Cho Frontend

Webpack là module bundler mạnh mẽ cho phép bạn optimize và manage dependencies hiệu quả. Hiểu rõ cách cấu hình Webpack sẽ giúp improve performance đáng kể cho ứng dụng của bạn.

Webpack Basics

Core Concepts

// webpack.config.js
const path = require('path');

module.exports = {
  // Entry point
  entry: './src/index.js',
  
  // Output configuration
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].[contenthash].js',
    clean: true
  },
  
  // Loaders
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env', '@babel/preset-react']
          }
        }
      },
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      },
      {
        test: /\.(png|svg|jpg|jpeg|gif)$/i,
        type: 'asset/resource'
      }
    ]
  },
  
  // Plugins
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ]
};

Entry và Output

// Multiple entry points
module.exports = {
  entry: {
    main: './src/index.js',
    vendor: './src/vendor.js',
    admin: './src/admin.js'
  },
  
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].[contenthash].bundle.js',
    chunkFilename: '[name].[contenthash].chunk.js'
  }
};

Code Splitting Strategies

1. Entry Points Splitting

module.exports = {
  entry: {
    app: './src/app.js',
    admin: './src/admin.js'
  },
  
  optimization: {
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all'
        }
      }
    }
  }
};

2. Dynamic Import

// Before: Single large bundle
import { heavyCalculation } from './utils/heavy';

// After: Lazy load when needed
const loadHeavyCalculation = async () => {
  const { heavyCalculation } = await import('./utils/heavy');
  return heavyCalculation;
};

// React lazy loading
import { lazy, Suspense } from 'react';

const HeavyComponent = lazy(() => import('./components/HeavyComponent'));

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <HeavyComponent />
    </Suspense>
  );
}

3. Route-based Splitting

// React Router v6
import { lazy } from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';

const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));
const Dashboard = lazy(() => import('./pages/Dashboard'));

function App() {
  return (
    <BrowserRouter>
      <Suspense fallback={<div>Loading...</div>}>
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/about" element={<About />} />
          <Route path="/dashboard" element={<Dashboard />} />
        </Routes>
      </Suspense>
    </BrowserRouter>
  );
}

Optimization Techniques

1. Tree Shaking

// webpack.config.js
module.exports = {
  mode: 'production',
  optimization: {
    usedExports: true,
    sideEffects: false
  }
};

// package.json
{
  "sideEffects": [
    "*.css",
    "*.scss"
  ]
};

2. Minification và Compression

const TerserPlugin = require('terser-webpack-plugin');
const CompressionPlugin = require('compression-webpack-plugin');

module.exports = {
  optimization: {
    minimize: true,
    minimizer: [
      new TerserPlugin({
        terserOptions: {
          compress: {
            drop_console: true,
            drop_debugger: true
          }
        }
      })
    ]
  },
  
  plugins: [
    new CompressionPlugin({
      algorithm: 'gzip',
      test: /\.(js|css|html|svg)$/,
      threshold: 8192,
      minRatio: 0.8
    })
  ]
};

3. Image Optimization

const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin');

module.exports = {
  module: {
    rules: [
      {
        test: /\.(jpe?g|png|gif|svg)$/i,
        type: 'asset',
        parser: {
          dataUrlCondition: {
            maxSize: 8192 // 8kb
          }
        }
      }
    ]
  },
  
  plugins: [
    new ImageMinimizerPlugin({
      minimizer: {
        implementation: ImageMinimizerPlugin.imageminMinify,
        options: {
          plugins: [
            ['gifsicle', { interlaced: true }],
            ['jpegtran', { progressive: true }],
            ['optipng', { optimizationLevel: 5 }]
          ]
        }
      }
    })
  ]
};

Development vs Production

Development Configuration

const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');

module.exports = merge(common, {
  mode: 'development',
  devtool: 'eval-cheap-module-source-map',
  
  devServer: {
    static: './dist',
    hot: true,
    open: true,
    historyApiFallback: true,
    compress: true,
    port: 3000
  },
  
  optimization: {
    splitChunks: {
      chunks: 'all'
    }
  }
});

Production Configuration

const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = merge(common, {
  mode: 'production',
  devtool: 'source-map',
  
  output: {
    filename: '[name].[contenthash].js',
    chunkFilename: '[name].[contenthash].chunk.js',
    clean: true
  },
  
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader'
        ]
      }
    ]
  },
  
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].[contenthash].css',
      chunkFilename: '[name].[contenthash].chunk.css'
    })
  ],
  
  optimization: {
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all'
        },
        common: {
          name: 'common',
          minChunks: 2,
          chunks: 'all',
          enforce: true
        }
      }
    }
  }
});

Performance Monitoring

Bundle Analysis

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
  plugins: [
    new BundleAnalyzerPlugin({
      analyzerMode: 'static',
      openAnalyzer: false,
      reportFilename: 'bundle-report.html'
    })
  ]
};

Performance Budget

module.exports = {
  performance: {
    maxAssetSize: 250000, // 250kb
    maxEntrypointSize: 250000,
    hints: 'warning',
    assetFilter: function(assetFilename) {
      return assetFilename.endsWith('.js');
    }
  }
};

Advanced Techniques

Module Federation

// Host application
module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: 'host',
      remotes: {
        app1: 'app1@http://localhost:3001/remoteEntry.js',
        app2: 'app2@http://localhost:3002/remoteEntry.js'
      }
    })
  ]
};

// Remote application
module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: 'app1',
      filename: 'remoteEntry.js',
      exposes: {
        './Button': './src/components/Button',
        './Header': './src/components/Header'
      }
    })
  ]
};

Web Workers

// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.worker\.js$/,
        use: { loader: 'worker-loader' }
      }
    ]
  }
};

// heavy-calculation.worker.js
self.onmessage = function(e) {
  const result = performHeavyCalculation(e.data);
  self.postMessage(result);
};

// main.js
import HeavyWorker from './heavy-calculation.worker.js';

const worker = new HeavyWorker();
worker.postMessage(data);
worker.onmessage = function(e) {
  console.log('Calculation result:', e.data);
};

Build Optimization Checklist

✅ Code splitting implemented
✅ Tree shaking enabled
✅ Minification configured
✅ Compression enabled (gzip/brotli)
✅ Images optimized
✅ Fonts subsetted
✅ Unused CSS removed
✅ Source maps for production
✅ Performance budget set
✅ Bundle analysis performed

Kết luận

Webpack configuration tốt sẽ giúp:

  • Reduce bundle size đáng kể
  • Improve loading performance
  • Better caching strategy
  • Enhanced development experience

Bài viết này là phần đầu tiên trong series về build tools. Trong các bài tiếp theo, chúng ta sẽ explore Vite, Rollup và esbuild.