Wie kann ich Anbieterskripte separat bündeln und bei Bedarf mit Webpack anfordern?

173

Ich versuche etwas zu tun, von dem ich glaube, dass es möglich sein sollte, aber ich kann es wirklich nicht verstehen, nur aus der Webpack-Dokumentation.

Ich schreibe eine JavaScript-Bibliothek mit mehreren Modulen, die voneinander abhängen können oder nicht. Darüber hinaus wird jQuery von allen Modulen verwendet, und einige von ihnen benötigen möglicherweise jQuery-Plugins. Diese Bibliothek wird dann auf mehreren verschiedenen Websites verwendet, für die möglicherweise einige oder alle Module erforderlich sind.

Das Definieren der Abhängigkeiten zwischen meinen Modulen war sehr einfach, aber das Definieren der Abhängigkeiten von Drittanbietern scheint schwieriger zu sein, als ich erwartet hatte.

Was ich erreichen möchte : Für jede App möchte ich zwei Bundle-Dateien haben, eine mit den erforderlichen Abhängigkeiten von Drittanbietern und eine mit den erforderlichen Modulen aus meiner Bibliothek.

Beispiel : Stellen wir uns vor, meine Bibliothek enthält die folgenden Module:

  • a (erfordert: jquery, jquery.plugin1)
  • b (erfordert: jquery, a)
  • c (erfordert: jquery, jquery.ui, a, b)
  • d (erfordert: jquery, jquery.plugin2, a)

Und ich habe eine App (siehe sie als eindeutige Eintragsdatei), die die Module a, b und c benötigt. Webpack für diesen Fall sollte die folgenden Dateien generieren:

  • Vendor Bundle : mit jquery, jquery.plugin1 und jquery.ui;
  • Website-Bundle : mit Modulen a, b und c;

Am Ende würde ich es vorziehen, jQuery als global zu haben, damit ich es nicht für jede einzelne Datei benötigen muss (ich könnte es zum Beispiel nur für die Hauptdatei benötigen). Und jQuery-Plugins würden nur $ global erweitern, falls sie benötigt werden (es ist kein Problem, wenn sie anderen Modulen zur Verfügung stehen, die sie nicht benötigen).

Angenommen, dies ist möglich, was wäre ein Beispiel für eine Webpack-Konfigurationsdatei für diesen Fall? Ich habe verschiedene Kombinationen von Ladern, externen Geräten und Plugins in meiner Konfigurationsdatei ausprobiert, aber ich verstehe nicht wirklich, was sie tun und welche ich verwenden soll. Danke dir!

Bensampaio
quelle
2
Was ist Ihre Lösung? Haben Sie es geschafft, einen anständigen Ansatz zu finden? Wenn ja, bitte posten! danke
GeekOnGadgets

Antworten:

140

In meiner Datei webpack.config.js (Version 1,2,3) habe ich

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

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

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

in meinem Plugins-Array

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

Jetzt habe ich eine Datei, die nur nach Bedarf Bibliotheken von Drittanbietern zu einer Datei hinzufügt.

Wenn Sie detaillierter werden möchten, wo Sie Ihre Lieferanten und Einstiegspunktdateien trennen:

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

Beachten Sie, dass die Reihenfolge der Plugins sehr wichtig ist.

Dies wird sich auch in Version 4 ändern. Wenn dies offiziell ist, aktualisiere ich diese Antwort.

Update: Index der Suchänderung für Windows-Benutzer

Rafael De Leon
quelle
1
Ich weiß nicht, ob dies bereits möglich war, als ich meine Frage gestellt habe, aber das ist tatsächlich das, wonach ich gesucht habe. Mit dieser Lösung muss ich meinen Lieferanteneintragsblock nicht mehr angeben. Vielen Dank!
Bensampaio
1
isExternalin minChunksmachte meinen Tag. Wie ist das nicht dokumentiert? Es gibt Nachteile?
Wesley Schleumer de Góes
Danke, aber ändere userRequest.indexOf ('/ node_modules /') in userRequest.indexOf ('node_modules') für Windows-Pfade
Kinjeiro
@ WesleySchleumerdeGóes es ist dokumentiert, aber ohne Beispiel options.minChunks (number|Infinity|function(module, count) -> boolean):sehe ich noch keinen Nachteil.
Rafael De Leon
2
Dies funktioniert nicht, wenn Lader verwendet werden, da sich auch der Pfad des Laders in befindet module.userRequest(und der Lader sich wahrscheinlich in befindet node_modules). Mein Code für isExternal():return typeof module.userRequest === 'string' && !!module.userRequest.split('!').pop().match(/(node_modules|bower_components|libraries)/);
cdauth
54

