Bagaimana cara menggabungkan skrip vendor secara terpisah dan membutuhkannya sesuai kebutuhan dengan Webpack?

173

Saya mencoba melakukan sesuatu yang saya yakini mungkin terjadi, tetapi saya benar-benar tidak mengerti bagaimana melakukannya hanya dari dokumentasi webpack.

Saya menulis perpustakaan JavaScript dengan beberapa modul yang mungkin atau tidak bergantung satu sama lain. Selain itu, jQuery digunakan oleh semua modul dan beberapa dari mereka mungkin memerlukan plugin jQuery. Pustaka ini kemudian akan digunakan pada beberapa situs web yang berbeda yang mungkin memerlukan beberapa atau semua modul.

Mendefinisikan dependensi antar modul saya sangat mudah, tetapi mendefinisikan dependensi pihak ketiga tampaknya lebih sulit dari yang saya harapkan.

Apa yang ingin saya capai : untuk setiap aplikasi saya ingin memiliki dua file bundel satu dengan dependensi pihak ketiga yang diperlukan dan lainnya dengan modul yang diperlukan dari perpustakaan saya.

Contoh : Mari kita bayangkan bahwa perpustakaan saya memiliki modul berikut:

  • a (membutuhkan: jquery, jquery.plugin1)
  • b (membutuhkan: jquery, a)
  • c (membutuhkan: jquery, jquery.ui, a, b)
  • d (membutuhkan: jquery, jquery.plugin2, a)

Dan saya punya aplikasi (melihatnya sebagai file entri unik) yang membutuhkan modul a, b dan c. Webpack untuk kasus ini harus menghasilkan file berikut:

  • bundel vendor : dengan jquery, jquery.plugin1 dan jquery.ui;
  • bundel situs web : dengan modul a, b dan c;

Pada akhirnya, saya lebih suka memiliki jQuery sebagai global jadi saya tidak perlu meminta itu pada setiap file tunggal (misalnya, saya hanya memerlukannya pada file utama). Dan plugin jQuery hanya akan memperpanjang $ global seandainya diperlukan (itu tidak masalah jika tersedia untuk modul lain yang tidak membutuhkannya).

Dengan asumsi ini mungkin, apa yang akan menjadi contoh file konfigurasi webpack untuk kasus ini? Saya mencoba beberapa kombinasi loader, eksternal, dan plugin pada file konfigurasi saya, tetapi saya tidak benar-benar mendapatkan apa yang mereka lakukan dan mana yang harus saya gunakan. Terima kasih!

bensampaio
sumber
2
apa solusinya? apakah Anda berhasil menemukan pendekatan yang layak. Jika demikian silakan posting! terima kasih
GeekOnGadgets

Jawaban:

140

dalam file webpack.config.js (Versi 1,2,3) saya, saya punya

function isExternal(module) {
  var context = module.context;

  if (typeof context !== 'string') {
    return false;
  }

  return context.indexOf('node_modules') !== -1;
}

di array plugin saya

plugins: [
  new CommonsChunkPlugin({
    name: 'vendors',
    minChunks: function(module) {
      return isExternal(module);
    }
  }),
  // Other plugins
]

Sekarang saya memiliki file yang hanya menambahkan lib pihak ke tiga file seperti yang diperlukan.

Jika Anda ingin mendapatkan lebih banyak rincian tempat Anda memisahkan vendor dan file titik masuk Anda:

plugins: [
  new CommonsChunkPlugin({
    name: 'common',
    minChunks: function(module, count) {
      return !isExternal(module) && count >= 2; // adjustable
    }
  }),
  new CommonsChunkPlugin({
    name: 'vendors',
    chunks: ['common'],
    // or if you have an key value object for your entries
    // chunks: Object.keys(entry).concat('common')
    minChunks: function(module) {
      return isExternal(module);
    }
  })
]

Perhatikan bahwa urutan plugin sangat penting.

Juga, ini akan berubah dalam versi 4. Ketika itu resmi, saya memperbarui jawaban ini.

Pembaruan: indexOf perubahan pencarian untuk pengguna windows

Rafael De Leon
sumber
1
Saya tidak tahu apakah ini sudah mungkin ketika saya memposting pertanyaan saya, tetapi ini memang yang saya cari. Dengan solusi ini saya tidak perlu lagi menentukan entri entri vendor saya. Terima kasih banyak!
bensampaio
1
isExternaldi minChunksbuat hari saya. Bagaimana ini tidak didokumentasikan? Ada kerugiannya?
Wesley Schleumer de Góes
Thx, tetapi ubah userRequest.indexOf ('/ node_modules /') menjadi userRequest.indexOf ('node_modules') untuk
path
@ WesleySchleumerdeGóes sudah didokumentasikan tetapi tanpa contoh options.minChunks (number|Infinity|function(module, count) -> boolean):saya belum melihat sisi negatifnya.
Rafael De Leon
2
Ini tidak akan berfungsi ketika menggunakan loader, karena jalur loader juga akan berada di module.userRequest(dan mungkin loader itu masuk node_modules). Kode saya untuk isExternal():return typeof module.userRequest === 'string' && !!module.userRequest.split('!').pop().match(/(node_modules|bower_components|libraries)/);
cdauth
54

