ベンダースクリプトを個別にバンドルし、必要に応じてWebpackでそれらを要求する方法


173

可能なはずのことをやろうとしているのですが、webpackのドキュメントだけではどうすればいいのか理解できません。

相互に依存している、または依存していないいくつかのモジュールを含むJavaScriptライブラリを作成しています。その上、jQueryはすべてのモジュールで使用され、それらの一部にはjQueryプラグインが必要な場合があります。このライブラリは、一部またはすべてのモジュールを必要とするいくつかの異なるWebサイトで使用されます。

モジュール間の依存関係を定義することは非常に簡単でしたが、サードパーティの依存関係を定義することは、思ったより難しいようです。

私が達成したいこと:各アプリについて、必要なサードパーティの依存関係を含むものと、ライブラリからの必要なモジュールを含むものの2つのバンドルファイルを用意します。

:私のライブラリに次のモジュールがあるとします。

  • a(必要:jquery、jquery.plugin1)
  • b(必須:jquery、a)
  • c(jquery、jquery.ui、a、bが必要)
  • d(jquery、jquery.plugin2、aが必要)

そして、モジュールa、b、cを必要とするアプリ(一意のエントリファイルとして表示する)があります。この場合のWebpackは、次のファイルを生成します。

  • ベンダーバンドル:jquery、jquery.plugin1、jquery.ui;
  • ウェブサイトバンドル:モジュールa、b、cを使用。

結局のところ、jQueryをグローバルとして使用したいので、すべてのファイルでjQueryを要求する必要はありません(たとえば、メインファイルでのみ要求することができます)。そして、jQueryプラグインは、必要な場合に備えて$グローバルを拡張するだけです(それらを必要としない他のモジュールで使用できる場合は問題ありません)。

これが可能であると仮定すると、この場合のwebpack構成ファイルの例は何でしょうか?設定ファイルでローダー、エクスターナル、プラグインのいくつかの組み合わせを試してみましたが、実際にそれらが何をしていて、どれを使用するべきかわかりません。ありがとうございました!


2
あなたの解決策は何ですか?きちんとしたアプローチを見つけることができましたか もしそうならそれを投稿してください!感謝
GeekOnGadgets

回答:


140

私のwebpack.config.js(バージョン1、2、3)ファイルには、

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

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

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

私のプラグイン配列

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

これで、必要に応じてサードパーティのライブラリを1つのファイルに追加するだけのファイルができました。

ベンダーとエントリポイントファイルを分ける場所をより細かくしたい場合:

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);
    }
  })
]

プラグインの順序は非常に重要であることに注意してください。

また、これはバージョン4で変更される予定です。それが公式の場合、私はこの回答を更新します。

更新: WindowsユーザーのindexOf検索の変更


1
質問を投稿したときに、これが既に可能であったかどうかはわかりませんが、これはまさに私が探していたものです。このソリューションにより、ベンダーエントリチャンクを指定する必要がなくなりました。どうもありがとう!
bensampaio

1
isExternalminChunks私の一日を作った。これはどのように文書化されていませんか?欠点はありますか?
Wesley Schleumer deGóes16年

Thxですが、Windowsパスの場合はuserRequest.indexOf( '/ node_modules /')をuserRequest.indexOf( 'node_modules')に変更します
Kinjeiro

@WesleySchleumerdeGóes文書化されていますが、例がなければoptions.minChunks (number|Infinity|function(module, count) -> boolean):、まだマイナス面は見られません。
Rafael De Leon

2
ローダーのパスも含まれるためmodule.userRequest(ローダーはおそらくにあるためnode_modules)、ローダーを使用している場合は機能しません。私のコードisExternal()return typeof module.userRequest === 'string' && !!module.userRequest.split('!').pop().match(/(node_modules|bower_components|libraries)/);
cdauth

54

私はあなたの問題を完全に理解しているかどうかはわかりませんが、最近同様の問題があったので、あなたを助けようと努めます。

ベンダーバンドル。

そのためにはCommonsChunkPluginを使用する必要があります。構成では、チャンクの名前(例:)vendor、および生成されるファイル名(例:)を指定しますvendor.js

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

ここで重要な部分は、vendorライブラリの意味を指定する必要があり、それをエントリセクションで行うことです。新しく宣言されたチャンクの名前と同じ名前(つまり、この場合は「ベンダー」)を持つエントリリストへの1つ以上の項目。そのエントリの値は、vendorバンドルに移動するすべてのモジュールのリストである必要があります。あなたの場合、それは次のようになります:

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

グローバルとしてのjQuery

同じ問題があり、それをProvidePluginで解決しました。ここでは、グローバルオブジェクトではなく、モジュールへの一種の切り傷を定義しています。つまり、次のように構成できます。

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

そして今$、あなたはあなたのコードのどこでも使うことができます-webpackは自動的にそれをに変換します

require('jquery')

