Gestire le dipendenze dei plugin jQuery in webpack

Sto usando Webpack nella mia applicazione, in cui creo due punti di ingresso - bundle.js per tutti i miei file/codici JavaScript, e vendors.js per tutte le librerie come jQuery e React. Cosa devo fare per utilizzare i plugin che hanno jQuery come dipendenze e voglio averli anche in vendors.js? E se questi plugin hanno più dipendenze?

Attualmente sto cercando di usare questo plugin jQuery qui - https://github.com/mbklein/jquery-elastic. La documentazione di Webpack menziona providePlugin e imports-loader. Ho usato providePlugin, ma ancora l'oggetto jQuery non è disponibile. Ecco come appare il mio webpack.config.js-

var webpack = require('webpack');
var bower_dir = __dirname + '/bower_components';
var node_dir = __dirname + '/node_modules';
var lib_dir = __dirname + '/public/js/libs';

var config = {
    addVendor: function (name, path) {
        this.resolve.alias[name] = path;
        this.module.noParse.push(new RegExp(path));
    },
    plugins: [
        new webpack.ProvidePlugin({
            $: "jquery",
            jquery: "jQuery",
            "window.jQuery": "jquery"
        }),
        new webpack.optimize.CommonsChunkPlugin('vendors', 'vendors.js', Infinity)
    ],
    entry: {
        app: ['./public/js/main.js'],
        vendors: ['react','jquery']
    },
    resolve: {
        alias: {
            'jquery': node_dir + '/jquery/dist/jquery.js',
            'jquery.elastic': lib_dir + '/jquery.elastic.source.js'
        }
    },
    output: {
        path: './public/js',
        filename: 'bundle.js'
    },
    module: {
        loaders: [
            { test: /\.js$/, loader: 'jsx-loader' },
            { test: /\.jquery.elastic.js$/, loader: 'imports-loader' }
        ]
    }
};
config.addVendor('react', bower_dir + '/react/react.min.js');
config.addVendor('jquery', node_dir + '/jquery/dist/jquery.js');
config.addVendor('jquery.elastic', lib_dir +'/jquery.elastic.source.js');

module.exports = config;

Ma nonostante questo, lancia ancora un errore nella console del browser:

Uncaught ReferenceError: jQuery non è definito

Allo stesso modo, quando uso l'imports-loader, lancia un errore,

require non è definito'

in questa linea:

var jQuery = require("jquery")

Tuttavia, potrei usare lo stesso plugin quando non lo aggiungo al mio file vendors.js e invece lo richiedo nel modo normale AMD come includo i miei altri file di codice JavaScript, come-

define(
[
    'jquery',
    'react',
    '../../common-functions',
    '../../libs/jquery.elastic.source'
],function($,React,commonFunctions){
    $("#myInput").elastic() //It works

});

Ma questo non è quello che voglio fare, perché questo significherebbe che jquery.elastic.source.js è in bundle con il mio codice JavaScript in bundle.js, e io voglio che tutti i miei plugin jQuery siano nel bundle vendors.js. Quindi come posso fare per ottenere questo?

Soluzione

Hai mescolato diversi approcci su come includere i moduli legacy del fornitore. Questo è il modo in cui lo affronterei:

1. Preferire CommonJS/AMD non minato rispetto a `dist

La maggior parte dei moduli collega la versione dist nel campo main del loro package.json. Mentre questo è utile per la maggior parte degli sviluppatori, per webpack è meglio dare un alias alla versione src perché in questo modo webpack è in grado di ottimizzare meglio le dipendenze (ad esempio quando si usa il DedupePlugin.

// webpack.config.js

module.exports = {
    ...
    resolve: {
        alias: {
            jquery: "jquery/src/jquery"
        }
    }
};

Tuttavia, nella maggior parte dei casi anche la versione dist funziona bene.


2. Usare il ProvidePlugin per iniettare globals impliciti

La maggior parte dei moduli legacy si basa sulla presenza di specifici globali, come fanno i plugin jQuery su $ o jQuery. In questo scenario si può configurare webpack, per anteporre var $ = require("jquery") ogni volta che incontra l'identificatore globale $.

var webpack = require("webpack");

    ...

    plugins: [
        new webpack.ProvidePlugin({
            $: "jquery",
            jQuery: "jquery"
        })
    ]

3. Usare il imports-loader per configurare this

Alcuni moduli legacy si basano sul fatto che this sia l'oggetto window. Questo diventa un problema quando il modulo viene eseguito in un contesto CommonJS dove this è uguale a module.exports. In questo caso è possibile sovrascrivere this con il imports-loader.

Esegui npm i imports-loader --save-dev e poi

module: {
    loaders: [
        {
            test: /[\/\\]node_modules[\/\\]some-module[\/\\]index\.js$/,
            loader: "imports-loader?this=>window"
        }
    ]
}

L'imports-loader può anche essere usato per iniettare manualmente variabili di ogni tipo. Ma la maggior parte delle volte il ProvidePlugin è più utile quando si tratta di globali impliciti.


4. Usa il imports-loader per disabilitare AMD

Ci sono moduli che supportano diversi stili di moduli, come AMD, CommonJS e legacy. Tuttavia, la maggior parte delle volte controllano prima la presenza di define e poi usano del codice bizzarro per esportare le proprietà. In questi casi, potrebbe essere utile forzare il percorso di CommonJS impostando define = false.

module: {
    loaders: [
        {
            test: /[\/\\]node_modules[\/\\]some-module[\/\\]index\.js$/,
            loader: "imports-loader?define=>false"
        }
    ]
}

5. Usare lo script-loader per importare globalmente gli script

Se non ti importa delle variabili globali e vuoi solo che gli script legacy funzionino, puoi anche usare lo script-loader. Esegue il modulo in un contesto globale, proprio come se li avessi inclusi tramite il tag <script>.


6. Usa noParse per includere grandi dist

Quando non c'è una versione AMD/CommonJS del modulo e si vuole includere il dist, si può contrassegnare questo modulo come noParse. Allora webpack includerà semplicemente il modulo senza analizzarlo, il che può essere usato per migliorare il tempo di compilazione. Questo significa che qualsiasi funzione che richieda il AST, come il ProvidePlugin, non funzionerà.

module: {
    noParse: [
        /[\/\\]node_modules[\/\\]angular[\/\\]angular\.js$/
    ]
}
Commentari (23)

Per l'accesso globale a jquery esistono diverse opzioni. Nel mio progetto webpack più recente, volevo un accesso globale a jquery, quindi ho aggiunto quanto segue alle dichiarazioni dei miei plugin:

 plugins: [
    new webpack.ProvidePlugin({
      $: "jquery",
      jQuery: "jquery"
    })
  ]

Questo significa che jquery è accessibile dall'interno del codice sorgente JavaScript tramite i riferimenti globali $ e jQuery.

Naturalmente, è necessario aver installato anche jquery tramite npm:

$ npm i jquery --save

Per un esempio funzionante di questo approccio, sentitevi liberi di fare un fork della mia app su github

Commentari (9)

Non so se ho capito molto bene quello che stai cercando di fare, ma ho dovuto usare plugin jQuery che richiedevano che jQuery fosse nel contesto globale (finestra) e ho messo il seguente nel mio entry.js:

var $ = require('jquery');
window.jQuery = $;
window.$ = $;

Devo solo richiedere dove voglio il jqueryplugin.min.js e window.$ viene esteso con il plugin come previsto.

Commentari (3)