Webpack ProviderPlugin vs externes?

84

J'explore l'idée d'utiliser Webpack avec Backbone.js .

J'ai suivi le guide de démarrage rapide et j'ai une idée générale du fonctionnement de Webpack, mais je ne sais pas comment charger une bibliothèque de dépendances telle que jquery / backbone / underscore.

Devraient-ils être chargés en externe <script>ou est-ce quelque chose que Webpack peut gérer comme le shim de RequireJS?

Selon le doc webpack: shimming modules , ProvidePluginet externalssemblent être liés à cela (il en va de même pour le bundle!chargeur quelque part) mais je ne sais pas quand utiliser lequel.

Merci

Henri
la source

Réponses:

153

C'est à la fois possible: vous pouvez inclure des bibliothèques avec un <script>(c'est-à-dire utiliser une bibliothèque à partir d'un CDN) ou les inclure dans le bundle généré.

Si vous le chargez via <script>tag, vous pouvez utiliser l' externalsoption permettant d'écrire require(...)dans vos modules.

Exemple avec bibliothèque de 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");

Exemple avec bibliothèque incluse dans le bundle:

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");

Le ProvidePluginpeut mapper des modules à des variables (libres). Donc , vous pouvez définir: « Chaque fois que j'utiliser la variable (gratuit) à l' xyzintérieur d' un module (webpack) devrait mettre xyzà require("abc"). »

Exemple sans ProvidePlugin:

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

Exemple avec ProvidePlugin:

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

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

Sommaire:

  • Bibliothèque à partir du CDN: utilisez la <script>balise et l' externalsoption
  • Bibliothèque à partir du système de fichiers: incluez la bibliothèque dans le bundle. (Peut-être modifier les resolveoptions pour trouver la bibliothèque)
  • externals: Rendre les variables globales disponibles en tant que module
  • ProvidePlugin: Rendre les modules disponibles en tant que variables libres à l'intérieur des modules
Tobias K.
la source
Doit ajouter newavant webpack.ProvidePlugin webpack.github.io/docs/list-of-plugins.html
MK Yung
Pourquoi ne pas simplement utiliser le chargeur de script? C'est beaucoup plus facile, comme @dtothefp l'a expliqué
timaschew
Si mon fichier webpack.config est dans un dossier appelé javascript et à l'intérieur, j'ai un dossier appelé vendeur avec mon fichier jquery. ne devrait pas être le chemin. résoudre: {alias: {jquery: "vendor / jquery-1.10.2.js"}}. Ne fonctionne toujours pas pour moi en utilisant l'alias
me-me
3
Passez simplement un chemin absolu vers l'option alias. Si vous passez un chemin relatif, il est relatif à l'emplacement du require / import dans le webpack 1. Dans le webpack 2, il est relatif au fichier webpack.config.js resp. l'option de contexte.
Tobias K.
@TobiasK. Un chemin absolu ne coopère pas avec les exportations par défaut. J'obtiens un objet {__esModule: true, default: MY_DEFAULT_EXPORT}au lieu de MY_DEFAULT_EXPORTdans les fichiers.
mgol
25

Il est intéressant de noter que si vous utilisez le ProvidePluginen combinaison avec la externalspropriété, cela vous permettra d'avoir jQuerypassé la fermeture de votre module Webpack sans avoir à le faire explicitement require. Cela peut être utile pour refactoriser le code hérité avec un grand nombre de références de fichiers différents $.

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

maintenant dans index.js

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

aura une sortie compilée avec quelque chose comme ci-dessous passé dans la webpackBootstrapfermeture:

/******/ ([
/* 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;

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

Par conséquent, vous pouvez voir que $fait référence à la fenêtre globale / à jQuerypartir du CDN, mais est passé dans la fermeture. Je ne sais pas si c'est une fonctionnalité prévue ou un hack chanceux, mais cela semble bien fonctionner pour mon cas d'utilisation.

dtothefp
la source
vous n'avez besoin d'aucun plugin si vous n'y allez pas require/import. $fonctionnerait simplement parce qu'il atteindra la portée mondiale quoi qu'il arrive. Le ProviderPluginnécessite une analyse de l'AST, c'est donc un plugin coûteux et augmentera considérablement votre temps de construction. C'est donc fondamentalement un gaspillage.
faceyspacey.com
@dtohefp cette réponse est une aubaine. Pouvez-vous expliquer pourquoi a ProvidePluginrenvoyé un objet comme, à myModule.defaultmoins que j'aie ajouté le module aux externes? Je n'ai jamais su qu'il y aurait une relation directe.
Slbox
11

Je sais que c'est un ancien article, mais j'ai pensé qu'il serait utile de mentionner que le chargeur de script Webpack peut également être utile dans ce cas. À partir de la documentation Webpack:

"script: exécute un fichier JavaScript une fois dans un contexte global (comme dans la balise script), les exigences ne sont pas analysées."

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

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

J'ai trouvé cela particulièrement utile lors de la migration d'anciens processus de construction qui concatent les fichiers du fournisseur JS et les fichiers d'application ensemble. Un mot d'avertissement est que le chargeur de script ne semble fonctionner que par surcharge require()et ne fonctionne pas pour autant que je sache en étant spécifié dans un fichier webpack.config. Bien que beaucoup soutiennent que la surcharge requireest une mauvaise pratique, elle peut être très utile pour concilier le script du fournisseur et de l'application dans un seul bundle, et en même temps exposer JS Globals qui n'ont pas besoin d'être transformés en bundles webpack supplémentaires. Par exemple:

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

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

Cela rendrait $ .cookie, History et moment globalement disponibles à l'intérieur et à l'extérieur de ce bundle, et regrouperait ces bibliothèques du fournisseur avec le script main.js et tous ses requirefichiers D.

De plus, cette technique est utile:

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

qui utilise Bower, examinera le mainfichier dans chaque required bibliothèques package.json. Dans l'exemple ci-dessus, History.js n'a pas de mainfichier spécifié, le chemin d'accès au fichier est donc nécessaire.

dtothefp
la source