ハンドルロードエラーと


TLドクター
HTMLWebPackPackを使用している場合は、時々荷物がロードされている間に間違って行く.we’ve got you covered .
エラーが起こる.
これはプログラミングに特に当てはまります.しかし、プログラムのコードが実行する機会がある前にエラーが発生した場合、これはかなり驚くべきものです.
これは私が最近対処しなければならなかった問題です.そして、これはクライアント側のWebアプリケーションでは非常に一般的な問題であるかもしれません.
見たエラーは次のようになります.
Uncaught SyntaxError: Unexpected token '<'
いくつかの研究の後、エラーが起こったときのシナリオは次のようなものであることがわかりました.
  • ユーザーのブラウザは、ウェブサイトへの最初の訪問でページをキャッシュします.ユーザーは、日Xまでサイトを再度訪問しません
  • 我々は積極的にウェブサイトを開発&リリース
  • それぞれの新しいリリースでは、サーバーにユニークなハッシュでバンドルを追加します
  • サーバーのリソースが限られているので、それぞれの新しいリリースが入ってくるので、最新のリリースを消去します
  • 日Xとページのキャッシュされたバージョンをもつユーザーは、幸せに入ります
  • ユーザのブラウザはフェッチしようとするbundle.[too-old-hash].js しかし、私たちがすでにいくつかの配備をしたので、それはサーバーに存在しません、そして、この古いリリースは消されました
  • その後、サーバーはHTML
  • JSコンパイラはHTMLとスローを解析できませんSyntaxError
  • アプリケーションは通常、クライアント側で反応してレンダリングされますが、バンドルがないので、ユーザーは空白のページを見ます
  • したがって、どのように、あなたはあなたの全アプリケーションが利用できないという事実のために起こるエラーを扱いますか?以下に、可能なフロントエンドのみの解決策を示します.
    あなたが沿ってコードを好むならば、あなたは見つけることができますsample repo with all setup but no solution implemented here .

    セットアップ


    使用するwebpack コードをコンパイル&バンドルするにはHtmlWebpackPlugin 我々のアプリケーションが最終的に生きるHTMLページを生成するために.
    我々のアプリケーションは何でも可能性があります.この記事はフレームワークagnosticです.

    可能な解決策と警告


    まず第一に、論理的に我々は何もできないbundle.[hash].js , このファイルがロードに失敗し、ランタイム中に使用できなくなります.
    では、どうしますか?さて、私たちは私たちのページにインラインJSを加えることができました.それは常に存在します、そして、したがって、若干の取扱いをすることができます.
    つくりましょうsrc/index.ejs HTMLWebPackpluginによって使用されるテンプレートの既定の場所はHTMLページを生成する場所です.このファイルを作成することで、生成されたページのHTMLスケルトンをカスタマイズできます.
    私の最初の素朴な試みはHTMLWebPackplugのテンプレートでいくつかのインラインのJSを追加するにはerror event このようなアプリケーションのスクリプトタグについて:src/index.ejs :
    <!DOCTYPE html>
    <html lang="en" dir="ltr">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Handling script loading errors with HtmlWebpackPlugin</title>
    </head>
    <body>
        <h1 id="content"></h1>
        <script>
        (function(){
            function showErrorPage() {
                // Doesn't matter for now
            }
    
            var appScript = document.querySelector('script[src^="bundle"]');
            appScript.addEventListener('error', showErrorPage);
        })();
        </script>
        <!--
        HTMLWebpackPlugin will insert bundled script here on build.
        It will look something like this:
    
        <script src="bundle.foo12.js"></script>
        -->
    </body>
    </html>
    
    しかしインライン時には動作しません<script> ’sコードが実行されます.<script src="bundle.foo12.js"></script> それがインラインスクリプトタグの下に位置していて、まだブラウザによって解析されていないので、DOMにまだ存在しません.さあ、DOMが準備されるまで待ちましょう.一度なら、同じようにして、イベントリスナーを添付しましょう(下記のように、私は簡潔にドキュメントの変更部分を省略します).src/index.ejs :
    <script>
    (function(){
        function showErrorPage() {
            // Doesn't matter for now
        }
    
        window.addEventListener('DOMContentLoaded', function() {
            var appScript = document.querySelector('script[src^="bundle"]');
            appScript.addEventListener('error', showErrorPage);
        });
    })();
    </script>
    
    残念ながら、これは動作しません.なぜなら、ブラウザがバンドルをロードしようとするプレーンスクリプトタグを見ると、すぐにバンドルをフェッチして実行します</html> それは DOMContentLoaded event .
    以下はグラフィカルに表示されるものです.

    この場合、私たちはイベントリスナーをずっと前にフェッチして、実行ステージを完了します、そして、我々のコールバックは決して発射されません.
    だから、私たちはあまりにも早いか遅すぎるどちらかのように見える.
    この段階で、私たちは、このタイプの非常に短い間隔または他のいくつかのブルートフォース解決でDOMのバンドルのスクリプト存在をチェックしようとすることができました.
    幸いにも、これは、HtmlWebPackpluginはエレガント&効率的なソリューションを実装するためにすべてを提供して以来、これは必要ありません.

    解決策


    我々は明らかに負荷イベントを聞く必要があります.
    しかし、読み込みイベントを聞くことができるように、我々のバンドルが時間内にイベントリスナーをアタッチするためにロードを開始するときに、より多くのコントロールが必要です.
    を、コントロールを追い越そう.
    まず最初に、HTMLWebPackplugに我々がそれを注入したくないと言いましょう<script> ‘私たちがページに制御できないs.webpack.config.js :
    plugins: [
        new HtmlWebpackPlugin({
            inject: false
        })
    ]
    
    今、我々はバンドルの<script> タグは、すべてのように、我々のアプリは決してロードされません.それは良いことだが、我々は作成することができます<script> HtmlWebPackpluginの情報を使用してタグを設定します.src/index.ejs :
    <script>
    (function() {
        function showErrorMessage() {
            alert('Oops, something went wrong! Please reload the page.');
        }
    
        // Paths of all bundles
        var bundlesSrcs = <%= JSON.stringify(htmlWebpackPlugin.files.js) %>;
        for(var i=0; i < bundlesSrcs.length; i++) {
            // Create script tag & configure it
            var scriptTag = document.createElement('script');
            scriptTag.src = bundlesSrcs[i];
            scriptTag.addEventListener('error', showErrorMessage);
    
            // Append script tag into body 
            document.body.appendChild(scriptTag);
        }
    })();
    </script>
    
    テンプレートを使用する場合HtmlWebpackPlugin will pass a variable called htmlWebpackPlugin to it . ここでアクセスhtmlWebpackPlugin.files.js これはWebPackによって作成されたすべてのJavaScriptバンドルのパスを含む実行時の配列です.
    この奇妙な建築<%= … %> ” ジャストジャストEmbedded JavaScript templating ドキュメントに情報を印刷するための構文.
    この場合、それはビルドのように何かに解決されます['bundle.foo12.js'] .
    パスの配列を取得すると、この配列を反復処理し、<script> 各パスのタグ.
    新しく作成する前に<script> ドキュメントには、エラーリスナーを添付します.今回はリスナーを時間通りに付けたので、エラーが発生した場合には解凍されます.
    このコードはコンパイルされず、そのままブラウザに出荷されるので、ここでES 5準拠の構文を使おうとしています.

    エラー:エラーメッセージの挿入イメージ


    irlブラウザの警告ボックスにメッセージの代わりにいくつかの良い“エラーページ”を表示したい.エラーページにイメージを表示します.
    それは問題ありません.我々のテンプレートは、それを可能にするのに十分柔軟です.
    まず、インストールしましょうfile-loader 画像を扱うことができます.terminal :
    npm install file-loader --save-dev
    
    さて、このローダを使うためにWebpackに伝えましょう.webpack.config.js :
    module: {
        rules: [
            {
                test: /\.(png|jpe?g|gif)$/i,
                loader: 'file-loader'
            }
        ]
    }
    
    今、我々は直接我々の内側のイメージを必要とすることができますindex.ejs テンプレート
    <%= require('./path_to_image').default %>
    
    詳しい情報はこちらsrc/index.ejs ファイル.
    <!DOCTYPE html>
    <html lang="en" dir="ltr">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Handling script loading errors with HtmlWebpackPlugin</title>
        <style>
            html, body, h1 {
                padding: 0;
                margin: 0;
            }
            #bundleLoadingErrorContainer {
                position: fixed;
                display: flex;
                flex-direction: column;
                align-items: center;
                justify-content: center;
                background-color: #FFF;
                text-align: center;
                width: 100%;
                height: 100%;
            }
            .bundle_error_title {
                padding: 0 1.5%;
            }
        </style>
    </head>
    <body>
        <h1 id="content"></h1>
        <div id="bundleLoadingErrorContainer" style="display: none;">
            <h2 class="bundle_error_title">Oops, something went wrong. Please reload the page.</h2>
            <figure class="photo">
                <img src="<%= require('./assets/bird.jpg').default %>" width="300" height="200" alt="bird">
                <br>
                <br>
                <figcaption>
                    Photo by <a href="https://unsplash.com/@photoholgic" target="_blank" rel="external noopener">Holger Link</a> on <a href="https://unsplash.com/" target="_blank" rel="external noopener">Unsplash</a>
                </figcaption>
            </figure>
        </div>
        <script>
        (function() {
            function showErrorMessage() {
                document.getElementById('bundleLoadingErrorContainer').removeAttribute('style');
            }
    
            var bundlesSrcs = <%= JSON.stringify(htmlWebpackPlugin.files.js) %>;
            for(var i=0; i < bundlesSrcs.length; i++) {
                var scriptTag = document.createElement('script');
                scriptTag.src = bundlesSrcs[i];
                scriptTag.addEventListener('error', showErrorMessage);
    
                document.body.appendChild(scriptTag);
            }
        })();
        </script>
    </body>
    </html>
    

    結論


    それが役に立つことを望みなさい!あなたは最終的なバージョンのすべてのコードを見つけることができますthis repo .
    さて、我々はすべて完了し、どのようにエラーハンドラをテストすることができますChrome’s request blocking feature .