web-dev-qa-db-fra.com

Solution simple pour partager des modules chargés via NPM dans plusieurs bundles Browserify ou Webpack

Je me tire les cheveux ici à la recherche d'une solution simple pour partager le code, requise via NPM, dans plusieurs bundles Browserify ou Webpack. En pensant, existe-t-il un fichier «pont»?

Ce n’est pas dû au temps de compilation (je suis au courant de watchify) mais plutôt au désir d’extraire toutes mes bibliothèques spécifiques à un fournisseur dans vendor.js afin de réduire la taille de mon fichier app.js et de ne pas planter le navigateur avec des cartes à la source massives. De plus, je le trouve plus propre si le besoin de voir le texte compilé apparaissait. Et donc:

// vendor.js

require('react');
require('lodash');
require('other-npm-module');
require('another-npm-module');

Il est très important que le code soit chargé à partir de NPM, par opposition à Bower, ou enregistré dans un répertoire "fournisseur" afin d'être importé via un chemin relatif et identifié via un shim. J'aimerais conserver chaque référence de bibliothèque extraite via NPM, à l'exception de ma source d'application actuelle. 

Dans app.js, je garde tout mon code source et, via le tableau externals, exclut de la compilation les bibliothèques du fournisseur répertoriées ci-dessus:

// app.js 

var React = require('react');
var _     = require('lodash');

var Component = React.createClass()

// ...

Et puis dans index.html, j'ai besoin des deux fichiers

// index.html
<script src='vendor.js'></script>
<script src='app.js'></script>

Avec Browserify ou Webpack, comment puis-je faire en sorte que app.js puisse "voir" le module chargé via npm? Je suis conscient de la création d’un ensemble d’externes, puis du référencement du fichier direct (par exemple, node_modules) via un alias, mais j’espère trouver une solution plus automatique et moins similaire à "Require.js". 

En gros, je me demande s’il est possible de relier les deux pour que app.js puisse regarder à l’intérieur de vendor.js afin de résoudre les dépendances. Cela semble être une opération simple et directe, mais je n'arrive pas à trouver de réponse nulle part sur ce large réseau. 

Merci!

16
cnp

Avec Webpack, vous utiliseriez plusieurs points d'entrée et le CommonChunkPlugin .

Extrait des webpack docs :


Pour diviser votre application en 2 fichiers, par exemple app.js et vendor.js, vous pouvez exiger les fichiers du fournisseur au format vendor.js. Ensuite, transmettez ce nom au CommonChunkPlugin comme indiqué ci-dessous.

module.exports = {
  entry: {
    app: "./app.js",
    vendor: ["jquery", "underscore", ...],
  },
  output: {
    filename: "bundle.js"
  },
  plugins: [
    new webpack.optimize.CommonsChunkPlugin(
        /* chunkName= */"vendor",
        /* filename= */"vendor.bundle.js"
    )
  ]
};

Cela supprimera tous les modules de la partie fournisseur de la partie application. Le bundle.js ne contiendra plus que le code de votre application, sans aucune de ses dépendances. Ce sont en vendor.bundle.js.

Dans votre page HTML, chargez vendor.bundle.js avant bundle.js.

<script src="vendor.bundle.js"></script>
<script src="bundle.js"></script>

11
Johannes Ewald

Il est recommandé de répertorier tous les fichiers/modules du fournisseur et d’utiliser CommonChunkPlugin . Cela devient cependant assez fastidieux et sujet aux erreurs.

Considérez les modules NPM suivants: fastclick et mprogress. Comme ils n’ont pas adopté le format CommonJS module, vous devez donner un coup de main à Webpack, comme ceci:

require('imports?define=>false!fastclick')(document.body);
require('mprogress/mprogress.min.css');
var Mprogress = require('mprogress/mprogress.min.js'),

En supposant maintenant que vous souhaitiez à la fois fastclick et mprogress dans votre bloc fournisseur, vous devriez probablement essayer ceci:

module.exports = {
  entry: {
    app: "./app.js",
    vendor: ["fastclick", "mprogress", ...]

Hélas, ça ne marche pas. Vous devez faire correspondre les appels à require():

module.exports = {
  entry: {
    app: "./app.js",
    vendor: [
      "imports?define=>false!fastclick", 
      "mprogress/mprogress.min.css", 
      "mprogress/mprogress.min.js", 
      ...]

Il vieillit, même avec quelques ruses resolve.alias. Voici ma solution de contournement. CommonChunkPlugin vous permet de spécifier un rappel qui vous indiquera si vous souhaitez ou non qu'un module soit inclus dans le bloc fournisseur. Si votre propre code source se trouve dans un répertoire src spécifique et que le reste se trouve dans le répertoire node_modules, rejetez simplement les modules en fonction de leur chemin:

var node_modules_dir = path.join(__dirname, 'node_modules'),
    app_dir          = path.join(__dirname, 'src');

module.exports = {
  entry: {
    app: "./app.js",
  },
  output: {
    filename: "bundle.js"
  },
  plugins: [
    new webpack.optimize.CommonsChunkPlugin(
      /* chunkName= */"vendor",
      /* filename= */"vendor.bundle.js"
      function (module, count) {
       return module.resource && module.resource.indexOf(app_dir) === -1;
      }
    )
  ]
};

module.resource est le chemin du module considéré. Vous pouvez également faire le contraire et inclure uniquement le module s'il est à l'intérieur de node_modules_dir, c'est-à-dire:

       return module.resource && module.resource.indexOf(node_modules_dir) === 0;

mais dans ma situation, je dirais plutôt: " place tout ce qui ne se trouve pas dans mon arbre source dans un bloc fournisseur ".

J'espère que cela pourra aider.

26
sebastien.b
// vendor anything coming from node_modules
minChunks: module => /node_modules/.test(module.resource)

Source: https://github.com/webpack/webpack/issues/2372#issuecomment-213149173

0
Alex