Aller au contenu

Feuille de chaleur Webpack

Webpack - Module statique Bundler

Webpack est un groupeur de modules statiques pour les applications JavaScript modernes. Lorsque webpack traite votre application, il construit en interne un graphique de dépendance à partir d'un ou de plusieurs points d'entrée et combine ensuite chaque module dont votre projet a besoin en un ou plusieurs paquets.

Copier toutes les commandes Générer PDF

Sommaire

  • [Installation] (#installation)
  • [Configuration de base] (#basic-configuration)
  • [Points d'entrée] (#entry-points)
  • [Extrait] (#output)
  • [Loaders] (#loaders)
  • [Plugins] (#plugins)
  • [Mode] (#mode)
  • [Serveur de développement] (#development-server)
  • [Doublure du code] (#code-splitting)
  • [Optimisation] (#optimization)
  • [Résolution sur les modules] (#module-resolution)
  • [Remplacement du module hôte] (#hot-module-replacement)
  • [variables environnementales] (#environment-variables)
  • [Construire la production] (#production-build)
  • [Configuration avancée] (#advanced-configuration)
  • [Performance] (#performance)
  • [Dépannage] (#troubleshooting)
  • [Meilleures pratiques] (#best-practices)

Installation

Installation mondiale

# Install webpack globally
npm install -g webpack webpack-cli

# Check version
webpack --version

Installation locale (recommandée)

# Initialize npm project
npm init -y

# Install webpack locally
npm install --save-dev webpack webpack-cli

# Install webpack dev server
npm install --save-dev webpack-dev-server

# Install common loaders and plugins
npm install --save-dev html-webpack-plugin css-loader style-loader file-loader url-loader
```_

### Paquet.json Scripts
```json
{
  "scripts": {
    "build": "webpack --mode production",
    "dev": "webpack --mode development",
    "start": "webpack serve --mode development",
    "watch": "webpack --mode development --watch"
  }
}
```_

## Configuration de base

### Minimal webpack.config.js
```javascript
const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
};

Configuration de base complète

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.[contenthash].js',
    path: path.resolve(__dirname, 'dist'),
    clean: true
  },
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: ['style-loader', 'css-loader']
      },
      {
        test: /\.(png|svg|jpg|jpeg|gif)$/i,
        type: 'asset/resource'
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ]
};

Configuration du script de type

const path = require('path');

module.exports = {
  entry: './src/index.ts',
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: 'ts-loader',
        exclude: /node_modules/
      }
    ]
  },
  resolve: {
    extensions: ['.tsx', '.ts', '.js']
  },
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
};

Points d'entrée

Point d'entrée unique

module.exports = {
  entry: './src/index.js'
};

Points d'entrée multiples

module.exports = {
  entry: {
    main: './src/index.js',
    vendor: './src/vendor.js'
  }
};

Points d'entrée dynamiques

module.exports = {
  entry: () => {
    return {
      main: './src/index.js',
      admin: './src/admin.js'
    };
  }
};

Entrée avec dépendances

module.exports = {
  entry: {
    main: {
      import: './src/index.js',
      dependOn: 'shared'
    },
    admin: {
      import: './src/admin.js',
      dependOn: 'shared'
    },
    shared: 'lodash'
  }
};

Produit

Configuration de sortie de base

const path = require('path');

module.exports = {
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
};

Sortie à entrées multiples

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

Sortie avec Hashing

module.exports = {
  output: {
    filename: '[name].[contenthash].js',
    path: path.resolve(__dirname, 'dist'),
    clean: true
  }
};

Configuration de la voie publique

module.exports = {
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
    publicPath: '/assets/'
  }
};

Produit de la bibliothèque

module.exports = {
  output: {
    filename: 'my-library.js',
    path: path.resolve(__dirname, 'dist'),
    library: {
      name: 'MyLibrary',
      type: 'umd'
    }
  }
};

Chargeurs

CSS Chargeurs

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: ['style-loader', 'css-loader']
      },
      {
        test: /\.s[ac]ss$/i,
        use: ['style-loader', 'css-loader', 'sass-loader']
      },
      {
        test: /\.less$/i,
        use: ['style-loader', 'css-loader', 'less-loader']
      }
    ]
  }
};

Chargeurs JavaScript

module.exports = {
  module: {
    rules: [
      {
        test: /\.m?js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env']
          }
        }
      },
      {
        test: /\.tsx?$/,
        use: 'ts-loader',
        exclude: /node_modules/
      }
    ]
  }
};

Chargeurs de fichiers

module.exports = {
  module: {
    rules: [
      {
        test: /\.(png|svg|jpg|jpeg|gif)$/i,
        type: 'asset/resource'
      },
      {
        test: /\.(woff|woff2|eot|ttf|otf)$/i,
        type: 'asset/resource'
      },
      {
        test: /\.(csv|tsv)$/i,
        use: ['csv-loader']
      },
      {
        test: /\.xml$/i,
        use: ['xml-loader']
      }
    ]
  }
};

Modules d'actifs

module.exports = {
  module: {
    rules: [
      {
        test: /\.png/,
        type: 'asset/resource'
      },
      {
        test: /\.html/,
        type: 'asset/resource'
      },
      {
        test: /\.txt/,
        type: 'asset/source'
      },
      {
        test: /\.jpg/,
        type: 'asset',
        parser: {
          dataUrlCondition: {
            maxSize: 4 * 1024 // 4kb
          }
        }
      }
    ]
  }
};

Options du chargeur

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              modules: true,
              sourceMap: true
            }
          }
        ]
      }
    ]
  }
};

