Administrar la dependencia del complemento jQuery en webpack

451

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 bibliotecas como jQuery y React. ¿Qué debo hacer para usar complementos que tienen jQuery como sus dependencias y quiero tenerlos también en vendors.js? ¿Qué pasa si esos complementos tienen dependencias múltiples?

Actualmente estoy tratando de usar este complemento jQuery aquí: https://github.com/mbklein/jquery-elastic . La documentación de Webpack menciona providePlugin e Importer - Loader . Usé providePlugin, pero aún así el objeto jQuery no está 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, todavía arroja un error en la consola del navegador:

Error de referencia no capturado: jQuery no está definido

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

requerir no está definido '

en esta linea:

var jQuery = require("jquery")

Sin embargo, podría usar el mismo complemento cuando no lo agrego a mi archivo vendors.js y, en su lugar, lo requiero de la forma AMD normal, como la forma en que 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 está incluido junto con mi código JavaScript en bundle.js, y quiero que todos mis complementos jQuery estén en el paquete vendors.js. Entonces, ¿cómo hago para lograr esto?

booleanhunter
fuente
3
No estoy seguro de si este es su problema, pero definitivamente necesita cambiar windows.jQuery a "window.jQuery": "jquery". Hay un error tipográfico en el sitio web de webpack donde supongo que obtuviste ese código.
Alex Hawkins
@AlexHawkins Oh sí, lo noté y lo arreglé. ¡Gracias por mencionarlo!
booleanhunter

Respuestas:

771

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

1. Prefiere CommonJS / AMD no minificados sobre dist

La mayoría de los módulos vinculan la distversión en el maincampo de su package.json. Si bien esto es útil para la mayoría de los desarrolladores, para webpack es mejor usar un alias de la srcversión porque de esta manera webpack puede optimizar mejor las dependencias (por ejemplo, cuando se usa DedupePlugin).

// webpack.config.js

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

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


2. Use ProvidePluginpara inyectar globals implícitos

La mayoría de los módulos heredados dependen de la presencia de globales específicos, como lo hacen los complementos jQuery en $o jQuery. En este escenario, puede configurar el paquete web, para anteponer var $ = require("jquery")cada vez que encuentra el $identificador global .

var webpack = require("webpack");

    ...

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

3. Use el cargador de importaciones para configurarthis

Algunos módulos heredados dependen de thisser el windowobjeto. Esto se convierte en un problema cuando el módulo se ejecuta en un contexto CommonJS donde thises igual module.exports. En este caso, puede anular thiscon el cargador de importaciones .

Corre npm i imports-loader --save-devy luego

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

El cargador de importaciones también se puede utilizar para inyectar manualmente variables de todo tipo. Pero la mayoría de las veces ProvidePlugines más útil cuando se trata de globales implícitos.


4. Use el cargador de importaciones para deshabilitar AMD

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

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

5. Utilice el cargador de guiones para importar guiones de forma global

Si no le importan las variables globales y solo quiere que los scripts heredados funcionen, también puede usar el cargador de scripts. Ejecuta el módulo en un contexto global, como si los hubiera incluido a través de la <script>etiqueta.


6. Use noParsepara incluir discos grandes

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

