56 votes

Migration vers Webpack 4 CommonsChunkPlugin

J'ai besoin d'aide pour migrer le code suivant de webpack 3 à 4.

new webpack.optimize.CommonsChunkPlugin({
    minChunks: module => module.context && module.context.indexOf("node_modules") !== -1,
    name: "vendor",
    chunks: ["main"]
})

J'ai deux fichiers d'entrée et je veux que seules les dépendances du premier soient incluses dans le chunk du vendeur. Les dépendances de la deuxième entrée doivent rester dans son propre bundle.

67voto

Legends Points 6728

À partir de webpack v4, le plugin CommonsChunkPlugin est déprécié.

Nous avons déprécié et supprimé CommonsChunkPlugin, et nous l'avons remplacé par un ensemble de valeurs par défaut et une API facilement surchargeable appelée optimization.splitChunks .

webpack.optimize.CommonsChunkPlugin has been removed, 
please use config.optimization.splitChunks instead.

Déprécié

Vous n'avez plus besoin d'utiliser ces plugins :

DedupePlugin a également été supprimé dans la version 4.

NoEmitOnErrorsPlugin -> optimization.noEmitOnErrors (activé par défaut en mode production) ModuleConcaténationPlugin -> optimization.concatenateModules (activé par défaut en mode prod) NamedModulesPlugin -> optimization.namedModules (activé par défaut en mode dev)


Recommandations pour webpack 4

Utilisez mini-css-extract-plugin au lieu de text-extract-plugin . Utilisez webpack-bundle-analyzer pour analyser votre production groupée de manière graphique.

Les scripts d'entrée sont de véritables "scripts d'entrée" dans votre application. N'ajoutez pas explicitement les fichiers du vendeur à l'application. entry: en webpack.config.js . Les applications SPA n'ont qu'une seule entrée et les applications multi-pages comme les applications classiques. ASP.NET MVC Les applications ont plusieurs points d'entrée. Webpack construira un graphe de dépendances à partir de vos scripts d'entrée et générera des paquets optimisés pour votre application.

Si vous souhaitez migrer à partir d'une version antérieure de webpack, il est préférable de vérifier le fichier guide de migration

Le Tree Shaking (élimination des codes morts) n'est activé qu'en mode production.


Webpack 4, la nouvelle façon de regrouper les ressources

( Vous devez retirer votre pensée CommonsChunkPlugin de votre tête. )

! !! Pendant ce temps, la doc de webpack a été mise à jour, une section SplitChunks a été ajouté ! !!

Il suit une nouvelle philosophie :

Webpack 4 effectue désormais des optimisations automatiques par défaut. Il analyse votre graphe de dépendances et crée des paquets optimaux (sortie), en fonction des conditions suivantes :

  1. Un nouveau chunk peut être partagé OU les modules sont issus du dossier node_modules
  2. Le nouveau morceau serait plus grand que 30kb (avant min+gz)
  3. Nombre maximal de requêtes parallèles lors du chargement de morceaux à la demande <= 5
  4. Nombre maximal de requêtes parallèles au chargement initial de la page <= 3

Tout cela peut être modifié à l'aide du SplitChunksPlugin ! ( voir la documentation de SplitChunksPlugin )

Une explication plus détaillée sur la façon d'utiliser le nouveau optimization.splitChunks API.


CommonsChunkPlugin a été supprimé car il présente de nombreux problèmes :

  • Cela peut entraîner le téléchargement de plus de code que nécessaire.
  • C'est inefficace sur les morceaux asynchrones.
  • Il est difficile à utiliser.
  • La mise en œuvre est difficile à comprendre.