お役に立てば幸いです。ここにある私のwebpack設定ファイルも見ることができます

私はwebpackが大好きですが、ドキュメントが世界で最も良いものではないことに同意します...しかし、ちょっと..人々は最初にAngularドキュメントについて同じことを言っていました:)


編集:

エントリポイント固有のベンダーチャンクを作成するには、CommonsChunkPluginsを複数回使用するだけです。

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

次に、さまざまなファイルに対してさまざまな外部ライブラリを宣言します。

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

いくつかのライブラリがエントリポイント間で(そしてそれらのほとんどで)重複している場合は、構成が異なる同じプラグインを使用して、ライブラリを共通ファイルに抽出できます。この例を参照してください。


お返事ありがとうございます。これは私が今まで見た中で最良のアプローチでしたが、残念ながらそれでも私の問題は解決しません...あなたの例をテストしたところ、vendor.jsファイルには、たとえ 'jquery'と 'jquery.plugin1'からのすべてのコードが含まれていますそれらは私のモジュールのいずれにも必要ありません。つまり、最終的には常にブラウザにロードされます。jqueryプラグインがたくさんあると、半分だけしか使用しなくても非常に大きなファイルになります。必要な場合にのみ、ベンダーバンドルに「jquery.plugin1」を含める方法はありませんか?
ベンサンパイオ、2015年

おかげで、私も何かを学びました:)私は複数のベンダーチャンクの作成で私の答えを更新しました。多分今それはあなたによく合うでしょう。
のMichałMargiel

4
このソリューションの問題は、各ページの依存関係がわかっていると想定していることです。しかし、私は予測できません... jQueryは、ページで使用されているモジュールの1つで必要な場合にのみ、ベンダーバンドルに含める必要があります。設定ファイルでそれを指定することにより、ページで使用されているモジュールで必要とされていなくても、常にベンダーバンドルに含まれますよね?基本的に、ベンダーバンドルの内容を予測することはできません。さもなければ、何百もの2ページしかないので、ひどい仕事になってしまいます...問題が発生しますか?何か案は?:)
ベンサンパイオ2015年

私はあなたの言っていることを理解していますが、私はそれを問題として見ません。ページで新しいライブラリを使用する場合は、そのページのベンダーライブラリリストに追加するだけです。ほんの数文字です。とにかく、ソリューションではローダーを指定してそれを行う必要があります。新しく作成されたモジュールを使用するページがわからない場合は、CommonChuncksプラグインにモジュールから共通ライブラリを自動的に抽出させます。
のMichałMargiel

ベンダーファイルのコンテキストを個別に設定するにはどうすればよいですか?
harshes53

44

スクリプトをベンダーのスクリプトとは別に自動的にバンドルすることに関心がある場合:

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'})
  ]
};

この機能の詳細については、公式ドキュメントをご覧ください。


4
vendor : Object.keys(pkg.dependencies) は常に機能するとは限らず、パッケージのビルド方法に依存していることに注意してください。
markyph 2016

1
あなたはいつもあなたのpackage.json設定に依存しています。この回避策はほとんどの場合に機能しますが、別の方法をとる必要がある例外があります。コミュニティに役立つ質問への独自の回答を投稿するのは興味深いかもしれません。
Freezystem

16
私はこれが好き。少しオシッコさせてもらいました。
cgatian

3
それはあなたがあなたのコードで全く使用していないかもしれないパッケージさえも含むことに注意してください... Object.keys(pkg.dependencies)すべてのためにバンドルするので!!!! ローダーのリストがそこにあると言いましょう...そうです、含まれます!!! 注意してください... devDependencyとDependencyの違いを慎重に分離してください
Rafael

1
@RafaelMilewskiなぜローダーがあるのdependencies
パンツ

13

また、あなたのケースを完全に理解しているかどうかはわかりませんが、バンドルごとに個別のベンダーチャンクを作成する構成スニペットを次に示します。

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
  }),
]

そして、CommonsChunkPluginドキュメントへのリンク:http : //webpack.github.io/docs/list-of-plugins.html#commonschunkplugin


このソリューションの問題は、Michalによって提供されたものと同じだと思います。bundle1とbundle2のベンダー依存関係を知っていると想定していますが、わかりません... 200個のバンドルがあるとします。構成ファイルでこれらすべてを指定したいとしますか?あなたの例を使用して、reactそれがbundle1とbundl2によって明示的に要求されている場合にのみ、ベンダーバンドルに存在するべきです。設定ファイルで指定する必要はありません...これは意味がありますか?何か案は?
ベンサンパイオ2015年

@Anakin問題は、なぜ200ベンダーのツールを別のファイルにバンドルするのかということです。共通のツールのみを個別のファイルにバンドルし、残りはプロジェクトバンドルで保持します。
maxisam 2016年

@アナキン私は同じ問題を扱っていると思います、私が間違っている場合は修正してください?stackoverflow.com/questions/35944067/...
pjdicke
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.