web-dev-qa-db-ja.com

小さな初期スクリプトと他のすべてのスクリプトの非同期読み込みを備えたWebpack

多数のページとさまざまな種類のページで構成される通常のWebサイトを開発するときに、Webpackを使い始めました。私は、必要なときにすべての依存関係をオンデマンドでロードするRequireJsスクリプトローダーに慣れています。ページが読み込まれると、JavaScriptのほんの一部がダウンロードされます。

私が達成したいのはこれです:

  • 非同期で依存関係を読み込む小さな初期JavaScriptファイル
  • 各ページタイプには独自のJavaScriptがあり、依存関係もある場合があります。
  • 共通モジュール、ベンダースクリプトは共通スクリプトにバンドルする必要があります

これを達成するために多くの構成を試しましたが、成功しませんでした。

entry: {
    main: 'main.js', //Used on all pages, e.g. mobile menu
    'standard-page': 'pages/standard-page.js',
    'start-page': 'pages/start-page.js',
    'vendor': ['jquery']
},
alias: {
    jquery: 'jquery/dist/jquery.js'
},
plugins: [
    new webpack.optimize.CommonsChunkPlugin("vendor", "vendor.js"),
    new webpack.optimize.CommonsChunkPlugin('common.js')
]

HTMLでは、次のようにJavaScriptをロードします。

<script src="/Static/js/dist/common.js"></script>
<script src="/Static/js/dist/main.js" async></script>

そして、特定のページタイプ(スタートページ)

<script src="/Static/js/dist/start-page.js" async></script>

common.jsは、ページの高速読み込みのための小さなファイルである必要があります。 main.jsは内部にasyncとrequire( 'jquery')をロードします。

Webpackからの出力は有望に見えますが、ベンダーバンドルを非同期にロードすることはできません。他の依存関係(自分のモジュールとdomReady)は自動生成されたチャンクに読み込まれますが、jqueryには読み込まれません。

これをほぼ実行するが、ベンダーを非同期にロードする重要な部分ではない多くの例を見つけることができます。

Webpackビルドからの出力:

                  Asset       Size  Chunks             Chunk Names
            main.js.map  570 bytes    0, 7  [emitted]  main
                main.js  399 bytes    0, 7  [emitted]  main
       standard-page.js  355 bytes    2, 7  [emitted]  standard-page
c6ff6378688eba5a294f.js  348 bytes    3, 7  [emitted]
          start-page.js  361 bytes    4, 7  [emitted]  start-page
8986b3741c0dddb9c762.js  387 bytes    5, 7  [emitted]
              vendor.js     257 kB    6, 7  [emitted]  vendor
              common.js    3.86 kB       7  [emitted]  common.js
2876de041eaa501e23a2.js     1.3 kB    1, 7  [emitted]  
16
erzki

この問題の解決策は2つあります。

  1. まず、理解する必要があります webpackでのコード分割の仕組み
  2. 次に、その共有バンドルを生成するには、CommonsChunkPluginなどを使用する必要があります。

コード分​​割

Webpackを使い始める前に、設定に依存することを学ぶ必要があります。 Require.jsはすべて構成ファイルに関するものでした。この考え方により、設定に依存しないnode.jsのCommonJSをより厳密にモデル化したwebpackに移行することが難しくなりました。

それを念頭に置いて、次のことを考慮してください。アプリがあり、JavaScriptの他の一部を非同期でロードしたい場合は、次のパラダイムのいずれかを使用する必要があります。

Require.ensure

_require.ensure_は、アプリケーションに「スプリットポイント」を作成する1つの方法です。繰り返しますが、設定でこれを行う必要があると思ったかもしれませんが、そうではありません。この例では、ファイルwebpackで_require.ensure_を押すと、自動的に2番目のバンドルが作成され、オンデマンドでロードされます。その分割ポイント内で実行されるコードは、別のファイルにまとめられます。

_require.ensure(['jquery'], function() {
    var $ = require('jquery');
    /* ... */
});
_

必須([])