Le SplitChunksPlugin possède également d'excellentes propriétés :

  • Il ne télécharge jamais les modules inutiles (tant que vous n'imposez pas la fusion des morceaux par le nom).
  • Il fonctionne aussi efficacement sur les morceaux asynchrones.
  • Elle est activée par défaut pour les morceaux asynchrones.
  • Il gère le fractionnement des fournisseurs avec des blocs de fournisseurs multiples.
  • Il est plus facile à utiliser
  • Il ne repose pas sur des bidouillages de graphes de morceaux.
  • Principalement automatique

--> Source


En ce qui concerne votre problème vous voulez diviser tous les deps de l'entrée 1 et de l'entrée 2 en paquets séparés.

      optimization: {
        splitChunks: {
          cacheGroups: {   
            "entry1-bundle": {
              test: /.../,   // <-- use the test property to specify which deps go here
              chunks: "all",
              name: "entry1-bundle",
 /** Ignore minimum size, minimum chunks and maximum requests and always create chunks for this cache group */
              enforce: true,
              priority: ..  // use the priority, to tell where a shared dep should go
            },
            "entry2-bundle": {
              test: /..../, // <-- use the test property to specify which deps go here
              chunks: "all",
              name: "entry2-bundle",
              enforce: true,
              priority: ..
            }
          }
        }
      },

Si vous n'ajoutez pas l'entrée optimization:splitChunks, l'entrée réglage par défaut est le suivant :

splitChunks: {
  chunks: 'async',
  minSize: 30000,
  minRemainingSize: 0,
  maxSize: 0,
  minChunks: 1,
  maxAsyncRequests: 6,
  maxInitialRequests: 4,
  automaticNameDelimiter: '~',
  automaticNameMaxLength: 30,
  cacheGroups: {
    vendors: {
      test: /[\\/]node_modules[\\/]/,
      priority: -10
    },
    default: {
      minChunks: 2,
      priority: -20,
      reuseExistingChunk: true
    }
  }
}

Vous pouvez définir l'optimisation.splitChunks.cacheGroups. par défaut à false pour désactiver le par défaut groupe de cache, idem pour vendeurs groupe de cachettes !

Voici d'autres Exemples de configuration de SplitChunks avec une explication.


Mise en œuvre d'interfaces actualisées pour SplitChunksOptions , CachGroupOptions y Optimization peuvent être trouvés aquí .

Les définitions d'interface ci-dessous peuvent ne pas être exactes à 100%, mais elles sont bonnes pour un aperçu simple :

SplitChunksOptions interface

interface SplitChunksOptions {
    /** Select chunks for determining shared modules (defaults to \"async\", \"initial\" and \"all\" requires adding these chunks to the HTML) */
    chunks?: "initial" | "async" | "all" | ((chunk: compilation.Chunk) => boolean);
    /** Minimal size for the created chunk */
    minSize?: number;
    /** Minimum number of times a module has to be duplicated until it's considered for splitting */
    minChunks?: number;
    /** Maximum number of requests which are accepted for on-demand loading */
    maxAsyncRequests?: number;
    /** Maximum number of initial chunks which are accepted for an entry point */
    maxInitialRequests?: number;
    /** Give chunks created a name (chunks with equal name are merged) */
    name?: boolean | string | ((...args: any[]) => any);
    /** Assign modules to a cache group (modules from different cache groups are tried to keep in separate chunks) */
    cacheGroups?: false | string | ((...args: any[]) => any) | RegExp | { [key: string]: CacheGroupsOptions };
}

CacheGroupsOptions interface :

interface CacheGroupsOptions {
    /** Assign modules to a cache group */
    test?: ((...args: any[]) => boolean) | string | RegExp;
    /** Select chunks for determining cache group content (defaults to \"initial\", \"initial\" and \"all\" requires adding these chunks to the HTML) */
    chunks?: "initial" | "async" | "all" | ((chunk: compilation.Chunk) => boolean);
    /** Ignore minimum size, minimum chunks and maximum requests and always create chunks for this cache group */
    enforce?: boolean;
    /** Priority of this cache group */
    priority?: number;
    /** Minimal size for the created chunk */
    minSize?: number;
    /** Minimum number of times a module has to be duplicated until it's considered for splitting */
    minChunks?: number;
    /** Maximum number of requests which are accepted for on-demand loading */
    maxAsyncRequests?: number;
    /** Maximum number of initial chunks which are accepted for an entry point */
    maxInitialRequests?: number;
    /** Try to reuse existing chunk (with name) when it has matching modules */
    reuseExistingChunk?: boolean;
    /** Give chunks created a name (chunks with equal name are merged) */
    name?: boolean | string | ((...args: any[]) => any);
}

Optimization Interface

interface Optimization {
    /**
     *  Modules are removed from chunks when they are already available in all parent chunk groups.
     *  This reduces asset size. Smaller assets also result in faster builds since less code generation has to be performed.
     */
    removeAvailableModules?: boolean;
    /** Empty chunks are removed. This reduces load in filesystem and results in faster builds. */
    removeEmptyChunks?: boolean;
    /** Equal chunks are merged. This results in less code generation and faster builds. */
    mergeDuplicateChunks?: boolean;
    /** Chunks which are subsets of other chunks are determined and flagged in a way that subsets don’t have to be loaded when the bigger chunk has been loaded. */
    flagIncludedChunks?: boolean;
    /** Give more often used ids smaller (shorter) values. */
    occurrenceOrder?: boolean;
    /** Determine exports for each module when possible. This information is used by other optimizations or code generation. I. e. to generate more efficient code for export * from. */
    providedExports?: boolean;
    /**
     *  Determine used exports for each module. This depends on optimization.providedExports. This information is used by other optimizations or code generation.
     *  I. e. exports are not generated for unused exports, export names are mangled to single char identifiers when all usages are compatible.
     *  DCE in minimizers will benefit from this and can remove unused exports.
     */
    usedExports?: boolean;
    /**
     *  Recognise the sideEffects flag in package.json or rules to eliminate modules. This depends on optimization.providedExports and optimization.usedExports.
     *  These dependencies have a cost, but eliminating modules has positive impact on performance because of less code generation. It depends on your codebase.
     *  Try it for possible performance wins.
     */
    sideEffects?: boolean;
    /** Tries to find segments of the module graph which can be safely concatenated into a single module. Depends on optimization.providedExports and optimization.usedExports. */
    concatenateModules?: boolean;
    /** Finds modules which are shared between chunk and splits them into separate chunks to reduce duplication or separate vendor modules from application modules. */
    splitChunks?: SplitChunksOptions | false;
    /** Create a separate chunk for the webpack runtime code and chunk hash maps. This chunk should be inlined into the HTML */
    runtimeChunk?: boolean | "single" | "multiple" | RuntimeChunkOptions;
    /** Avoid emitting assets when errors occur. */
    noEmitOnErrors?: boolean;
    /** Instead of numeric ids, give modules readable names for better debugging. */
    namedModules?: boolean;
    /** Instead of numeric ids, give chunks readable names for better debugging. */
    namedChunks?: boolean;
    /** Defines the process.env.NODE_ENV constant to a compile-time-constant value. This allows to remove development only code from code. */
    nodeEnv?: string | false;
    /** Use the minimizer (optimization.minimizer, by default uglify-js) to minimize output assets. */
    minimize?: boolean;
    /** Minimizer(s) to use for minimizing the output */
    minimizer?: Array<Plugin | Tapable.Plugin>;
    /** Generate records with relative paths to be able to move the context folder". */
    portableRecords?: boolean;
}
}