Greffons

Plugin HTML Webpack

const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      filename: 'index.html',
      inject: 'body'
    })
  ]
};

Extrait CSS Greffon

const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: [MiniCssExtractPlugin.loader, 'css-loader']
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].[contenthash].css'
    })
  ]
};

Définir le greffon

const webpack = require('webpack');

module.exports = {
  plugins: [
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify('production'),
      'process.env.API_URL': JSON.stringify('https://api.example.com')
    })
  ]
};

Copier le plugin Webpack

const CopyWebpackPlugin = require('copy-webpack-plugin');

module.exports = {
  plugins: [
    new CopyWebpackPlugin({
      patterns: [
        { from: 'public', to: 'public' },
        { from: 'assets/images', to: 'images' }
      ]
    })
  ]
};

Analyseur de groupe Greffon

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

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

Nettoyer le plugin Webpack

const { CleanWebpackPlugin } = require('clean-webpack-plugin');

module.exports = {
  plugins: [
    new CleanWebpackPlugin()
  ]
};

Mode

Mode de développement

module.exports = {
  mode: 'development',
  devtool: 'eval-source-map'
};

Mode de production

module.exports = {
  mode: 'production',
  devtool: 'source-map'
};

Mode spécifique Configuration

module.exports = (env, argv) => {
  const isProduction = argv.mode === 'production';

  return {
    mode: argv.mode,
    devtool: isProduction ? 'source-map' : 'eval-source-map',
    optimization: {
      minimize: isProduction
    }
  };
};

Serveur de développement

Serveur Dev de base

module.exports = {
  devServer: {
    static: './dist',
    port: 3000,
    open: true
  }
};

Configuration avancée du serveur Dev

module.exports = {
  devServer: {
    static: {
      directory: path.join(__dirname, 'dist')
    },
    compress: true,
    port: 3000,
    hot: true,
    open: true,
    historyApiFallback: true,
    proxy: {
      '/api': {
        target: 'http://localhost:8080',
        changeOrigin: true,
        pathRewrite: {
          '^/api': ''
        }
      }
    }
  }
};

Serveur HTTPS Dev

module.exports = {
  devServer: {
    https: true,
    // or with custom certificates
    https: {
      key: fs.readFileSync('/path/to/server.key'),
      cert: fs.readFileSync('/path/to/server.crt'),
      ca: fs.readFileSync('/path/to/ca.pem')
    }
  }
};

Fractionnement du code

Découpe des points d'entrée

module.exports = {
  entry: {
    main: './src/index.js',
    vendor: './src/vendor.js'
  }
};

Importations dynamiques

// In your JavaScript code
import('./math.js').then(math => {
  console.log(math.add(16, 26));
});

// Or with async/await
async function loadMath() {
  const math = await import('./math.js');
  return math.add(16, 26);
}

Plugin SplitChunks

module.exports = {
  optimization: {
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all'
        },
        common: {
          name: 'common',
          minChunks: 2,
          chunks: 'all',
          enforce: true
        }
      }
    }
  }
};

fractionnement avancé du code

module.exports = {
  optimization: {
    splitChunks: {
      chunks: 'all',
      minSize: 20000,
      maxSize: 244000,
      cacheGroups: {
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true
        },
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10,
          reuseExistingChunk: true
        },
        react: {
          test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
          name: 'react',
          chunks: 'all'
        }
      }
    }
  }
};

Optimisation

Minification

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

module.exports = {
  optimization: {
    minimize: true,
    minimizer: [
      new TerserPlugin({
        terserOptions: {
          compress: {
            drop_console: true
          }
        }
      })
    ]
  }
};

CSS Optimisation

const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');

module.exports = {
  optimization: {
    minimizer: [
      new CssMinimizerPlugin()
    ]
  }
};

Shaking d'arbre

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

Runtime Chunk

module.exports = {
  optimization: {
    runtimeChunk: 'single'
  }
};

Concaténation du module

module.exports = {
  optimization: {
    concatenateModules: true
  }
};

Résolution du module

Résoudre la configuration