AMDバージョンのrequire()でも同じことができます。これは、依存関係の配列を取るものです。これにより、同じ分割ポイントも作成されます。

_require(['jquery'], function($) {
    /* ... */
});
_

共有バンドル

上記の例では、entryを使用して、jQueryを含むvendorバンドルを作成します。これらの依存関係バンドルを手動で指定する必要はありません。代わりに、Webpackの上の分割ポイントを使用すると、これが自動的に生成されます。

entryは、ページで使用する個別の_<script>_タグにのみ使用してください

以上で、CommonsChunkPluginを使用してチャンクをさらに最適化できるようになりましたが、ほとんどの魔法はあなたのために行われ、共有する依存関係を指定する以外に、他に何もする必要はありません。 webpackは、追加の_<script>_タグやentry構成を必要とせずに、共有チャンクを自動的に取り込みます。

結論

あなたが説明するシナリオ(複数の_<script>_タグ)は、実際にはあなたが望むものではないかもしれません。 webpackを使用すると、すべての依存関係とバンドルを単一の_<script>_タグのみで自動的に管理できます。 require.jsからwebpackへのリファクタリングを数回繰り返した結果、通常は依存関係を管理する最も簡単で最良の方法であることがわかりました。

ではごきげんよう!

13
Jamund Ferguson

私は最近同じ道を旅しました、バンドルが大きすぎると思いますので、Webpack出力の最適化に取り組んでいます、HTTP2はjsファイルを並列にロードでき、個別のファイルを使用するとキャッシュがより良くなり、いくつかの依存関係がバンドルに複製されていましたなど。私はWebpack 4 SplitChunksPlugin構成で動作するソリューションを取得しましたが、現在、ほとんどのWebpackの動的import()構文を使用する方向に動いています。 「魔法のコメント」を介して:

import(/* webpackChunkName: "mymodule" */ "mymodule"); // I added an resolve.alias.mymodule entry in Webpack.config
1
Ted Fitzpatrick

これが私が思いついた解決策です。

まず、これらの2つの関数をwindow.*にエクスポートします-ブラウザーでそれらを使用します。

export function requireAsync(module) {
    return new Promise((resolve, reject) => require(`bundle!./pages/${module}`)(resolve));
}

export function runAsync(moduleName, data={}) {
    return requireAsync(moduleName).then(module => {
        if(module.__esModule) {
            // if it's an es6 module, then the default function should be exported as module.default
            if(_.isFunction(module.default)) {
                return module.default(data);
            }
        } else if(_.isFunction(module)) {
            // if it's not an es6 module, then the module itself should be the function
            return module(data);
        }
    })
}

次に、スクリプトの1つをページに含める場合は、これをHTMLに追加します。

<script>requireAsync('script_name.js')</script>

これで、pages/ディレクトリ内のすべてが個別のチャンクにプリコンパイルされ、必要な場合にのみ、実行時に非同期でロードできます。

さらに、上記の関数を使用して、サーバー側のデータをクライアント側のスクリプトに渡す便利な方法があります。

<script>runAsync('script_that_needs_data', {my:'data',wow:'much excite'})</script>

そして今、あなたはそれにアクセスできます:

// script_that_needs_data.js
export default function({my,wow}) {
    console.log(my,wow);
}
1
mpen

少し前に、私はIE11でimportlazyがどのように機能するかを確認するために、このような小さな「Proof of Concept」を作成しました。私はそれが機能することを認めなければなりません:)ボタンをクリックした後、ページの背景色を変更するためのコードが読み込まれます- 完全な例

Js:

// polyfils for IE11
import 'core-js/modules/es.array.iterator';

const button = document.getElementById('background');

button.addEventListener('click', async (event) => {
  event.preventDefault();
  try {
    const background = await import(/* webpackChunkName: "background" */ `./${button.dataset.module}.js`);
    background.default();
  } catch (error) {
    console.log(error);
  }
})

HTML:

<button id="background" class="button-primary" data-module="background">change the background</button>
0
Grzegorz T.