1 votes

Donc en gros, parce que webpack 4 fonctionne comme par magie, je n'ai rien à faire pour diviser mes chunks ?

1 votes

Si vous avez une application plutôt petite, cela n'a aucun sens, car il devrait cracher des paquets optimaux. Je vous suggère de démarrer un nouveau projet webpack 4, mais de commencer très petit. Ajoutez les dépendances et compilez le bundle.

8voto

Carloluis Points 2722

J'ai deux fichiers d'entrée et je veux que seules les dépendances du premier soient incluses dans le chunk du vendeur. Les dépendances de la deuxième entrée doivent rester dans son propre bundle.

En supposant que vos points d'entrée sont main y secondary :

entry: {
    main: 'path-to/main.js',
    secondary: 'path-to/secondary.js'
}

Utilisation de webpack-4 Vous ne pouvez extraire que les vendors des modules de main mais laissent les modules tiers référencés dans les secondary à l'intérieur de ce chunk en utilisant le test fonction de la cacheGroups que vous voulez créer.

optimization: {
    splitChunks: {
        cacheGroups: {
            vendors: {
                name: 'vendors',
                chunks: 'all',
                reuseExistingChunk: true,
                priority: 1,
                enforce: true,
                test(module, chunks) {
                    const name = module.nameForCondition && module.nameForCondition();
                    return chunks.some(chunk => {
                        return chunk.name === 'main' && /[\\/]node_modules[\\/]/.test(name);
                    });
                }
            },
            secondary: {
                name: 'secondary',
                chunks: 'all',
                priority: 2,
                enforce: true,
                test(module, chunks) {
                    return chunks.some(chunk => chunk.name === 'secondary');
                }
            }
        }
    }
}

0 votes

J'ai fait une solution similaire et le résultat est un code qui ne s'exécute jamais - le runtime n'est pas ajouté.

0 votes

Afin d'ajouter des script séparés pour le runtime de webpack, vous devez définir la propriété runtime au sein de la propriété optimization objet : runtimeChunk: 'single' . Pouvez-vous partager votre config. et fournir plus de détails ? Vérifiez également ceci Démonstration de Webpack-4 pour l'orientation. J'espère que cela vous aidera !

0 votes

J'avais une configuration comme dans votre réponse - plusieurs points d'entrée et un chunkDef avec le même nom que le point d'entrée "secondaire". Peu importe ce que j'ai configuré dans le chunk runtime, je n'ai jamais pu exécuter le chunk secondaire à moins d'exécuter le chunk vendor, qui n'aurait pas dû être une dépendance du secondaire. J'ai passé quelques jours sur ce problème et j'abandonne. Dans mon cas, une multiconfiguration serait une meilleure solution.

4voto

Nick B. Points 21

Cela m'a pris un certain temps à comprendre, mais la réalisation clé pour moi était que les chunks dans webpack 4 prend maintenant une fonction, ce qui vous permet de n'inclure qu'une entrée spécifique. Je suppose qu'il s'agit d'un changement récent, car au moment où j'ai posté ce message, il ne figurait pas dans la documentation officielle.

splitChunks: {
  cacheGroups: {
    vendor: {
      name: 'vendor',
      chunks: chunk => chunk.name == 'main',
      reuseExistingChunk: true,
      priority: 1,
      test: module =>
        /[\\/]node_modules[\\/]/.test(module.context),
      minChunks: 1,
      minSize: 0,
    },
  },
},

4voto

Roger Points 499

Veuillez noter que j'ai corrigé le problème en modifiant ceci dans mon webpack.common.js :

  plugins: [
    new webpack.optimize.CommonsChunkPlugin({
      name: ['vendor']
    })
    ]

A ceci :

  optimization: {
      runtimeChunk: "single", // enable "runtime" chunk
      splitChunks: {
          cacheGroups: {
              vendor: {
                  test: /[\\/]node_modules[\\/]/,
                  name: "vendor",
                  chunks: "all"
              }
          }
      }
  },

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X