Ich bin nicht sicher, ob ich Ihr Problem vollständig verstehe, aber da ich kürzlich ein ähnliches Problem hatte, werde ich versuchen, Ihnen zu helfen.

Vendor Bundle.

Sie sollten dafür CommonsChunkPlugin verwenden . In der Konfiguration geben Sie den Namen des Blocks (z. B. vendor) und den Dateinamen an, der generiert werden soll ( vendor.js).

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

Jetzt wichtiger Teil, müssen Sie jetzt angeben, was vendorBibliothek bedeutet, und das tun Sie in einem Eintragsabschnitt. Ein weiteres Element zur Eintragsliste mit demselben Namen wie der Name des neu deklarierten Blocks (in diesem Fall dh "Anbieter"). Der Wert dieses Eintrags sollte die Liste aller Module sein, die Sie zum Bündeln verschieben möchten vendor. in deinem Fall sollte es ungefähr so ​​aussehen:

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

JQuery als global

Hatte das gleiche Problem und löste es mit ProvidePlugin . Hier definieren Sie kein globales Objekt, sondern eine Art Shurtcuts zu Modulen. dh Sie können es so konfigurieren:

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

Und jetzt können Sie es einfach $überall in Ihrem Code verwenden - Webpack konvertiert das automatisch in

require('jquery')

Ich hoffe es hat geholfen. Sie können sich auch meine Webpack-Konfigurationsdatei ansehen, die sich hier befindet

Ich liebe Webpack, aber ich stimme zu, dass die Dokumentation nicht die schönste der Welt ist ... aber hey ... die Leute sagten am Anfang dasselbe über Angular-Dokumentation :)


Bearbeiten:

Verwenden Sie CommonsChunkPlugins mehrmals, um Einstiegspunkt-spezifische Hersteller-Chunks zu erhalten:

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

und deklarieren Sie dann verschiedene Erweiterungsbibliotheken für verschiedene Dateien:

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

Wenn sich einige Bibliotheken (und für die meisten von ihnen) zwischen Einstiegspunkten überschneiden, können Sie sie mit demselben Plugin und unterschiedlicher Konfiguration in eine gemeinsame Datei extrahieren. Siehe dieses Beispiel.

Michał Margiel
quelle
Vielen Dank für Ihre Antwort. Dies war der beste Ansatz, den ich bisher gesehen habe, aber leider löst er mein Problem immer noch nicht ... Ich habe Ihr Beispiel getestet und die Datei vendor.js enthält weiterhin den gesamten Code von 'jquery' und 'jquery.plugin1', auch wenn Sie werden von keinem meiner Module benötigt. Dies bedeutet, dass sie am Ende immer in den Browser geladen werden. Wenn ich viele JQuery-Plugins habe, führt dies zu einer sehr großen Datei, selbst wenn nur die Hälfte davon verwendet wird. Gibt es keine Möglichkeit, 'jquery.plugin1' nur dann in das Vendor Bundle aufzunehmen, wenn dies erforderlich ist?
Bensampaio
danke, also habe ich auch etwas gelernt :) Ich habe meine Antwort mit der Erstellung mehrerer Vendor-Chunks aktualisiert. Vielleicht passt es dir jetzt besser.
Michał Margiel
4
Das Problem bei dieser Lösung ist, dass davon ausgegangen wird, dass ich die Abhängigkeiten für jede Seite kenne. Ich kann jedoch nicht vorhersagen, dass ... jQuery nur dann in einem Vendor Bundle enthalten sein sollte, wenn es von einem der auf der Seite verwendeten Module benötigt wird. Wenn Sie angeben, dass die Konfigurationsdatei immer im Herstellerpaket enthalten ist, auch wenn sie von keinem auf der Seite verwendeten Modul benötigt wird, oder? Grundsätzlich kann ich den Inhalt von Vendor-Bundles nicht vorhersagen, sonst habe ich eine verdammt gute Arbeit, weil ich nicht nur 2 Seiten habe, sondern Hunderte ... Haben Sie das Problem? Irgendwelche Ideen? :)
Bensampaio
Ich verstehe, was Sie sagen, aber ich sehe das nicht als Problem. Wenn Sie eine neue Bibliothek auf einer Seite verwenden, fügen Sie sie einfach einer Lieferantenbibliotheksliste für diese Seite hinzu. Es sind nur wenige Zeichen. Wie auch immer in Ihrer Lösung müssen Sie dies tun, indem Sie den Loader angeben. Wenn Sie nicht wissen, auf welchen Seiten Ihr neu erstelltes Modul verwendet wird, lassen Sie das CommonChuncks-Plugin automatisch allgemeine Bibliotheken aus Ihren Modulen extrahieren.
Michał Margiel
Wie kann ich den Kontext für Herstellerdateien separat festlegen?
harsches 53
44

