Come raggruppare gli script del fornitore separatamente e richiederli secondo necessità con Webpack?


173

Sto cercando di fare qualcosa che credo dovrebbe essere possibile, ma non riesco davvero a capire come farlo solo dalla documentazione del webpack.

Sto scrivendo una libreria JavaScript con diversi moduli che possono dipendere o meno l'uno dall'altro. Inoltre, jQuery è utilizzato da tutti i moduli e alcuni di essi potrebbero aver bisogno di plugin jQuery. Questa libreria verrà quindi utilizzata su diversi siti Web diversi che potrebbero richiedere alcuni o tutti i moduli.

Definire le dipendenze tra i miei moduli è stato molto semplice, ma definire le loro dipendenze di terze parti sembra essere più difficile di quanto mi aspettassi.

Cosa vorrei ottenere : per ogni app voglio avere due file bundle uno con le dipendenze di terze parti necessarie e l'altro con i moduli necessari dalla mia libreria.

Esempio : immaginiamo che la mia libreria abbia i seguenti moduli:

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

E ho un'app (la vedo come un file di voce univoco) che richiede i moduli a, bec. Webpack per questo caso dovrebbe generare i seguenti file:

  • bundle fornitore : con jquery, jquery.plugin1 e jquery.ui;
  • pacchetto di siti Web : con i moduli a, bec;

Alla fine, preferirei avere jQuery come globale, quindi non ho bisogno di richiederlo su ogni singolo file (potrei richiederlo solo sul file principale, per esempio). E i plugin jQuery estenderebbero solo $ global nel caso fossero necessari (non è un problema se sono disponibili per altri moduli che non ne hanno bisogno).

Supponendo che ciò sia possibile, quale sarebbe un esempio di un file di configurazione del webpack per questo caso? Ho provato diverse combinazioni di caricatori, esterni e plugin sul mio file di configurazione, ma non riesco davvero a capire cosa stanno facendo e quali dovrei usare. Grazie!


2
qual è la tua soluzione? sei riuscito a trovare un approccio decente. Se è così, per favore, pubblicalo! grazie
GeekOnGadgets

Risposte:


140

nel mio file webpack.config.js (Versione 1,2,3), ho

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

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

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

nel mio array di plugin

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

Ora ho un file che aggiunge solo librerie di terze parti a un file come richiesto.

Se vuoi ottenere più granularità in cui separi i tuoi fornitori e i file dei punti di ingresso:

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

Nota che l'ordine dei plugin conta molto.

Inoltre, questo cambierà nella versione 4. Quando è ufficiale, aggiorno questa risposta.

Aggiornamento: indexOf modifica della ricerca per gli utenti di Windows


1
Non so se questo fosse già possibile quando ho pubblicato la mia domanda, ma è proprio quello che stavo cercando. Con questa soluzione non ho più bisogno di specificare il mio pezzo di entrata fornitore. Molte grazie!
Bensampaio,

1
isExternalin minChunksfatto la mia giornata. Come non è documentato? Ci sono aspetti negativi?
Wesley Schleumer de Góes,

Grazie, ma cambia userRequest.indexOf ('/ node_modules /') in userRequest.indexOf ('node_modules') per i percorsi di Windows
Kinjeiro,

@ WesleySchleumerdeGóes è documentato ma senza esempio options.minChunks (number|Infinity|function(module, count) -> boolean):non vedo ancora un aspetto negativo.
Rafael De Leon,

2
Questo non funzionerà quando si usano i caricatori, poiché sarà presente anche il percorso del caricatore module.userRequest(e probabilmente il caricatore è in node_modules). Il mio codice per isExternal():return typeof module.userRequest === 'string' && !!module.userRequest.split('!').pop().match(/(node_modules|bower_components|libraries)/);
cdauth,

54

Non sono sicuro di aver compreso appieno il tuo problema, ma dato che di recente ho riscontrato un problema simile, cercherò di aiutarti.

Pacchetto fornitore.

Per questo dovresti usare CommonsChunkPlugin . nella configurazione si specifica il nome del blocco (ad es. vendor) e il nome del file che verrà generato ( vendor.js).

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

Ora parte importante, ora devi specificare cosa significa vendorlibreria e lo fai in una sezione di entrata. Un altro elemento nella lista di voci con lo stesso nome del nome del pezzo appena dichiarato (ovvero "fornitore" in questo caso). Il valore di quella voce dovrebbe essere l'elenco di tutti i moduli che si desidera spostare in vendorbundle. nel tuo caso dovrebbe essere simile a:

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

JQuery come globale

Ha avuto lo stesso problema e l'ho risolto con ProvidePlugin . qui non si sta definendo l'oggetto globale ma il tipo di shurtcuts ai moduli. cioè puoi configurarlo così:

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

E ora puoi semplicemente usare $ovunque nel tuo codice - webpack lo convertirà automaticamente in

require('jquery')

Spero abbia aiutato. puoi anche guardare il mio file di configurazione del webpack che è qui

