Как отдельно связать вендорные скрипты и требовать их по мере необходимости с Webpack?


173

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

Я пишу библиотеку JavaScript с несколькими модулями, которые могут зависеть или не зависеть друг от друга. Кроме того, jQuery используется всеми модулями, и некоторые из них могут нуждаться в плагинах jQuery. Затем эта библиотека будет использоваться на нескольких разных веб-сайтах, для которых могут потребоваться некоторые или все модули.

Определить зависимости между моими модулями было очень просто, но определить их сторонние зависимости оказалось сложнее, чем я ожидал.

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

Пример : давайте представим, что моя библиотека имеет следующие модули:

  • a (требуется: jquery, jquery.plugin1)
  • б (требуется: JQuery, а)
  • c (требуется: jquery, jquery.ui, a, b)
  • d (требуется: jquery, jquery.plugin2, a)

И у меня есть приложение (рассматривается как уникальный входной файл), для которого требуются модули a, b и c. Webpack для этого случая должен сгенерировать следующие файлы:

  • комплект поставщиков : с jquery, jquery.plugin1 и jquery.ui;
  • комплектация сайта : с модулями a, b и c;

В конце концов, я бы предпочел использовать jQuery в качестве глобального, поэтому мне не нужно требовать его для каждого отдельного файла (например, я могу требовать его только для основного файла). А плагины jQuery просто расширили бы $ global в случае необходимости (это не проблема, если они доступны для других модулей, которым они не нужны).

Предполагая, что это возможно, что будет примером файла конфигурации веб-пакета для этого случая? Я попробовал несколько комбинаций загрузчиков, внешних модулей и плагинов в моем файле конфигурации, но я не совсем понимаю, что они делают и какие мне следует использовать. Спасибо!


2
каково ваше решение? Вам удалось найти достойный подход. Если так, пожалуйста, отправьте это! спасибо
— GeekOnGadgets

Ответы:


140

в моем файле webpack.config.js (версия 1,2,3), у меня есть

function isExternal(module) {
  var context = module.context;

  if (typeof context !== 'string') {
    return false;
  }

  return context.indexOf('node_modules') !== -1;
}

в моем массиве плагинов

plugins: [
  new CommonsChunkPlugin({
    name: 'vendors',
    minChunks: function(module) {
      return isExternal(module);
    }
  }),
  // Other plugins
]

Теперь у меня есть файл, который только добавляет сторонние библиотеки в один файл по мере необходимости.

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

plugins: [
  new CommonsChunkPlugin({
    name: 'common',
    minChunks: function(module, count) {
      return !isExternal(module) && count >= 2; // adjustable
    }
  }),
  new CommonsChunkPlugin({
    name: 'vendors',
    chunks: ['common'],
    // or if you have an key value object for your entries
    // chunks: Object.keys(entry).concat('common')
    minChunks: function(module) {
      return isExternal(module);
    }
  })
]

Обратите внимание, что порядок плагинов имеет большое значение.

Кроме того, это изменится в версии 4. Когда это будет официально, я обновлю этот ответ.

Обновление: изменение индекса поиска для пользователей Windows


1
Я не знаю, было ли это уже возможно, когда я отправил свой вопрос, но это действительно то, что я искал. С этим решением мне больше не нужно указывать свой блок ввода поставщика. Большое спасибо!
— Бенсампайо

1
isExternalв minChunksсделал мой день. Как это не задокументировано? Есть минусы?
— Уэсли Шлеймер де Гоес

Спасибо, но измените userRequest.indexOf ('/ node_modules /') на userRequest.indexOf ('node_modules') для оконных
— патчей

@ WesleySchleumerdeGeses это задокументировано, но без примера options.minChunks (number|Infinity|function(module, count) -> boolean):я пока не вижу обратной стороны.
— Рафаэль Де Леон

2
Это не будет работать при использовании загрузчиков, так как путь к загрузчику также будет в module.userRequest(и загрузчик, вероятно, находится в node_modules). Мой код для isExternal():return typeof module.userRequest === 'string' && !!module.userRequest.split('!').pop().match(/(node_modules|bower_components|libraries)/);
— cdauth

