Webpack ProvidePlugin vs externals?

84

Ich untersuche die Idee, Webpack mit Backbone.js zu verwenden .

Ich habe die Kurzanleitung befolgt und habe eine allgemeine Vorstellung davon, wie Webpack funktioniert, aber ich bin mir nicht sicher, wie ich eine Abhängigkeitsbibliothek wie jquery / backbone / underscore laden soll.

Sollten sie extern mit geladen werden <script>oder kann Webpack damit wie RequireJS Shim umgehen?

Laut dem Webpack doc: Shimming-Module , ProvidePluginund externalsscheinen damit verwandt zu sein (so ist bundle!Loader irgendwo), aber ich kann nicht herausfinden, wann ich welche verwenden soll.

Vielen Dank

Henry
quelle

Antworten:

153

Beides ist möglich: Sie können Bibliotheken mit einem <script>(dh zum Verwenden einer Bibliothek aus einem CDN) oder in das generierte Bundle aufnehmen.

Wenn Sie es über ein <script>Tag laden , können Sie die externalsOption verwenden, um das Schreiben require(...)in Ihre Module zu ermöglichen.

Beispiel mit Bibliothek von CDN:

<script src="https://code.jquery.com/jquery-git2.min.js"></script>

// the artifial module "jquery" exports the global var "jQuery"
externals: { jquery: "jQuery" }

// inside any module
var $ = require("jquery");

Beispiel mit Bibliothek im Bundle enthalten:

copy `jquery-git2.min.js` to your local filesystem

// make "jquery" resolve to your local copy of the library
// i. e. through the resolve.alias option
resolve: { alias: { jquery: "/path/to/jquery-git2.min.js" } }

// inside any module
var $ = require("jquery");

Die ProvidePluginkönnen Module (freien) Variablen zuordnen. So definieren Sie könnte: „Jedes Mal , verwende ich die (freie) Variable xyzin einem Modul , das Sie (webpack) sollte so eingestellt xyzzu require("abc").“

Beispiel ohne ProvidePlugin:

// You need to require underscore before you can use it
var _ = require("underscore");
_.size(...);

Beispiel mit ProvidePlugin:

plugins: [
  new webpack.ProvidePlugin({
    "_": "underscore"
  }) 
]

// If you use "_", underscore is automatically required
_.size(...)

Zusammenfassung:

  • Bibliothek von CDN: Verwenden Sie <script>Tag und externalsOption
  • Bibliothek aus Dateisystem: Nehmen Sie die Bibliothek in das Bundle auf. (Ändern Sie möglicherweise die resolveOptionen, um die Bibliothek zu finden.)
  • externals: Stellen Sie globale Variablen als Modul zur Verfügung
  • ProvidePlugin: Stellen Sie Module als freie Variablen in Modulen zur Verfügung
Tobias K.
quelle
Sollte newvor webpack.ProvidePlugin webpack.github.io/docs/list-of-plugins.html
MK Yung
Warum nicht einfach den Script-Loader verwenden? Dies ist viel einfacher, wie @dtothefp erklärt hat
timaschew
Wenn sich meine Datei webpack.config in einem Ordner namens Javascript befindet und sich darin ein Ordner namens Vendor mit meiner JQuery-Datei befindet. sollte der Weg nicht sein. Lösung: {Alias: {jquery: "vendor / jquery-1.10.2.js"}}. Funktioniert immer noch nicht für mich mit dem Alias
ich-ich
3
Übergeben Sie einfach einen absoluten Pfad zur Alias-Option. Wenn Sie einen relativen Pfad übergeben, ist dieser relativ zum Speicherort des Erfordernisses / Imports in Webpack 1. In Webpack 2 ist er relativ zur Datei webpack.config.js bzw.. die Kontextoption.
Tobias K.
@TobiasK. Ein absoluter Pfad kooperiert nicht mit Standardexporten. Ich bekomme ein Objekt {__esModule: true, default: MY_DEFAULT_EXPORT}statt MY_DEFAULT_EXPORTin den Dateien.
mgol
25

Etwas Cooles ist, dass Sie, wenn Sie das ProvidePluginin Kombination mit der externalsEigenschaft verwenden, jQueryin den Abschluss Ihres Webpack-Moduls gelangen können, ohne dies explizit tun zu müssen require. Dies kann nützlich sein, um Legacy-Code mit vielen verschiedenen Dateireferenzen zu überarbeiten $.