Adoro il webpack, ma sono d'accordo sul fatto che la documentazione non è la più bella del mondo ... ma hey .. all'inizio la gente diceva la stessa cosa sulla documentazione angolare :)


Modificare:

Per avere blocchi di fornitori specifici per i punti di accesso basta usare CommonsChunkPlugins più volte:

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

e quindi dichiarare diverse librerie extenral per file diversi:

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

Se alcune librerie si sovrappongono (e per la maggior parte di esse) tra i punti di ingresso, è possibile estrarle in un file comune usando lo stesso plugin solo con una configurazione diversa. Vedi questo esempio


Grazie mille per la tua risposta Questo è stato l'approccio migliore che ho visto finora, ma sfortunatamente non risolve ancora il mio problema ... Ho provato il tuo esempio e il file vendor.js conterrà comunque tutto il codice di 'jquery' e 'jquery.plugin1' anche se non sono richiesti da nessuno dei miei moduli. Ciò significa che alla fine verranno sempre caricati sul browser. Se ho molti plugin jquery, questo si tradurrà in un file molto grande anche se ne viene utilizzata solo la metà. Non c'è modo di includere 'jquery.plugin1' nel bundle del fornitore solo se è necessario?
Bensampaio,

grazie, quindi ho imparato anche qualcosa :) Ho aggiornato la mia risposta con la creazione di più blocchi di fornitori. forse adesso ti andrà meglio.
Michał Margiel,

4
Il problema con questa soluzione è che presuppone che io sappia quali sono le dipendenze per ogni pagina. Ma non posso prevedere che ... jQuery dovrebbe essere incluso in un pacchetto fornitore se è richiesto da uno dei moduli utilizzati nella pagina. Specificando che sul file di configurazione sarà sempre nel bundle del fornitore anche se non richiesto da alcun modulo utilizzato nella pagina, giusto? Fondamentalmente, non posso prevedere il contenuto dei pacchetti dei fornitori, altrimenti avrò un lavoro infernale perché non ho solo 2 pagine ne ho centinaia ... Hai il problema? Qualche idea? :)
Bensampaio,

Capisco quello che stai dicendo, ma non lo vedo come un problema. Se usi una nuova libreria in una pagina, aggiungila agli elenchi di librerie di un fornitore per quella pagina. Sono solo pochi personaggi. Comunque nella tua soluzione devi farlo specificando loader.Se non sai quali pagine useranno il tuo modulo appena creato, allora lascia che il plugin CommonChuncks estragga automaticamente le librerie comuni dai tuoi moduli.
Michał Margiel,

Come posso impostare il contesto separatamente per i file del fornitore?
harshes53,

44

Se sei interessato a raggruppare automaticamente i tuoi script separatamente da quelli dei fornitori:

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

Puoi leggere di più su questa funzione nella documentazione ufficiale .


4
Si noti che vendor : Object.keys(pkg.dependencies) non sempre funziona e dipende dalla modalità di creazione del pacchetto.
markyph,

1
Sei sempre dipendente da come package.jsonè impostato. Questa soluzione alternativa funziona nella maggior parte dei casi ma ci sono eccezioni in cui dovrai prendere una strada diversa. Potrebbe essere interessante pubblicare la tua risposta alla domanda per aiutare la community.
Freezystem,

16
Mi piace questo. Mi ha fatto fare pipì un po '.
cgatian,

3
noterai che includerà anche pacchetti che potresti non usare nemmeno nel tuo codice ... a causa del Object.keys(pkg.dependencies)raggruppamento di tutto !!!! diciamo che hai un sacco di caricatori elencati lì ... sì, saranno inclusi !!! quindi abbi cura di te ... separa con cura cosa è devDipendenza e cosa è dipendenza
Rafael Milewski,

1
@RafaelMilewski perché dovresti avere caricatori dependencies?
Pantaloni

13

Inoltre, non sono sicuro di comprendere appieno il tuo caso, ma qui è il frammento di configurazione per creare blocchi di fornitori separati per ciascuno dei tuoi bundle:

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

E link ai CommonsChunkPlugindocumenti: http://webpack.github.io/docs/list-of-plugins.html#commonschunkplugin


Credo che il problema con questa soluzione sia lo stesso di quello fornito da Michal. Stai supponendo che conosca le dipendenze del fornitore per bundle1 e bundle2, ma non ... Immagina di avere 200 bundle vorresti specificare tutto ciò nel file di configurazione? Usando il tuo esempio,react dovrebbe essere presente nel bundle del fornitore solo se esplicitamente richiesto da bundle1 e bundl2. Non avrei dovuto specificarlo nel file di configurazione ... Ha senso? Qualche idea?
Bensampaio,

@Anakin la domanda è: perché vuoi raggruppare lo strumento di 200 fornitori in un file separato. Vorrei solo raggruppare gli strumenti comuni in un file separato e mantenere il resto con i pacchetti del progetto.
maxisam

@Anakin Penso di avere a che fare con lo stesso problema, correggimi se sbaglio? stackoverflow.com/questions/35944067/...
pjdicke
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.