54

Я не уверен, полностью ли я понимаю вашу проблему, но, поскольку у меня недавно была похожая проблема, я постараюсь помочь вам.

Поставщик комплектации.

Вы должны использовать CommonsChunkPlugin для этого. в конфигурации вы указываете имя чанка (например vendor) и имя файла, который будет сгенерирован ( vendor.js).

new webpack.optimize.CommonsChunkPlugin("vendor", "vendor.js", Infinity),

Теперь важная часть, теперь вы должны указать, что означает vendorбиблиотека, и вы делаете это в разделе ввода. Еще один элемент в списке записей с тем же именем, что и имя только что объявленного чанка (в данном случае «vendor»). Значением этой записи должен быть список всех модулей, которые вы хотите переместить в vendorпакет. в вашем случае это должно выглядеть примерно так:

entry: {
    app: 'entry.js',
    vendor: ['jquery', 'jquery.plugin1']
}

JQuery как глобальный

Была такая же проблема и решена с помощью ProvidePlugin . здесь вы определяете не глобальный объект, а какие-то помехи для модулей. то есть вы можете настроить его так:

new webpack.ProvidePlugin({
    $: "jquery"
})

И теперь вы можете просто использовать в $любом месте вашего кода - веб-пакет автоматически преобразует это в

require('jquery')

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

Я люблю webpack, но я согласен, что документация не самая лучшая в мире ... но эй .. люди говорили то же самое об Angular документации в начале :)


Редактировать:

Чтобы иметь чанки вендоров для конкретной точки входа, просто используйте CommonsChunkPlugins несколько раз:

new webpack.optimize.CommonsChunkPlugin("vendor-page1", "vendor-page1.js", Infinity),
new webpack.optimize.CommonsChunkPlugin("vendor-page2", "vendor-page2.js", Infinity),

а затем объявите разные внешние библиотеки для разных файлов:

entry: {
    page1: ['entry.js'],
    page2: ['entry2.js'],
    "vendor-page1": [
        'lodash'
    ],
    "vendor-page2": [
        'jquery'
    ]
},

Если некоторые библиотеки перекрываются (и для большинства из них) между точками входа, вы можете извлечь их в общий файл, используя один и тот же плагин только с другой конфигурацией. Смотрите этот пример.


Большое спасибо за ваш ответ. Это был лучший подход, который я видел до сих пор, но, к сожалению, он по-прежнему не решает мою проблему ... Я проверил ваш пример, и файл vendor.js будет по-прежнему содержать весь код из jquery и jquery.plugin1, даже если они не требуются ни одним из моих модулей. Это означает, что в конце они всегда будут загружены в браузер. Если у меня будет много плагинов jquery, это приведет к очень большому файлу, даже если используется только половина из них. Нет ли способа включить 'jquery.plugin1' в комплект поставщика, только если это необходимо?
— Бенсампайо

спасибо, так что я тоже кое-что узнал :) Я обновил свой ответ созданием нескольких вендоров. Может быть, теперь это будет лучше для вас.
— Михал Маргиэль

4
Проблема с этим решением состоит в том, что оно предполагает, что я знаю, каковы зависимости для каждой страницы. Но я не могу предсказать, что ... jQuery должен быть включен только в комплект поставщика, если этого требует один из модулей, используемых на странице. Указав, что в файле конфигурации он всегда будет в комплекте поставщика, даже если он не требуется ни для одного модуля, используемого на странице, верно? По сути, я не могу предсказать содержимое пакетов поставщиков, иначе у меня будет адская работа, потому что у меня нет только двух страниц, у меня есть сотни ... У вас есть проблема? Любые идеи? :)
— bensampaio

