Як поєднати окремі сценарії постачальників і вимагати їх за потреби за допомогою Webpack?


173

Я намагаюся зробити щось, що, на мою думку, повинно бути можливим, але я справді не можу зрозуміти, як це зробити лише з документації на веб-пакет.

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

Визначити залежності між моїми модулями було дуже просто, але визначити їх сторонні залежності, здається, складніше, ніж я очікував.

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

Приклад : Давайте уявимо, що моя бібліотека має такі модулі:

  • a (потрібно: jquery, jquery.plugin1)
  • b (вимагає: jquery, a)
  • c (вимагає: jquery, jquery.ui, a, b)
  • d (потрібно: jquery, jquery.plugin2, a)

І у мене є додаток (бачити його як унікальний файл вступу), який вимагає модулів a, b і c. Вебпакет для цього випадку повинен генерувати такі файли:

  • комплект постачальників : з 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
Я не знаю, чи це вже було можливо, коли я розмістив своє запитання, але це справді те, що я шукав. З цим рішенням мені більше не потрібно вказувати свій вхідний постачальник. Дуже дякую!
bensampaio

1
isExternalу minChunksсвій день. Як це не зафіксовано? Є недоліки?
Веслі Шлейлер де Гос

Thx, але змініть userRequest.indexOf ('/ node_modules /') на userRequest.indexOf ('node_modules') для
патчів

@ WesleySchleumerdeGóes це задокументовано, але без прикладу 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комплект. у вашому випадку це має виглядати приблизно так:

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

JQuery як глобальний

Була така ж проблема і вирішено її за допомогою ProvidePlugin . тут ви визначаєте не глобальний об’єкт, а вид виправдання модулів. тобто ви можете налаштувати його так:

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

А тепер ви можете просто використовувати $будь-де в своєму коді - webpack автоматично перетворить це в

require('jquery')

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

Я люблю веб-пакет, але я згоден, що документація не найприємніша у світі ... але ей .. люди говорили те саме про кутову документацію на початку :)


Редагувати:

Щоб мати певні групи постачальників, просто використовуйте 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" у комплект постачальників, лише якщо це потрібно?
bensampaio

спасибі, тому я теж щось навчився :) Я оновив свою відповідь створенням декількох купок продавців. можливо, зараз воно вам краще підійде.
Michał Margiel

4
Проблема цього рішення полягає в тому, що він передбачає, що я знаю, які залежності існують для кожної сторінки. Але я не можу передбачити, що ... jQuery має бути включений у комплект постачальників лише тоді, коли цього вимагає один з модулів, що використовуються на сторінці. Вказавши, що у конфігураційному файлі він завжди буде в комплекті постачальника, навіть якщо цього не вимагає жоден модуль, який використовується на сторінці, правда? В основному, я не можу передбачити вміст пакетів постачальників, інакше у мене буде пекла робота, тому що у мене немає лише 2 сторінок, я маю сотні ... Чи розумієте ви проблему? Будь-які ідеї? :)
bensampaio

Я розумію, що ви говорите, але не бачу цього як проблеми. Якщо ви використовуєте нову бібліотеку на сторінці, просто додайте її до списків бібліотек постачальників для цієї сторінки. Це лише кілька символів. У вашому рішенні ви повинні це зробити, вказавши завантажувач. Якщо ви не знаєте, на яких сторінках буде використовуватися ваш щойно створений модуль - тоді нехай плагін CommonChuncks автоматично витягує загальні бібліотеки з ваших модулів.
Michał Margiel

Як я можу встановити контекст окремо для файлів постачальника?
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

16
Мені подобається це. Це змусило мене трохи поглянути.
cgatian

3
зауважте, що він навіть буде включати пакети, які ви, можливо, навіть не використовуєте у своєму коді ... через це Object.keys(pkg.dependencies)буде в комплекті все !!!! Нехай каже, у вас є купа вантажників, перелічених там ... так, це буде включено !!! так що будьте обережні ... розділіть обережно, що таке devDependence, а що залежність
Rafael Milewski

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. Мені не слід було б це вказувати у конфігураційному файлі ... Це має сенс? Будь-які ідеї?
bensampaio

@Anakin питання, чому ви хочете зв’язати інструмент 200 постачальників в окремий файл. Я б лише зв'язував загальні інструменти в окремий файл, а решту зберігав із пакетами проектів.
maxisam

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