Saya tidak yakin apakah saya sepenuhnya memahami masalah Anda, tetapi karena saya memiliki masalah serupa baru-baru ini, saya akan mencoba membantu Anda.

Bundel vendor.

Anda harus menggunakan CommonsChunkPlugin untuk itu. dalam konfigurasi Anda menentukan nama chunk (eg vendor), dan nama file yang akan dihasilkan ( vendor.js).

new webpack.optimize.CommonsChunkPlugin("vendor", "vendor.js", Infinity),

Sekarang bagian penting, Anda sekarang harus menentukan apa artinya vendorperpustakaan dan Anda melakukannya di bagian entri. Satu item lagi ke daftar entri dengan nama yang sama dengan nama chunk yang baru dinyatakan (yaitu 'vendor' dalam kasus ini). Nilai entri itu haruslah daftar semua modul yang ingin Anda pindahkan ke vendorbundel. dalam kasus Anda akan terlihat seperti:

entry: {
    app: 'entry.js',
    vendor: ['jquery', 'jquery.plugin1']
}

JQuery sebagai global

Punya masalah yang sama dan menyelesaikannya dengan ProvidPlugin . di sini Anda tidak mendefinisikan objek global tetapi semacam shurtcuts ke modul. yaitu Anda dapat mengkonfigurasinya seperti itu:

new webpack.ProvidePlugin({
    $: "jquery"
})

Dan sekarang Anda bisa menggunakan $di mana saja dalam kode Anda - webpack akan secara otomatis mengubahnya menjadi

require('jquery')

Saya harap ini membantu. Anda juga dapat melihat file konfigurasi webpack saya yaitu di sini

Saya suka webpack, tapi saya setuju bahwa dokumentasi itu bukan yang terbaik di dunia ... tapi hei .. orang-orang mengatakan hal yang sama tentang dokumentasi Angular pada awalnya :)


Edit:

Untuk mendapatkan potongan vendor khusus titik masuk, gunakan CommonsChunkPlugins beberapa kali:

new webpack.optimize.CommonsChunkPlugin("vendor-page1", "vendor-page1.js", Infinity),
new webpack.optimize.CommonsChunkPlugin("vendor-page2", "vendor-page2.js", Infinity),

dan kemudian mendeklarasikan pustaka extenral berbeda untuk file yang berbeda:

entry: {
    page1: ['entry.js'],
    page2: ['entry2.js'],
    "vendor-page1": [
        'lodash'
    ],
    "vendor-page2": [
        'jquery'
    ]
},

Jika beberapa perpustakaan tumpang tindih (dan sebagian besar dari mereka) antara titik masuk maka Anda dapat mengekstraknya ke file umum menggunakan plugin yang sama hanya dengan konfigurasi yang berbeda. Lihat contoh ini .

Michał Margiel
sumber
Terima kasih banyak atas balasan Anda. Ini adalah pendekatan terbaik yang saya lihat sampai sekarang tetapi sayangnya itu masih tidak menyelesaikan masalah saya ... Saya menguji contoh Anda dan file vendor.js masih akan berisi semua kode dari 'jquery' dan 'jquery.plugin1' bahkan jika mereka tidak diperlukan oleh modul saya. Ini berarti pada akhirnya mereka akan selalu dimuat ke browser. Jika saya memiliki banyak plugin jquery, ini akan menghasilkan file yang sangat besar walaupun hanya setengahnya yang digunakan. Apakah tidak ada cara untuk memasukkan 'jquery.plugin1' dalam bundel vendor hanya jika diperlukan?
bensampaio
terima kasih, jadi saya telah mempelajari sesuatu juga :) Saya telah memperbarui jawaban saya dengan membuat beberapa vendor-chunks. mungkin sekarang ini akan lebih cocok untuk Anda.
Michał Margiel
4
Masalah dengan solusi ini adalah mengasumsikan saya tahu apa dependensi untuk setiap halaman. Tetapi saya tidak dapat memprediksi bahwa ... jQuery hanya boleh disertakan pada bundel vendor jika diperlukan oleh salah satu modul yang digunakan dalam halaman. Dengan menentukan bahwa pada file konfigurasi itu akan selalu ada dalam bundel vendor bahkan jika tidak diperlukan oleh modul apa pun yang digunakan di halaman, kan? Pada dasarnya, saya tidak dapat memprediksi isi bundel vendor, jika tidak saya akan memiliki pekerjaan yang berat karena saya tidak memiliki hanya 2 halaman, saya memiliki ratusan ... Apakah Anda mendapatkan masalah? Ada ide? :)
bensampaio
Saya mengerti apa yang Anda katakan tetapi saya tidak melihat itu sebagai masalah. Jika Anda menggunakan perpustakaan baru di halaman, maka tambahkan saja ke daftar perpustakaan vendor untuk halaman itu. Itu hanya beberapa karakter. Pokoknya dalam solusi Anda, Anda harus melakukannya dengan menentukan loader. Jika Anda tidak tahu halaman mana yang akan menggunakan modul yang baru Anda buat - maka biarkan CommonChuncks plugin untuk mengekstrak perpustakaan umum dari modul Anda secara otomatis.
Michał Margiel
Bagaimana saya bisa mengatur konteks secara terpisah untuk file vendor?
harshes53
44

