Gestión de la dependencia del plugin de jQuery en webpack

Estoy usando Webpack en mi aplicación, en la que creo dos puntos de entrada - bundle.js para todos mis archivos/códigos JavaScript, y vendors.js para todas las librerías como jQuery y React. ¿Qué hago para usar plugins que tienen jQuery como sus dependencias y quiero tenerlos también en vendors.js? ¿Y si esos plugins tienen múltiples dependencias?

Actualmente estoy intentando utilizar este plugin de jQuery - https://github.com/mbklein/jquery-elastic. La documentación de Webpack menciona providePlugin e imports-loader. He utilizado providePlugin, pero el objeto jQuery sigue sin estar disponible. Así es como se ve mi 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;

Pero a pesar de esto, sigue arrojando un error en la consola del navegador:

Uncaught ReferenceError: jQuery is not defined

Del mismo modo, cuando utilizo el cargador de importaciones, arroja un error,

require no está definido'

en esta línea:

var jQuery = require("jquery")

Sin embargo, podría usar el mismo plugin cuando no lo añado a mi archivo vendors.js y en su lugar lo requiero de la manera normal de AMD como incluyo mis otros archivos de código JavaScript, como-

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

});

Pero esto no es lo que quiero hacer, ya que esto significaría que jquery.elastic.source.js se incluye junto con mi código JavaScript en bundle.js, y quiero que todos mis plugins jQuery estén en el paquete vendors.js. Entonces, ¿cómo puedo hacer para lograr esto?

Solución

Usted ha mezclado diferentes enfoques sobre cómo incluir los módulos de proveedores heredados. Así es como yo lo abordaría:

1. Preferir CommonJS/AMD sin minificar sobre dist.

La mayoría de los módulos enlazan la versión dist en el campo main de su package.json. Aunque esto es útil para la mayoría de los desarrolladores, para webpack es mejor poner un alias a la versión src porque de esta manera webpack es capaz de optimizar mejor las dependencias (por ejemplo, cuando se utiliza el DedupePlugin).

// webpack.config.js

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

Sin embargo, en la mayoría de los casos la versión dist también funciona bien.


2. Utilice el ProvidePlugin para inyectar globales implícitos

La mayoría de los módulos heredados dependen de la presencia de globals específicos, como los plugins de jQuery hacen con $ o jQuery. En este caso puedes configurar webpack para que añada var $ = require("jquery") cada vez que encuentre el identificador global $.

var webpack = require("webpack");

    ...

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

3. Utilice el imports-loader para configurar this

Algunos módulos heredados dependen de que this sea el objeto window. Esto se convierte en un problema cuando el módulo se ejecuta en un contexto CommonJS donde this es igual a module.exports. En este caso puedes anular this con el imports-loader.

Ejecuta npm i imports-loader --save-dev y luego

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

El imports-loader también se puede utilizar para inyectar manualmente variables de todo tipo. Pero la mayoría de las veces el ProvidePlugin es más útil cuando se trata de globales implícitos.


4. Utilice el imports-loader para desactivar AMD

Hay módulos que soportan diferentes estilos de módulos, como AMD, CommonJS y legacy. Sin embargo, la mayoría de las veces primero comprueban la existencia de define y luego utilizan algún código peculiar para exportar las propiedades. En estos casos, podría ayudar forzar la ruta de CommonJS estableciendo define = false.

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

5. Utilice el script-loader para importar globalmente los scripts

Si no te importan las variables globales y sólo quieres que los scripts heredados funcionen, también puedes usar el script-loader. Este ejecuta el módulo en un contexto global, igual que si los hubieras incluido a través de la etiqueta <script>.


6. Utilice noParse para incluir discos grandes

Cuando no hay una versión AMD/CommonJS del módulo y quieres incluir el dist, puedes marcar este módulo como noParse. Entonces webpack simplemente incluirá el módulo sin analizarlo, lo que puede utilizarse para mejorar el tiempo de compilación. Esto significa que cualquier característica que requiera el AST, como el ProvidePlugin, no funcionará.

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

Para el acceso global a jquery existen varias opciones. En mi proyecto webpack más reciente, quería un acceso global a jquery, así que añadí lo siguiente a las declaraciones de mis plugins:

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

Esto significa que jquery es accesible desde el código fuente de JavaScript a través de las referencias globales $ y jQuery.

Por supuesto, también es necesario haber instalado jquery a través de npm:

$ npm i jquery --save

Para un ejemplo de trabajo de este enfoque por favor siéntase libre de fork mi aplicación en github

Comentarios (9)

No sé si entiendo muy bien lo que tratas de hacer, pero tuve que usar plugins de jQuery que requerían que jQuery estuviera en el contexto global (ventana) y puse lo siguiente en mi entry.js:

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

Luego solo tengo que requerir donde quiera el jqueryplugin.min.js y window.$ se extiende con el plugin como se esperaba.

Comentarios (3)