Falls Sie daran interessiert sind, Ihre Skripte automatisch getrennt von denen des Anbieters zu bündeln:

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

Weitere Informationen zu dieser Funktion finden Sie in der offiziellen Dokumentation .

Freezystem
quelle
4
Bitte beachten Sie, dass vendor : Object.keys(pkg.dependencies) dies nicht immer funktioniert und davon abhängt, wie das Paket erstellt wird.
Markyph
1
Sie sind immer darauf angewiesen, wie Sie package.jsoneingestellt sind. Diese Problemumgehung funktioniert in den meisten Fällen, es gibt jedoch Ausnahmen, in denen Sie einen anderen Weg einschlagen müssen. Es könnte interessant sein, eine eigene Antwort auf die Frage zu veröffentlichen, um der Community zu helfen.
Freezystem
16
Ich mag das. Ich musste ein wenig pinkeln.
Cgatian
3
Beachten Sie, dass es sogar Pakete enthält, die Sie möglicherweise gar nicht in Ihrem Code verwenden ... weil Object.keys(pkg.dependencies)alles gebündelt wird !!!! Nehmen wir an, Sie haben dort eine Reihe von Ladern aufgelistet ... ja, das wird enthalten sein !!! Also pass auf dich auf ... trenne vorsichtig, was devDependency und was dependency ist
Rafael Milewski
1
@ RafaelMilewski warum sollten Sie Lader haben dependencies?
Hose
13

Ich bin mir auch nicht sicher, ob ich Ihren Fall vollständig verstehe, aber hier ist ein Konfigurationsausschnitt, mit dem Sie für jedes Ihrer Bundles separate Vendor Chunks erstellen können:

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

Und Link zu CommonsChunkPluginDokumenten: http://webpack.github.io/docs/list-of-plugins.html#commonschunkplugin

Alex Fedoseev
quelle
Ich glaube, das Problem mit dieser Lösung ist das gleiche wie das von Michal. Sie gehen davon aus, dass ich die Herstellerabhängigkeiten für Bundle1 und Bundle2 kenne, aber ich weiß nicht ... Stellen Sie sich vor, Sie haben 200 Bundles. Möchten Sie all das in der Konfigurationsdatei angeben? In Ihrem Beispiel reactsollte es nur dann im Vendor-Bundle vorhanden sein, wenn es von Bundle1 und Bundl2 explizit benötigt wird. Ich sollte das nicht in der Konfigurationsdatei angeben müssen ... Ist das sinnvoll? Irgendwelche Ideen?
Bensampaio
@ Anakin die Frage ist, warum Sie 200 Anbieter Tool in einer separaten Datei bündeln möchten. Ich würde nur gängige Tools in einer separaten Datei bündeln und den Rest mit den Projektpaketen behalten.
Maxisam
@ Anakin Ich glaube, ich habe es mit dem gleichen Problem zu tun. Korrigieren Sie mich, wenn ich falsch liege? stackoverflow.com/questions/35944067/…
pjdicke