Webpack 4 миграции CommonsChunkPlugin

Мне нужна помощь в переносе следующего кода из веб-пакета 3 в 4.

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

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

3 ответа

Начиная с версии 4, CommonsChunkPlugin устарел.

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

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

Устаревшие

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

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

NoEmitOnErrorsPlugin -> оптимизация. NoEmitOnErrors (включен по умолчанию в производственном режиме). ModuleConcatenationPlugin -> оптимизация .concatenateModules (включен по умолчанию в расширенном режиме).


Рекомендации по вебпаку 4

использование mini-css-extract-plugin вместо text-extract-plugin, использование webpack-bundle-analyzer проанализировать ваши выходные данные в графическом виде.

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

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

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


Webpack 4, новый способ объединения ресурсов

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

!!! Между тем обновлен документ Webpack, раздел SplitChunks был добавлен!!!

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

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

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

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

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



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

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

SplitChunksPlugin также имеет несколько замечательных свойств:

  • Он никогда не загружает ненужный модуль (если вы не используете слияние фрагментов по имени)
  • Работает эффективно и на асинхронных блоках
  • Он включен по умолчанию для асинхронных блоков
  • Он обрабатывает расщепление вендора с несколькими порциями вендора
  • Проще в использовании
  • Это не зависит от хаков графа
  • В основном автоматический

-> Источник


Что касается вашей проблемы, вы хотите разделить все группы записей 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
        }
    }
}

Вы можете установить оптимизацию.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'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 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 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;
}
}

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

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

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

Используя webpack-4 Вы можете извлечь только 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');
                }
            }
        }
    }
}

Обратите внимание, что я исправил проблему, изменив это в моем webpack.common.js:

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

К этому:

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

Мне потребовалось некоторое время, чтобы понять, но ключевым моментом для меня было то, что 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,
    },
  },
},
Другие вопросы по тегам