Перемещение Webpack 4 CommonsChunkPlugin

Мне нужна помощь в переносе следующего кода с webpack 3 на 4.

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

У меня есть два файла ввода и вы хотите, чтобы в кусок поставщика включались только зависимости первого. Зависимости второй записи должны оставаться в своем собственном пакете.

Ответы

Ответ 1

Начиная с webpack v4 CommonsChunkPlugin устарел.

Мы возражали и удалены CommonsChunkPlugin, и заменили его с набором значений по умолчанию и легко перегружаемых API называется optimization.splitChunks.

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

Устаревшие

Вам больше не нужно использовать эти плагины:

DedupePlugin также был удален в v4

NoEmitOnErrorsPluginoptim.noEmitOnErrors (по умолчанию в режиме производства) ModuleConcatenationPluginoptim.concatenateModules (по умолчанию в режиме prod) NamedModulesPluginoptim.namedModules (по умолчанию в режиме dev)


Рекомендации по webpack 4

Используйте mini-css-extract-plugin вместо text-extract-plugin. Используйте webpack-bundle-analyzer чтобы анализировать собранный результат графически.

Сценарии ввода - это настоящие "Entry-Scripts" для вашего приложения, не добавляйте файлы поставщиков явно для entry: в webpack.config.js. Приложения SPA имеют одну запись, а приложения с несколькими страницами, такие как классические приложения ASP.NET MVC имеют несколько точек входа. Webpack построит график зависимостей из ваших сценариев ввода и создаст оптимизированные пакеты для вашего приложения.

Если вы хотите перейти с более старой версии веб-пакета, лучше всего проверить руководство по миграции

Встряхивание дерева (исключение мертвого кода) разрешено только в режиме производства.


Webpack 4, новый способ связывания активов

(Вы должны удалить мышление CommonsChunkPlugin со своей головы)

!!! Тем временем обновлен webpack doc, добавлен раздел SplitChunks !!!

Это следует новой философии:

Webpack 4 по умолчанию автоматически выполняет оптимизацию. Он анализирует график зависимостей и создает оптимальные пакеты (выходные данные) на основе следующих условий:

  1. Новый фрагмент может быть разделен или модули из папки node_modules
  2. Новый кусок будет больше 30 КБ (до min + gz)
  3. Максимальное количество параллельных запросов при загрузке кусков по запросу <= 5
  4. Максимальное количество параллельных запросов при начальной загрузке страницы <= 3

Все это можно настроить с помощью SplitChunksPlugin! (см. документацию SplitChunksPlugin)

Более подробное объяснение того, как использовать новый API optimization.splitChunks.



CommonsChunkPlugin был удален, потому что у него много проблем:

  • Это может привести к загрузке большего количества кода, чем требуется.
  • Это неэффективно для асинхронных кусков.
  • Его сложно использовать.
  • Реализация трудно понять.

SplitChunksPlugin также обладает отличными свойствами:

  • Он никогда не загружает ненужный модуль (пока вы не применяете слияние chunk через имя)
  • Он работает эффективно и в асинхронных кусках
  • Его по умолчанию для асинхронных фрагментов
  • Он обрабатывает разделение поставщиков с несколькими кусками поставщиков
  • Его проще в использовании
  • Он не полагается на hack graph hacks
  • В основном автоматический

-> Источник


Что касается вашей проблемы, вы хотите разбить все отпечатки entry1 и entry2 на отдельные пакеты.

      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: ..
            }
          }
        }
      },

Если вы не добавляете оптимизацию: запись splitChunks по умолчанию выглядит следующим образом:

splitChunks: {
    chunks: "async",
    minSize: 30000,
    minChunks: 1,
    maxAsyncRequests: 5,
    maxInitialRequests: 3,
    automaticNameDelimiter: '~',
    name: true,
    cacheGroups: {
        vendors: {
            test: /[\\/]node_modules[\\/]/,
            priority: -10
        },
    default: {
            minChunks: 2,
            priority: -20,
            reuseExistingChunk: true
        }
    }
}

Вы можете установить optim.splitChunks.cacheGroups. по умолчанию false, чтобы отключить группу кеша по умолчанию, то же самое для группы кэшей поставщиков !


Современные реализации интерфейса для SplitChunksOptions, CachGroupOptions и Optimization можно найти здесь.

Определения интерфейса ниже не могут быть на 100% точными, но хороши для простого обзора:

Интерфейс SplitChunksOptions

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 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 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 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 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 dont 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;
}
}

Ответ 2

У меня есть два файла ввода и вы хотите, чтобы в кусок поставщика включались только зависимости первого. Зависимости второй записи должны оставаться в своем собственном пакете.

Предполагая, что ваши точки входа являются main и secondary:

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

Использование Вы можете извлекать только модули vendors из main блока, но оставлять другие модули третьих сторон, на которые ссылается secondary внутри этого фрагмента, используя test функцию cacheGroups вы хотите создать.

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');
                }
            }
        }
    }
}

Ответ 3

Мне потребовалось некоторое время, чтобы понять, но ключевая реализация для меня заключалась в том, что аргумент chunks в webpack 4 теперь выполняет функцию, которая позволяет включать только конкретную запись. Я предполагаю, что это недавнее изменение, потому что во время публикации его не было в официальной документации.

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,
    },
  },
},