Mengelola ketergantungan plugin jQuery di webpack

451

Saya menggunakan Webpack di aplikasi saya, di mana saya membuat dua titik masuk - bundle.js untuk semua file / kode JavaScript saya, dan vendor.js untuk semua perpustakaan seperti jQuery dan React. Apa yang harus saya lakukan untuk menggunakan plugin yang memiliki jQuery sebagai dependensinya dan saya ingin memilikinya juga di vendor.js? Bagaimana jika plugin tersebut memiliki banyak dependensi?

Saat ini saya mencoba menggunakan plugin jQuery ini di sini - https://github.com/mbklein/jquery-elastic . Dokumentasi Webpack menyebutkan Plugin dan import-loader. Saya menggunakan ProvidPlugin, tetapi masih objek jQuery tidak tersedia. Inilah tampilan webpack.config.js saya seperti-

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;

Namun terlepas dari ini, masih ada kesalahan di konsol browser:

ReferenceError Uncaught: jQuery tidak didefinisikan

Demikian pula, ketika saya menggunakan import-loader, ia melempar kesalahan,

membutuhkan tidak didefinisikan '

di baris ini:

var jQuery = require("jquery")

Namun, saya bisa menggunakan plugin yang sama ketika saya tidak menambahkannya ke file vendor.js saya dan sebaliknya membutuhkannya dengan cara AMD normal seperti cara saya memasukkan file kode JavaScript saya yang lain, seperti-

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

});

Tapi ini bukan yang ingin saya lakukan, karena ini berarti jquery.elastic.source.js dibundel bersama dengan kode JavaScript saya di bundle.js, dan saya ingin semua plugin jQuery saya ada di bundel vendor.js. Jadi bagaimana cara mencapai ini?

booleanhunter
sumber
3
Tidak yakin apakah ini masalah Anda, tetapi Anda pasti perlu mengubah windows.jQuery menjadi "window.jQuery": "jquery". Ada salah ketik di situs web webpack tempat saya berasumsi Anda mendapatkan kode itu.
Alex Hawkins
@AlexHawkins Oh ya, saya perhatikan itu dan memperbaikinya. Terima kasih telah menunjukkannya!
booleanhunter

Jawaban:

770

Anda telah menggabungkan berbagai pendekatan cara memasukkan modul vendor lawas. Beginilah cara saya mengatasinya:

1. Lebih memilih CommonJS / AMD yang tidak ditambang dist

Sebagian besar modul menghubungkan distversi di mainbidangnya package.json. Meskipun ini berguna untuk sebagian besar pengembang, untuk webpack lebih baik untuk srcversi alias karena cara ini webpack dapat mengoptimalkan dependensi lebih baik (misalnya ketika menggunakan DedupePlugin).

// webpack.config.js

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

Namun, dalam kebanyakan kasus distversi ini berfungsi dengan baik juga.


2. Gunakan ProvidePluginuntuk menyuntikkan global tersirat

Sebagian besar modul lawas mengandalkan keberadaan global tertentu, seperti yang dilakukan plugin jQuery di $atau jQuery. Dalam skenario ini, Anda dapat mengonfigurasi webpack, untuk menambahkan var $ = require("jquery")setiap kali bertemu $pengenal global .

var webpack = require("webpack");

    ...

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

3. Gunakan import-loader untuk mengkonfigurasithis

Beberapa modul warisan mengandalkan thismenjadi windowobjek. Ini menjadi masalah ketika modul dieksekusi dalam konteks CommonJS di mana thissama dengan module.exports. Dalam hal ini Anda dapat mengganti thisdengan import-loader .

Jalankan npm i imports-loader --save-devkemudian

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

Import-loader juga dapat digunakan untuk menyuntikkan variabel secara manual dari semua jenis. Tapi sebagian besar waktu ProvidePluginlebih berguna ketika datang ke global tersirat.


4. Gunakan import-loader untuk menonaktifkan AMD

Ada modul yang mendukung gaya modul yang berbeda, seperti AMD, CommonJS dan warisan. Namun, sebagian besar waktu mereka pertama kali memeriksa definedan kemudian menggunakan beberapa kode unik untuk mengekspor properti. Dalam kasus ini, ini bisa membantu untuk memaksa jalur CommonJS dengan menetapkan define = false.

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

5. Gunakan pemuat skrip untuk mengimpor skrip secara global

Jika Anda tidak peduli dengan variabel global dan hanya ingin skrip lawas berfungsi, Anda juga dapat menggunakan pemuat skrip. Ini mengeksekusi modul dalam konteks global, sama seperti jika Anda memasukkannya melalui <script>tag.


6. Gunakan noParseuntuk memasukkan dist besar