//webpack.config.js
module.exports = {
  entry: './index.js',
  output: { 
    filename: '[name].js' 
  },
  externals: {
    jquery: 'jQuery'
  },
  plugins: [
    new webpack.ProvidePlugin({
      $: 'jquery',
    })
  ]
};

jetzt in index.js

console.log(typeof $ === 'function');

wird eine kompilierte Ausgabe mit etwas wie unten in den webpackBootstrapAbschluss übergeben haben:

/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {

    /* WEBPACK VAR INJECTION */(function($) {
        console.log(typeof $ === 'function');

    /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(1)))

/***/ },
/* 1 */
/***/ function(module, exports, __webpack_require__) {

    module.exports = jQuery;

/***/ }
/******/ ])

Daher können Sie sehen, dass dies $auf das globale / Fenster jQueryaus dem CDN verweist , aber an den Abschluss übergeben wird. Ich bin mir nicht sicher, ob dies beabsichtigte Funktionalität oder ein glücklicher Hack ist, aber es scheint für meinen Anwendungsfall gut zu funktionieren.

dtothefp
quelle
Sie brauchten kein Plugin, wenn Sie nicht dazu gingen require/import. $würde einfach funktionieren, weil es den globalen Geltungsbereich erreichen wird, egal was passiert. Das ProviderPluginerfordert das Parsen des AST, damit es ein teures Plugin ist und Ihre Build-Zeit spürbar verlängert. Es ist also im Grunde eine Verschwendung.
faceyspacey.com
@dtohefp Diese Antwort ist ein Glücksfall. Können Sie möglicherweise erklären, warum ProvidePluginein Objekt wie zurückgegeben wurde, es myModule.defaultsei denn, ich habe das Modul zu externen hinzugefügt? Ich hätte nie gedacht, dass es eine direkte Beziehung geben würde.
Slbox
11

Ich weiß, dass dies ein alter Beitrag ist, dachte aber, es wäre nützlich zu erwähnen, dass der Webpack-Skriptlader auch in diesem Fall nützlich sein kann. Aus den Webpack-Dokumenten:

"Skript: Führt eine JavaScript-Datei einmal im globalen Kontext aus (wie im Skript-Tag), erfordert, dass sie nicht analysiert werden."

http://webpack.github.io/docs/list-of-loaders.html

https://github.com/webpack/script-loader

Ich fand dies besonders nützlich, wenn ältere Build-Prozesse migriert werden, die JS-Herstellerdateien und App-Dateien zusammenfassen. Ein Wort der Warnung ist, dass der Skriptlader nur durch Überladung zu funktionieren scheint require()und nicht funktioniert, soweit ich dies anhand einer webpack.config-Datei feststellen kann. Obwohl viele argumentieren, dass Überladung requireeine schlechte Praxis ist, kann es sehr nützlich sein, Anbieter- und App-Skripte in einem Bundle zusammenzufassen und gleichzeitig JS Globals verfügbar zu machen, die nicht in zusätzliche Webpack-Bundles eingeteilt werden müssen. Beispielsweise:

require('script!jquery-cookie/jquery.cookie');
require('script!history.js/scripts/bundled-uncompressed/html4+html5/jquery.history');
require('script!momentjs');

require('./scripts/main.js');

Dies würde $ .cookie, History und moment innerhalb und außerhalb dieses Bundles global verfügbar machen und diese Herstellerbibliotheken mit dem Skript main.js und allen required-Dateien bündeln .

Nützlich bei dieser Technik ist auch:

resolve: {
  extensions: ["", ".js"],
  modulesDirectories: ['node_modules', 'bower_components']
},
plugins: [
  new webpack.ResolverPlugin(
    new webpack.ResolverPlugin.DirectoryDescriptionFilePlugin("bower.json", ["main"])
   )
]

Wenn Sie Bower verwenden, wird die mainDatei in jeder required-Bibliothek package.json angezeigt. Im obigen Beispiel ist in History.js keine mainDatei angegeben, daher ist der Pfad zur Datei erforderlich.

dtothefp
quelle