Gerenciando a dependência do plugin jQuery no webpack

451

Estou usando o Webpack no meu aplicativo, no qual crio dois pontos de entrada - bundle.js para todos os meus arquivos / códigos JavaScript e vendors.js para todas as bibliotecas, como jQuery e React. O que faço para usar plug-ins que possuem jQuery como dependências e quero tê-los também em vendors.js? E se esses plugins tiverem várias dependências?

Atualmente, estou tentando usar este plugin jQuery aqui - https://github.com/mbklein/jquery-elastic . A documentação do Webpack menciona allowPlugin e imports-loader. Eu usei o allowPlugin, mas ainda o objeto jQuery não está disponível. Aqui está como meu webpack.config.js se parece:

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;

Mas, apesar disso, ele ainda gera um erro no console do navegador:

ReferenceError não capturado: jQuery não está definido

Da mesma forma, quando eu uso o carregador de importações, ele gera um erro,

exigir não está definido '

nesta linha:

var jQuery = require("jquery")

No entanto, eu poderia usar o mesmo plug-in quando não o adicionei ao meu arquivo vendors.js e, em vez disso, exigi-lo da maneira AMD normal, como incluo meus outros arquivos de código JavaScript, como:

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

});

Mas não é isso que eu quero fazer, pois isso significa que o jquery.elastic.source.js está incluído no meu código JavaScript no bundle.js e quero que todos os meus plugins jQuery estejam no pacote vendors.js. Então, como faço para conseguir isso?

booleanhunter
fonte
3
Não tenho certeza se esse é o seu problema, mas você definitivamente precisa alterar o windows.jQuery para "window.jQuery": "jquery". Há um erro de digitação no site da webpack, de onde suponho que você tenha recebido esse código.
Alex Hawkins
@AlexHawkins Ah, sim, eu notei isso e consertei. Obrigado por apontar isso!
Booleanhunter 26/05

Respostas:

770

Você combinou diferentes abordagens de como incluir módulos de fornecedores herdados. É assim que eu lidaria com isso:

1. Prefira CommonJS / AMD não minificado em vez de dist

A maioria dos módulos vincula a distversão no maincampo deles package.json. Embora isso seja útil para a maioria dos desenvolvedores, para o webpack é melhor usar o alias da srcversão, pois assim o webpack pode otimizar melhor as dependências (por exemplo, ao usar o DedupePlugin).

// webpack.config.js

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

No entanto, na maioria dos casos, a distversão também funciona bem.


2. Use o ProvidePluginpara injetar globais implícitos

A maioria dos módulos herdados depende da presença de globais específicos, como os plugins jQuery em $ou jQuery. Nesse cenário, você pode configurar o webpack, para acrescentar var $ = require("jquery")sempre que encontrar o $identificador global .

var webpack = require("webpack");

    ...

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

3. Use o imports-loader para configurarthis

Alguns módulos herdados dependem de thisser o windowobjeto. Isso se torna um problema quando o módulo é executado em um contexto CommonJS em que thisé igual a module.exports. Nesse caso, você pode substituir thiso carregador de importações .

Corra npm i imports-loader --save-deve depois

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

O carregador de importações também pode ser usado para injetar manualmente variáveis ​​de todos os tipos. Mas na maioria das vezes ProvidePluginé mais útil quando se trata de globais implícitos.


4. Use o imports-loader para desativar o AMD

Existem módulos que suportam diferentes estilos de módulos, como AMD, CommonJS e legado. No entanto, na maioria das vezes, eles primeiro procuram definee, em seguida, usam algum código peculiar para exportar propriedades. Nesses casos, pode ajudar a forçar o caminho CommonJS configurando define = false.

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

5. Use o carregador de scripts para importar scripts globalmente

Se você não se importa com variáveis ​​globais e apenas deseja que scripts herdados funcionem, também pode usar o carregador de scripts. Ele executa o módulo em um contexto global, como se você os tivesse incluído através da <script>tag.


6. Use noParsepara incluir discos grandes