Ketika tidak ada versi AMD / CommonJS dari modul dan Anda ingin memasukkan dist, Anda dapat menandai modul ini sebagai noParse. Kemudian webpack hanya akan menyertakan modul tanpa menguraikannya, yang dapat digunakan untuk meningkatkan waktu pembuatan. Ini berarti bahwa semua fitur yang memerlukan AST , seperti ProvidePlugin, tidak akan berfungsi.

module: {
    noParse: [
        /[\/\\]node_modules[\/\\]angular[\/\\]angular\.js$/
    ]
}
Johannes Ewald
sumber
3
Ini ProvidePluginditerapkan pada semua kemunculan pengidentifikasi yang diberikan di semua file. The imports-loaderdapat diterapkan pada file tertentu saja, tetapi Anda tidak harus menggunakan kedua untuk variabel yang sama / dependensi.
Johannes Ewald
5
Saya bingung dengan ini. Di mana jquery sebenarnya berasal? Lokal atau CDN? Semuanya setelah 3. tidak jelas apakah itu perlu. Apa langkah aktual untuk mengintegrasikan jquery ke proyek Anda menggunakan webpack. Apakah itu memotong versi yang tersedia melalui CDN?
HelpMeStackOverflowMyOnlyHope
2
Segala sesuatu dari CDN berada di luar jangkauan untuk webpack, jadi ini merujuk ke instalasi jquery lokal. Jquery hanya diperlukan, tidak ada langkah-langkah khusus yang diperlukan untuk mengintegrasikannya. Pertanyaan ini adalah tentang bagaimana mengintegrasikan plugin jquery yang bergantung pada $variabel global .
Johannes Ewald
8
Apakah semua ini, masih tidak berfungsi; Kemudian ditambahkan: "module": { "loaders": [ { test: require.resolve("jquery"), loader: "expose?$!expose?jQuery" },dan itu bekerja dengan baik.
musicformellons
5
Lakukan semua ini, untuk hanya menyertakan paket jquery ?, sungguh menyebalkan. Saya akan kembali ke tegukan saya
Rahul Gupta
89

Untuk akses global ke jquery maka ada beberapa opsi. Dalam proyek webpack terbaru saya, saya ingin akses global ke jquery jadi saya menambahkan yang berikut ini ke deklarasi plugin saya:

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

Ini berarti jquery dapat diakses dari dalam kode sumber JavaScript melalui referensi global $ dan jQuery.

Tentu saja, Anda harus menginstal jquery melalui npm:

$ npm i jquery --save

Untuk contoh kerja dari pendekatan ini, jangan ragu untuk bercabang aplikasi saya di github

arcseldon
sumber
2
Tolong bisakah Anda meninggalkan komentar jika Anda menurunkan peringkat suara yang menjelaskan alasannya. Informasi di atas akurat. Silakan lihat aplikasi saya yang berfungsi di: github.com/arcseldon/react-babel-webpack-starter-app menggunakan pendekatan di atas.
arcseldon
Saya bukan downvoter, tapi mungkin ini karena ProvidePluginsolusi yang Anda sarankan sudah diajukan?
Léo Lam
@ LéoLam - terima kasih atas komentar Anda. hargai poin Anda - saya telah menyimpulkan jawabannya melalui pertanyaan terpisah, dan baru saja membagikan cuplikannya di sini saya pikir mungkin paling relevan dengan orang lain yang ingin meniru apa yang telah saya lakukan. Namun Anda benar, jawaban resmi tidak mencakup opsi ini.
arcseldon
1
Ini bekerja tetapi saya mendapat 2 peringatan: ./~/jQuery/dist/jquery.js There is another module with an equal name when case is ignored.dan yang sama dengan./~/jquery/dist/jquery.js
pistou
7
Saya perlu menambahkan 'window.jQuery': 'jquery'ke daftar itu untuk membuat paket npm ms-signalr-client memuat. Saya juga melakukan 'window.$': 'jquery'tindakan yang baik :)
Sammi
57

Saya tidak tahu apakah saya mengerti dengan sangat baik apa yang Anda coba lakukan, tetapi saya harus menggunakan plugin jQuery yang mengharuskan jQuery berada dalam konteks global (jendela) dan saya memasukkan yang berikut ini di entry.js:

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

Saya hanya perlu meminta di mana pun saya mau jqueryplugin.min.jsdan window.$diperpanjang dengan plugin seperti yang diharapkan.

sanfilippopablo
sumber
2
Pada dasarnya saya membuat kesalahan dengan menggunakan ProvidPlugin bersama dengan kondisi noParse. Plugin seperti ProvidPlugin tidak berfungsi jika kita melakukan NoParse, sebagaimana dinyatakan dalam poin nomor 6 dari jawabannya. Anda dapat melihat kesalahan itu dalam kode
booleanhunter
ya, saya telah menemukan bahwa bekerja lebih konsisten di plugin daripada memberikan plugin dll ... khususnya jika Anda membawa sudut 1.x melalui skrip-loader.
Pelacak1
27

Saya mendapatkan hal-hal yang berfungsi dengan baik saat mengekspos $dan jQuerysebagai variabel global dengan Webpack 3.8.1 dan berikut ini.

Instal jQuery sebagai ketergantungan proyek. Anda dapat menghilangkan @3.2.1untuk menginstal versi terbaru atau menentukan versi lain.

npm install --save jquery@3.2.1

Instal expose-loadersebagai dependensi pengembangan jika belum diinstal.

npm install expose-loader --save-dev

Konfigurasikan Webpack untuk memuat dan mengekspos jQuery untuk kami.

// 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
sumber
5
Anda lupa menyebutkan bahwa Anda harus menginstal expose-loaderagar berfungsi dengan baik npm install expose-loader --save-dev
Paulo Griiettner
4
Ini bekerja untuk saya 👍. jquery: 3.3.1, expose-loader: 0.7.5, webpack: 4.20.2
AKT
Itu expose-loadedmelakukannya untuk saya. Saya tidak mendapatkan kesalahan pada waktu kompilasi, tetapi pada alat pengembang Chrome. Itu terpecahkan sekarang.
Johan Vergeer
Terima kasih! Ini bekerja dengan "jquery": "^ 3.4.1", dan "webpack": "^ 4.41.5". Apakah hanya saya atau meminta jQuery untuk bekerja dengan Webpack tidak semanis ini?
Carles Alcolea
18

Tambahkan ini ke array plugin Anda di webpack.config.js

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

kemudian memerlukan jquery secara normal

require('jquery');

Jika rasa sakit tetap ada untuk mendapatkan skrip lain untuk melihatnya, cobalah menempatkannya secara eksplisit dalam konteks global melalui (dalam entri js)

window.$ = jQuery;
KhaledMohamedP
sumber
18

Dalam file webpack.config.js Anda, tambahkan di bawah ini:

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

Instal jQuery menggunakan npm:

$ npm i jquery --save

Dalam file app.js tambahkan baris di bawah ini:

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

Ini berhasil untuk saya. :)