Я понимаю, что вы говорите, но я не вижу в этом проблемы. Если вы используете новую библиотеку на странице, просто добавьте ее в списки библиотек поставщиков для этой страницы. Это всего лишь несколько символов. В любом случае, в вашем решении вы должны сделать это, указав loader. Если вы не знаете, на каких страницах будет использоваться ваш вновь созданный модуль, - тогда позвольте плагину CommonChuncks автоматически извлекать общие библиотеки из ваших модулей.
— Михал Маргиэль

Как я могу установить контекст отдельно для файлов вендора?
— harshes53

44

Если вы заинтересованы в автоматическом объединении ваших скриптов отдельно от вендоров:

var webpack = require('webpack'),
    pkg     = require('./package.json'),  //loads npm config file
    html    = require('html-webpack-plugin');

module.exports = {
  context : __dirname + '/app',
  entry   : {
    app     : __dirname + '/app/index.js',
    vendor  : Object.keys(pkg.dependencies) //get npm vendors deps from config
  },
  output  : {
    path      : __dirname + '/dist',
    filename  : 'app.min-[hash:6].js'
  },
  plugins: [
    //Finally add this line to bundle the vendor code separately
    new webpack.optimize.CommonsChunkPlugin('vendor', 'vendor.min-[hash:6].js'),
    new html({template : __dirname + '/app/index.html'})
  ]
};

Вы можете прочитать больше об этой функции в официальной документации .


4
Обратите внимание, что vendor : Object.keys(pkg.dependencies) это не всегда работает и зависит от того, как собран пакет.
— Маркиф

1
Ты всегда зависишь от того, как ты package.jsonнастроен. Этот обходной путь работает в большинстве случаев, но есть исключения, когда вам придется выбрать другой путь. Может быть интересно опубликовать свой ответ на вопрос, чтобы помочь сообществу.
— Freezystem

17
Мне это нравится. Это заставило меня немного пописать.
— cgatian

3
обратите внимание, что он даже будет включать пакеты, которые вы, возможно, даже не используете в своем коде ... из-за того, что Object.keys(pkg.dependencies)будет связывать все !!!! пусть говорит, что там есть куча загрузчиков ... да, это будет включено !!! так что будьте осторожны ... разделите осторожно, что такое devDependency и что такое зависимость
— Рафаэль Милевски

1
@RafaelMilewski зачем вам грузчики dependencies?
— Брюки

13

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

entry: {
  bundle1: './build/bundles/bundle1.js',
  bundle2: './build/bundles/bundle2.js',
  'vendor-bundle1': [
    'react',
    'react-router'
  ],
  'vendor-bundle2': [
    'react',
    'react-router',
    'flummox',
    'immutable'
  ]
},

plugins: [
  new webpack.optimize.CommonsChunkPlugin({
    name: 'vendor-bundle1',
    chunks: ['bundle1'],
    filename: 'vendor-bundle1.js',
    minChunks: Infinity
  }),
  new webpack.optimize.CommonsChunkPlugin({
    name: 'vendor-bundle2',
    chunks: ['bundle2'],
    filename: 'vendor-bundle2-whatever.js',
    minChunks: Infinity
  }),
]

И ссылка на CommonsChunkPluginдокументы: http://webpack.github.io/docs/list-of-plugins.html#commonschunkplugin


Я считаю, что проблема с этим решением та же, что и у Михала. Вы предполагаете, что я знаю зависимости от поставщиков для bundle1 и bundle2, но я не знаю ... Представьте, что у вас есть 200 пакетов, вы хотите указать все это в файле конфигурации? Используя ваш пример, он reactдолжен присутствовать в комплекте поставщика только в том случае, если это явно требуется для bundle1 и bundl2. Мне не нужно указывать это в файле конфигурации ... Имеет ли это смысл? Любые идеи?
— Бенсампайо

@ Anakin вопрос в том, почему вы хотите объединить 200 инструментов поставщиков в отдельный файл. Я бы только объединял общие инструменты в отдельный файл, а оставшиеся оставлял с пакетами проекта.
— Maxisam

@ Анакин Я думаю, что я имею дело с той же проблемой, поправьте меня, если я не прав? stackoverflow.com/questions/35944067/…
— pjdicke
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.