module: {
    noParse: [
        /[\/\\]node_modules[\/\\]angular[\/\\]angular\.js$/
    ]
}
Johannes Ewald
fuente
3
El ProvidePluginse aplica en todas las apariciones de los identificadores dados en todos los archivos. El imports-loaderse puede aplicar sobre determinados ficheros solamente, pero no se debe utilizar tanto para las mismas variables / dependencias.
Johannes Ewald
55
Estoy confundido por esto. ¿De dónde viene realmente el jquery? ¿Localmente o un CDN? Todo después de 3. no está claro si eso es necesario. ¿Cuáles son los pasos reales para integrar jquery en su proyecto usando webpack? ¿Omite la versión que está disponible a través de CDN?
HelpMeStackOverflowMyOnlyHope
2
Todo el contenido de un CDN está fuera del alcance del paquete web, por lo que esto se refiere a una instalación local de jquery. Jquery solo puede ser requerido, no hay pasos especiales necesarios para integrarlo. Esta pregunta trata sobre cómo integrar complementos jquery que dependen de la $variable global .
Johannes Ewald
8
Hizo todo esto, todavía no funcionaba; Luego agregó: "module": { "loaders": [ { test: require.resolve("jquery"), loader: "expose?$!expose?jQuery" },y funcionó bien.
musicformellons
55
Haga todo esto, ¿solo incluir un paquete jquery ?, qué pena. Voy a volver a mi trago
Rahul Gupta
89

Para el acceso global a jquery, existen varias opciones. En mi proyecto webpack más reciente, quería acceso global a jquery, así que agregué lo siguiente a mis declaraciones de complementos:

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

Esto significa que se puede acceder a jquery desde el código fuente de JavaScript a través de referencias globales $ y jQuery.

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

$ npm i jquery --save

Para un ejemplo práctico de este enfoque, no dude en bifurcar mi aplicación en github

arcseldon
fuente
2
¿Puede dejar un comentario si rebaja una votación explicando el motivo? La información anterior es precisa. Consulte mi aplicación de trabajo en: github.com/arcseldon/react-babel-webpack-starter-app utilizando el enfoque anterior.
arcseldon
No soy el votante, pero ¿tal vez esto se deba a que la ProvidePluginsolución que sugirió ya fue propuesta?
Léo Lam
@ LéoLam: gracias por tu comentario. aprecio su punto: deduje la respuesta a través de consultas separadas, y acabo de compartir el fragmento aquí que pensé que probablemente era más relevante para otros que desean emular lo que había hecho. Sin embargo, tienes razón, la respuesta oficial cubre esta opción.
arcseldon
1
Está funcionando pero recibo 2 advertencias: ./~/jQuery/dist/jquery.js There is another module with an equal name when case is ignored.y la misma con./~/jquery/dist/jquery.js
pistou
77
Necesitaba agregar 'window.jQuery': 'jquery'a esa lista para hacer que se cargara el paquete ms-signalr-client npm. También puse en 'window.$': 'jquery'buena medida :)
Sammi
57

No sé si entiendo muy bien lo que está tratando de hacer, pero tuve que usar los complementos 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.$ = $;

Solo tengo que requerir donde quiera jqueryplugin.min.jsy window.$se extiende con el complemento como se esperaba.

sanfilippopablo
fuente
2
Básicamente estaba cometiendo el error de usar ProvidePlugin junto con la condición noParse. Los complementos como ProvidePlugin no funcionan si hacemos NoParse, como se indica en el punto número 6 de la respuesta. Puedes ver ese error en el código
booleanhunter
Sí, descubrí que funciona de manera más consistente en todos los complementos que los complementos de suministro, etc., especialmente si está introduciendo 1.x angular a través del cargador de scripts.
Rastreador1
27

Conseguí que las cosas funcionaran bien durante la exposición $y jQuerycomo variables globales con Webpack 3.8.1 y lo siguiente.

Instale jQuery como una dependencia del proyecto. Puede omitir la @3.2.1instalación de la última versión o especificar otra versión.

npm install --save jquery@3.2.1

Instalar expose-loadercomo una dependencia de desarrollo si aún no está instalado.

npm install expose-loader --save-dev

Configure Webpack para cargar y exponer jQuery por nosotros.

// webpack.config.js
const webpack = require('webpack')

module.exports = {
  entry: [
    // entry bits
  ],
  output: {
    // output bits
  },
  module: {
    rules: [
      // any other rules
      {
        // Exposes jQuery for use outside Webpack build
        test: require.resolve('jquery'),
        use: [{
          loader: 'expose-loader',
          options: 'jQuery'
        },{
          loader: 'expose-loader',
          options: '$'
        }]
      }
    ]
  },
  plugins: [
    // Provides jQuery for other JS bundled with Webpack
    new webpack.ProvidePlugin({
      $: 'jquery',
      jQuery: 'jquery'
    })
  ]
}
Harlem Ardilla
fuente
55
Olvidó mencionar que necesita instalar expose-loaderpara que funcione correctamente npm install expose-loader --save-dev
Paulo Griiettner
44
Esto funcionó para mí 👍. jquery: 3.3.1, expose-loader: 0.7.5, webpack: 4.20.2
AKT
Lo expose-loadedhicieron por mí. No recibí un error en el momento de la compilación, pero en las herramientas para desarrolladores de Chrome. Eso está resuelto ahora.
Johan Vergeer
¡Gracias! Esto funcionó con "jquery": "^ 3.4.1" y "webpack": "^ 4.41.5". ¿Soy solo yo o hacer que jQuery funcione con Webpack no debería ser tan ridículo?
Carles Alcolea
18

Agregue esto a su matriz de complementos en webpack.config.js

new webpack.ProvidePlugin({
    'window.jQuery': 'jquery',
    'window.$': 'jquery',
})

entonces requieren jquery normalmente

require('jquery');

Si el dolor persiste al obtener otros scripts para verlo, intente colocarlo explícitamente en el contexto global a través de (en la entrada js)

window.$ = jQuery;
KhaledMohamedP
fuente
18

En su archivo webpack.config.js, agregue a continuación:

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

Instale jQuery usando npm:

$ npm i jquery --save

En el archivo app.js, agregue las siguientes líneas:

import $ from 'jquery';
window.jQuery = $;
window.$ = $;

Esto funcionó para mí. :)