const path = require('path');

module.exports = {
  resolve: {
    extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'],
    alias: {
      '@': path.resolve(__dirname, 'src'),
      'components': path.resolve(__dirname, 'src/components'),
      'utils': path.resolve(__dirname, 'src/utils')
    },
    modules: [
      'node_modules',
      path.resolve(__dirname, 'src')
    ]
  }
};

Résoudre l'automne

module.exports = {
  resolve: {
    fallback: {
      "fs": false,
      "path": require.resolve("path-browserify"),
      "crypto": require.resolve("crypto-browserify")
    }
  }
};

Remplacement du module à chaud

Activer HMR

const webpack = require('webpack');

module.exports = {
  devServer: {
    hot: true
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin()
  ]
};

RSM dans le code

// In your JavaScript modules
if (module.hot) {
  module.hot.accept('./library.js', function() {
    console.log('Accepting the updated library module!');
    // Re-render or update your app
  });
}

Réagir à la recharge chaude

const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');

module.exports = {
  module: {
    rules: [
      {
        test: /\.[jt]sx?$/,
        exclude: /node_modules/,
        use: [
          {
            loader: 'babel-loader',
            options: {
              plugins: ['react-refresh/babel']
            }
          }
        ]
      }
    ]
  },
  plugins: [
    new ReactRefreshWebpackPlugin()
  ]
};

Variables d'environnement

Définir Plugin pour les variables d'environnement

const webpack = require('webpack');

module.exports = {
  plugins: [
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
      'process.env.API_URL': JSON.stringify(process.env.API_URL || 'http://localhost:3000')
    })
  ]
};

Environnement Configuration

module.exports = (env, argv) => {
  const isProduction = argv.mode === 'production';

  return {
    mode: argv.mode,
    plugins: [
      new webpack.DefinePlugin({
        'process.env.NODE_ENV': JSON.stringify(argv.mode),
        'process.env.API_URL': JSON.stringify(
          isProduction ? 'https://api.production.com' : 'http://localhost:3000'
        )
      })
    ]
  };
};

Plugin DotEnv

const Dotenv = require('dotenv-webpack');

module.exports = {
  plugins: [
    new Dotenv({
      path: './.env',
      safe: true,
      systemvars: true
    })
  ]
};

Construction de production

Configuration de production

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');

module.exports = {
  mode: 'production',
  entry: './src/index.js',
  output: {
    filename: '[name].[contenthash].js',
    path: path.resolve(__dirname, 'dist'),
    clean: true
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env']
          }
        }
      },
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader']
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      minify: {
        removeComments: true,
        collapseWhitespace: true,
        removeRedundantAttributes: true,
        useShortDoctype: true,
        removeEmptyAttributes: true,
        removeStyleLinkTypeAttributes: true,
        keepClosingSlash: true,
        minifyJS: true,
        minifyCSS: true,
        minifyURLs: true
      }
    }),
    new MiniCssExtractPlugin({
      filename: '[name].[contenthash].css'
    })
  ],
  optimization: {
    minimize: true,
    minimizer: [
      new TerserPlugin({
        terserOptions: {
          compress: {
            drop_console: true
          }
        }
      }),
      new CssMinimizerPlugin()
    ],
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all'
        }
      }
    },
    runtimeChunk: 'single'
  }
};

Construire des scripts

{
  "scripts": {
    "build": "webpack --mode production",
    "build:analyze": "webpack --mode production --env analyze",
    "build:stats": "webpack --mode production --json > stats.json"
  }
}

Configuration avancée

Demande multipages

const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: {
    home: './src/home.js',
    about: './src/about.js',
    contact: './src/contact.js'
  },
  plugins: [
    new HtmlWebpackPlugin({
      filename: 'home.html',
      template: './src/home.html',
      chunks: ['home']
    }),
    new HtmlWebpackPlugin({
      filename: 'about.html',
      template: './src/about.html',
      chunks: ['about']
    }),
    new HtmlWebpackPlugin({
      filename: 'contact.html',
      template: './src/contact.html',
      chunks: ['contact']
    })
  ]
};

Microfenêtres Configuration

const ModuleFederationPlugin = require('@module-federation/webpack');

module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: 'shell',
      remotes: {
        mfe1: 'mfe1@http://localhost:3001/remoteEntry.js',
        mfe2: 'mfe2@http://localhost:3002/remoteEntry.js'
      }
    })
  ]
};

Application Web progressive

const WorkboxPlugin = require('workbox-webpack-plugin');

module.exports = {
  plugins: [
    new WorkboxPlugin.GenerateSW({
      clientsClaim: true,
      skipWaiting: true
    })
  ]
};

Chargeurs personnalisés

// custom-loader.js
module.exports = function(source) {
  // Transform the source
  return `export default ${JSON.stringify(source)}`;
};

// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.custom$/,
        use: path.resolve(__dirname, 'custom-loader.js')
      }
    ]
  }
};

Plugins personnalisés

class MyCustomPlugin {
  apply(compiler) {
    compiler.hooks.emit.tapAsync('MyCustomPlugin', (compilation, callback) => {
      console.log('This is an example plugin!');
      callback();
    });
  }
}

module.exports = {
  plugins: [
    new MyCustomPlugin()
  ]
};

Rendement

Budgets d'exécution

module.exports = {
  performance: {
    maxAssetSize: 250000,
    maxEntrypointSize: 250000,
    hints: 'warning'
  }
};

Chargement paresseux

// Dynamic imports for lazy loading
const LazyComponent = React.lazy(() => import('./LazyComponent'));

// Webpack magic comments
import(
  /* webpackChunkName: "my-chunk-name" */
  /* webpackMode: "lazy" */
  './modules/my-module.js'
);

Préconditionnement et préchargement

// Prefetch
import(
  /* webpackPrefetch: true */
  './modules/LoginModal.js'
);

// Preload
import(
  /* webpackPreload: true */
  './modules/ChartingLibrary.js'
);

Analyse de l'ensemble

# Generate stats file
webpack --profile --json > stats.json

# Analyze with webpack-bundle-analyzer
npx webpack-bundle-analyzer stats.json

# Use webpack-bundle-analyzer plugin
npm install --save-dev webpack-bundle-analyzer

Dépannage

Questions et solutions communes

Module non trouvé

// Check resolve configuration
module.exports = {
  resolve: {
    extensions: ['.js', '.jsx', '.ts', '.tsx'],
    alias: {
      '@': path.resolve(__dirname, 'src')
    }
  }
};

Problèmes de mémoire

# Increase Node.js memory limit
node --max-old-space-size=4096 node_modules/.bin/webpack

# Or in package.json
{
  "scripts": {
    "build": "node --max-old-space-size=4096 node_modules/.bin/webpack"
  }
}

Temps de construction lents

module.exports = {
  // Use cache
  cache: {
    type: 'filesystem'
  },

  // Exclude node_modules from babel-loader
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: 'babel-loader'
      }
    ]
  },

  // Use thread-loader for expensive loaders
  module: {
    rules: [
      {
        test: /\.js$/,
        use: [
          'thread-loader',
          'babel-loader'
        ]
      }
    ]
  }
};

Questions relatives aux cartes sources

module.exports = {
  // Different source map options
  devtool: 'eval-source-map', // Development
  devtool: 'source-map', // Production
  devtool: 'cheap-module-source-map', // Faster builds
  devtool: false // No source maps
};

Configuration de débogage

module.exports = {
  stats: {
    colors: true,
    modules: false,
    children: false,
    chunks: false,
    chunkModules: false
  }
};

Meilleures pratiques

Organisation de configuration

// webpack.common.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/index.js',
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ]
};

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

module.exports = merge(common, {
  mode: 'development',
  devtool: 'eval-source-map',
  devServer: {
    static: './dist'
  }
});

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

module.exports = merge(common, {
  mode: 'production',
  devtool: 'source-map'
});

Optimisation des performances

  • Utiliser le mode de production pour les optimisations
  • Activer les tremblements d'arbre pour supprimer le code mort
  • Split chunks pour améliorer la mise en cache
  • Utiliser le hachage du contenu pour la mise en cache à long terme
  • ** Minimiser la taille du paquet** avec les chargeurs et les plugins appropriés

Expérience en matière de développement

  • Utiliser HMR pour un développement plus rapide
  • Configurer les cartes sources pour le débogage
  • Set up dev server avec proxy pour les appels API
  • Utilisez webpack-bundle-analyzer pour comprendre la composition du faisceau

Code Organisation

  • Séparer la configuration pour différents environnements
  • Alias utilisés pour les importations plus propres
  • Organiser les chargeurs par type de fichier
  • Les plugins liés au groupe ensemble

Résumé

Webpack est un groupeur de modules puissant et flexible qui peut gérer les exigences de construction complexes pour les applications Web modernes. Les principales caractéristiques sont les suivantes :

  • Module Bundling: Combine des modules en paquets optimisés
  • Loaders: Transformer les fichiers pendant le processus de construction
  • Plugins: Extension de la fonctionnalité webpack
  • Doublure du code: Diviser le code en petits morceaux pour une meilleure performance
  • Remplacement du module hôte: Mettre à jour les modules sans recharger la page complète
  • Tree Shaking: Supprimer le code inutilisé des paquets
  • Gestion des actifs: Gérer divers types de fichiers et actifs

En maîtrisant la configuration du webpack et en suivant les meilleures pratiques, vous pouvez créer des processus de construction efficaces qui améliorent l'expérience de développement et la performance de l'application.