Quando não há uma versão AMD / CommonJS do módulo e você deseja incluir o dist, pode sinalizar este módulo como noParse. Em seguida, o webpack incluirá apenas o módulo sem analisá-lo, o que pode ser usado para melhorar o tempo de compilação. Isso significa que qualquer recurso que exija o AST , como o ProvidePlugin, não funcionará.

module: {
    noParse: [
        /[\/\\]node_modules[\/\\]angular[\/\\]angular\.js$/
    ]
}
Johannes Ewald
fonte
3
O ProvidePluginé aplicado em todas as ocorrências dos identificadores fornecidos em todos os arquivos. O imports-loaderpode ser aplicado em arquivos específicos somente, mas você não deve usar tanto para as mesmas variáveis / dependências.
Johannes Ewald
5
Estou confuso com isso. De onde o jquery realmente está vindo? Localmente ou uma CDN? Tudo depois de 3. não está claro se isso é necessário. Quais são as etapas reais para integrar o jquery ao seu projeto usando o webpack. Ignora a versão que está disponível via CDN?
HelpMeStackOverflowMyOnlyHope
2
Tudo no CDN está fora do escopo do webpack, portanto, isso se refere a uma instalação jquery local. O Jquery pode ser necessário, não há etapas especiais necessárias para integrá-lo. Esta pergunta é sobre como integrar plugins jquery que dependem da $variável global .
Johannes Ewald
8
Fez tudo isso, ainda não funcionando; Em seguida, acrescentou: "module": { "loaders": [ { test: require.resolve("jquery"), loader: "expose?$!expose?jQuery" },e funcionou bem.
Musicformellons
5
Faça tudo isso, para incluir apenas um pacote jquery ?, que dor. Estou voltando para a minha compilação de gole
Rahul Gupta 4/17
89

Para acesso global ao jquery, existem várias opções. No meu projeto mais recente do webpack, eu queria acesso global ao jquery e adicionei o seguinte às minhas declarações de plug-ins:

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

Isso significa que o jquery pode ser acessado no código-fonte JavaScript por meio das referências globais $ e jQuery.

Obviamente, você também deve ter instalado o jquery via npm:

$ npm i jquery --save

Para um exemplo prático dessa abordagem, fique à vontade para bifurcar meu aplicativo no github

arcseldon
fonte
2
Você pode deixar um comentário se você rebaixar uma votação explicando o motivo. As informações acima são precisas. Por favor, veja meu aplicativo de trabalho em: github.com/arcseldon/react-babel-webpack-starter-app usando a abordagem acima.
Arcseldon
Eu não sou o menos favorável, mas talvez seja porque a ProvidePluginsolução que você sugere já foi proposta?
Léo Lam
@ LéoLam - obrigado pelo seu comentário. agradeço o seu argumento - deduzi a resposta por meio de perguntas separadas e acabei de compartilhar o trecho aqui que pensei ser provavelmente mais relevante para outras pessoas que desejassem imitar o que havia feito. Você está certo, porém, a resposta oficial cobre essa opção.
Arcseldon
1
Está funcionando, mas recebo 2 avisos: ./~/jQuery/dist/jquery.js There is another module with an equal name when case is ignored.e o mesmo com./~/jquery/dist/jquery.js
pistou
7
Eu precisava adicionar 'window.jQuery': 'jquery'a essa lista para fazer o pacote npm ms-signalr-client carregar. Eu também colocar em 'window.$': 'jquery'boa medida :)
Sammi
57

Não sei se entendi muito bem o que você está tentando fazer, mas tive que usar os plugins jQuery que exigiam que o jQuery estivesse no contexto global (janela) e coloquei o seguinte no meu entry.js:

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

O eu só tenho que exigir onde eu quiser o jqueryplugin.min.jse window.$é estendido com o plugin conforme o esperado.

sanfilippopablo
fonte
2
Basicamente, eu estava cometendo o erro de usar o ProvidePlugin junto com a condição noParse. Plugins como o ProvidePlugin não funcionam se fizermos o NoParse, conforme indicado no ponto 6 da resposta. Você pode ver esse erro no código
booleanhunter
Sim, descobri que funciona de maneira mais consistente entre plug-ins do que com os plug-ins de fornecimento, etc.
precisa
27

Consegui fazer as coisas funcionarem bem ao expor $e jQuerycomo variáveis ​​globais com o Webpack 3.8.1 e o seguinte.

Instale o jQuery como uma dependência do projeto. Você pode omitir @3.2.1a instalação da versão mais recente ou especificar outra versão.

npm install --save jquery@3.2.1

Instale expose-loadercomo uma dependência de desenvolvimento, se ainda não estiver instalado.

npm install expose-loader --save-dev

Configure o Webpack para carregar e expor o jQuery para nós.

// 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'
    })
  ]
}
HarlemSquirrel
fonte
5
Você esqueceu de mencionar que você precisa instalar expose-loader, a fim de que ele funcione corretamente npm install expose-loader --save-dev
Paulo Griiettner
4
Isso funcionou para mim 👍. jquery: 3.3.1, carregador de exposição: 0.7.5, webpack: 4.20.2
AKT
O expose-loadedfizeram por mim. Não recebi um erro no momento da compilação, mas nas ferramentas de desenvolvedor do Chrome. Isso está resolvido agora.
Johan Vergeer
Obrigado! Isso funcionou com "jquery": "^ 3.4.1" e "webpack": "^ 4.41.5". Sou eu ou fazer o jQuery funcionar com o Webpack não deve ser tão ridículo?
Carles Alcolea
18