Jika Anda tertarik untuk menggabungkan skrip Anda secara terpisah dari skrip vendor:

var webpack = require('webpack'),
    pkg     = require('./package.json'),  //loads npm config file
    html    = require('html-webpack-plugin');

module.exports = {
  context : __dirname + '/app',
  entry   : {
    app     : __dirname + '/app/index.js',
    vendor  : Object.keys(pkg.dependencies) //get npm vendors deps from config
  },
  output  : {
    path      : __dirname + '/dist',
    filename  : 'app.min-[hash:6].js'
  },
  plugins: [
    //Finally add this line to bundle the vendor code separately
    new webpack.optimize.CommonsChunkPlugin('vendor', 'vendor.min-[hash:6].js'),
    new html({template : __dirname + '/app/index.html'})
  ]
};

Anda dapat membaca lebih lanjut tentang fitur ini di dokumentasi resmi .

Freezystem
sumber
4
Harap dicatat bahwa vendor : Object.keys(pkg.dependencies) ini tidak selalu berfungsi dan tergantung pada bagaimana paket dibuat.
markyph
1
Anda selalu bergantung pada bagaimana Anda package.jsondiatur. Penanganan masalah ini berfungsi dalam banyak kasus tetapi ada pengecualian di mana Anda harus mengambil jalan yang berbeda. Mungkin menarik untuk mengirim jawaban Anda sendiri ke pertanyaan untuk membantu komunitas.
Freezystem
16
Saya suka ini. Itu membuat saya buang air kecil.
cgatian
3
perhatikan bahwa ia bahkan akan menyertakan paket yang bahkan mungkin tidak Anda gunakan sama sekali dalam kode Anda ... karena Object.keys(pkg.dependencies)akan menggabungkan semuanya !!!! katakanlah Anda memiliki banyak loader yang terdaftar di sana ... ya itu akan dimasukkan !!! jadi berhati-hatilah ... pisahkan dengan hati-hati apa itu devDependency dan apa dependensi
Rafael Milewski
1
@ RafaelMilewski mengapa Anda memiliki loader dependencies?
Celana
13

Juga tidak yakin apakah saya sepenuhnya memahami kasus Anda, tetapi di sini potongan konfigurasi untuk membuat potongan vendor terpisah untuk masing-masing bundel Anda:

entry: {
  bundle1: './build/bundles/bundle1.js',
  bundle2: './build/bundles/bundle2.js',
  'vendor-bundle1': [
    'react',
    'react-router'
  ],
  'vendor-bundle2': [
    'react',
    'react-router',
    'flummox',
    'immutable'
  ]
},

plugins: [
  new webpack.optimize.CommonsChunkPlugin({
    name: 'vendor-bundle1',
    chunks: ['bundle1'],
    filename: 'vendor-bundle1.js',
    minChunks: Infinity
  }),
  new webpack.optimize.CommonsChunkPlugin({
    name: 'vendor-bundle2',
    chunks: ['bundle2'],
    filename: 'vendor-bundle2-whatever.js',
    minChunks: Infinity
  }),
]

Dan tautan ke CommonsChunkPlugindokumen: http://webpack.github.io/docs/list-of-plugins.html#commonschunkplugin

Alex Fedoseev
sumber
Saya percaya masalah dengan solusi ini sama dengan yang disediakan oleh Michal. Anda berasumsi saya tahu dependensi vendor untuk bundle1 dan bundle2, tapi saya tidak ... Bayangkan Anda memiliki 200 bundel, apakah Anda ingin menentukan semua itu pada file konfigurasi? Menggunakan contoh Anda, reactseharusnya hanya ada dalam bundel vendor jika secara eksplisit diperlukan oleh bundle1 dan bundl2. Saya tidak harus menentukan itu pada file konfigurasi ... Apakah ini masuk akal? Ada ide?
bensampaio
@ Anakin pertanyaannya adalah mengapa Anda ingin menggabungkan 200 alat vendor ke dalam file terpisah. Saya hanya akan menggabungkan alat-alat umum ke dalam file terpisah, dan menyimpan sisanya dengan bundel proyek.
maxisam
@ Anakin Saya pikir saya sedang berurusan dengan masalah yang sama, koreksi saya jika saya salah? stackoverflow.com/questions/35944067/…
pjdicke