ashwini
fuente
¿Cómo actuará esto si tienes por ej. app.js y jquery-module.js, que requieren jquery ?, para mí obtengo jquery13981378127 y jquery12389723198 como instancias en la ventana?
Martea
8

Intenté algunas de las respuestas proporcionadas, pero ninguna parecía funcionar. Entonces probé esto:

new webpack.ProvidePlugin({
    'window.jQuery'    : 'jquery',
    'window.$'         : 'jquery',
    'jQuery'           : 'jquery',
    '$'                : 'jquery'
});

Parece funcionar sin importar qué versión estoy usando

Cam Tullos
fuente
+1 parece ser un malentendido de que los globales agregados aquí se establecerán automáticamente en la ventana, no parece funcionar así, debe ser explícito.
James
Correcto, esto no lo agrega al objeto de ventana. Crea un alias de ventana dentro del paquete web. Entonces, si intenta usar $fuera del archivo requerido de un paquete web, no estará definido.
Cam Tullos
7

Esto funciona en webpack 3:

en el archivo webpack.config.babel.js:

resolve: {
    alias: {
         jquery: "jquery/src/jquery"
    },
 ....
}

Y usa ProvidePlugin

new webpack.ProvidePlugin({
        '$': 'jquery',
        'jQuery': 'jquery',
    })
SharpCoder
fuente
1
Para otros que son súper nuevos en webpack (¡como yo!) "Resolver" va en su webpack.config.js en module.exports = {} como entrada, salida, complementos, módulo, etc.
DavGarcia
1
Y el nuevo webpack.ProvidePlugin va dentro de la matriz de complementos.
DavGarcia
2

La mejor solución que encontré fue:

https://github.com/angular/angular-cli/issues/5139#issuecomment-283634059

Básicamente, debe incluir una variable ficticia en typings.d.ts, eliminar cualquier "importación * como $ de 'jquery" de su código y luego agregar manualmente una etiqueta al script jQuery en su html SPA. De esta forma, el paquete web no estará en su camino, y debería poder acceder a la misma variable global jQuery en todos sus scripts.

Fabricio
fuente
2

Esto funciona para mí en webpack.config.js

    new webpack.ProvidePlugin({
        $: 'jquery',
        jQuery: 'jquery',
        'window.jQuery': 'jquery'
    }),

en otro javascript o en HTML agregue:

global.jQuery = require('jquery');
JPRLCol
fuente
0

Editar: a veces desea usar el paquete web simplemente como un paquete de módulos para un proyecto web simple, para mantener su propio código organizado. La siguiente solución es para aquellos que solo desean que una biblioteca externa funcione como se espera dentro de sus módulos, sin tener que dedicar mucho tiempo a las configuraciones de paquetes web. (Editado después de -1)

Solución rápida y simple (es6) si todavía tiene dificultades o desea evitar la configuración externa / la configuración adicional del complemento webpack:

<script src="cdn/jquery.js"></script>
<script src="cdn/underscore.js"></script>
<script src="etc.js"></script>
<script src="bundle.js"></script>

dentro de un módulo:

const { jQuery: $, Underscore: _, etc } = window;
frdnrdb
fuente