Adicione isso ao seu array de plugins no webpack.config.js

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

então exija jquery normalmente

require('jquery');

Se a dor persistir na obtenção de outros scripts, tente explicitamente colocá-lo no contexto global via (na entrada js)

window.$ = jQuery;
KhaledMohamedP
fonte
18

No seu arquivo webpack.config.js, adicione abaixo:

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

Instale o jQuery usando o npm:

$ npm i jquery --save

No arquivo app.js, adicione as linhas abaixo:

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

Isso funcionou para mim. :)

ashwini
fonte
Como isso vai agir se você tem por ex. app.js e jquery-module.js, que exigem jquery ?, para mim eu recebo jquery13981378127 e jquery12389723198 como instâncias na janela?
Martea
8

Tentei algumas das respostas fornecidas, mas nenhuma delas parecia funcionar. Então eu tentei isso:

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

Parece funcionar, não importa qual versão eu estou usando

Cam Tullos
fonte
O +1 parece um mal-entendido de que as globais adicionadas aqui serão automaticamente ajustadas contra a janela, não parece funcionar assim, e você precisa ser explícito.
James
Correto, isso não o adiciona ao objeto de janela. Ele cria um alias de janela dentro do webpack. Portanto, se você tentar usar $fora de um arquivo exigido pelo webpack, ele será indefinido.
Cam Tullos
7

Isso funciona no webpack 3:

no arquivo webpack.config.babel.js:

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

E use o ProvidePlugin

new webpack.ProvidePlugin({
        '$': 'jquery',
        'jQuery': 'jquery',
    })
SharpCoder
fonte
1
Para outros que são super novos no webpack (como eu!), "Resolve" entra no seu webpack.config.js em module.exports = {} como entrada, saída, plugins, módulo, etc.
DavGarcia
1
E o novo webpack.ProvidePlugin entra na matriz de plugins.
21418 DavGarcia
2

A melhor solução que encontrei foi:

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

Basicamente, você precisa incluir uma variável fictícia em typings.d.ts, remover qualquer "importação * como $ de 'jquery" do seu código e adicionar manualmente uma marca ao script jQuery ao seu html SPA. Dessa forma, o webpack não estará no seu caminho e você poderá acessar a mesma variável global do jQuery em todos os seus scripts.

Fabricio
fonte
2

Isso funciona para mim no webpack.config.js

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

em outro javascript ou em HTML, adicione:

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

Editar: Às vezes, você deseja usar o webpack simplesmente como um empacotador de módulo para um projeto simples da web - para manter seu próprio código organizado. A solução a seguir é para aqueles que querem apenas que uma biblioteca externa funcione conforme o esperado em seus módulos - sem usar muito tempo mergulhando nas configurações do webpack. (Editado após -1)

Solução rápida e simples (es6) se você ainda estiver com problemas ou quiser evitar a configuração externa / configuração adicional do plugin 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 um módulo:

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