ashwini
sumber
Bagaimana ini akan bertindak jika Anda memiliki mantan. app.js, dan jquery-module.js, bahwa keduanya memerlukan jquery ?, bagi saya saya mendapatkan jquery13981378127, dan jquery12389723198 sebagai contoh di jendela?
Martea
8

Saya mencoba beberapa jawaban yang diberikan tetapi tidak satupun yang sepertinya berhasil. Lalu saya mencoba ini:

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

Tampaknya berfungsi, apa pun versi yang saya gunakan

Cam Tullos
sumber
+1 tampaknya merupakan kesalahpahaman bahwa global yang ditambahkan di sini secara otomatis akan diatur melawan jendela, sepertinya tidak berfungsi seperti yang Anda butuhkan secara eksplisit.
James
Benar, ini tidak menambahkannya ke objek jendela. Itu membuat alias jendela di dalam webpack. Jadi, jika Anda mencoba menggunakan di $luar file yang diperlukan webpack, itu akan tidak terdefinisi.
Cam Tullos
7

Ini berfungsi di webpack 3:

dalam file webpack.config.babel.js:

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

Dan gunakan ProvidPlugin

new webpack.ProvidePlugin({
        '$': 'jquery',
        'jQuery': 'jquery',
    })
SharpCoder
sumber
1
Untuk yang lain yang super baru di webpack (seperti saya!) "Tekad" masuk di webpack.config.js Anda di bawah module.exports = {} seperti entri, output, plugin, modul, dll.
DavGarcia
1
Dan webpack baru. ProvidePlugin masuk ke dalam array plugin.
DavGarcia
2

Solusi terbaik yang saya temukan adalah:

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

Pada dasarnya, Anda perlu memasukkan variabel dummy pada typings.d.ts, menghapus "impor * sebagai $ dari 'jquery" dari kode Anda, dan kemudian secara manual menambahkan tag ke skrip jQuery ke html SPA Anda. Dengan cara ini, webpack tidak akan menghalangi Anda, dan Anda harus dapat mengakses variabel jQuery global yang sama di semua skrip Anda.

Fabricio
sumber
2

Ini berfungsi untuk saya di webpack.config.js

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

di javascript lain atau ke dalam HTML tambahkan:

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

Sunting: Terkadang Anda ingin menggunakan webpack hanya sebagai bundler modul untuk proyek web sederhana - untuk menjaga kode Anda sendiri terorganisir. Solusi berikut adalah bagi mereka yang hanya ingin perpustakaan eksternal berfungsi seperti yang diharapkan di dalam modul mereka - tanpa menggunakan banyak waktu menyelam ke pengaturan webpack. (Diedit setelah -1)

Solusi cepat dan sederhana (es6) jika Anda masih berjuang atau ingin menghindari konfigurasi eksternal / konfigurasi plugin webpack tambahan:

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

di dalam modul:

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