42

Google PageSpeedを使用して JSF 2.1 + PrimeFaces 4.0 webapp のパフォーマンスを分析している間、特に JavaScript ファイルの解析を延期することをお勧めします。次のような aと<p:layout>と のフォームを含むテスト ページで...<p:watermark><p:fileUpload>

<p:layout>
    <p:layoutUnit position="west" size="100">Test</p:layoutUnit>
    <p:layoutUnit position="center">
        <h:form enctype="multipart/form-data">
            <p:inputText id="input" />
            <p:watermark for="input" value="watermark" />
            <p:focus for="input" />
            <p:fileUpload/>
            <p:commandButton value="submit" />
        </h:form>
    </p:layoutUnit>
</p:layout>

...延期できる次のJavaScriptファイルをリストします。

  • primefaces.js(219.5KiB)
  • jquery-plugins.js(191.8KiB)
  • jquery.js(95.3KiB)
  • layout.js(76.4KiB)
  • fileupload.js(23.8KiB)
  • watermark.js(4.7KiB)

この Google Developers の記事にリンクしており、遅延読み込みとそれを実現する方法が説明されています。基本的に、のイベント<script>中に必要なを動的に作成する必要があります。古いバグのあるブラウザを完全に無視する最も単純な形式では、次のようになります。onloadwindow

<script>
    window.addEventListener("load", function() {
        var script = document.createElement("script");
        script.src = "filename.js";
        document.head.appendChild(script);
    }, false);
</script>

これらのスクリプトを制御できる場合は実行可能ですが、リストされているスクリプトはすべて JSF によって強制的に自動インクルードされます。また、PrimeFaces は一連のインライン スクリプトを HTML 出力にレンダリングし、これらは直接および$(xxx)から呼び出されます。これは、 やのようなエラーが発生するだけなので、実際にそれらをイベントに延期することは簡単にできないことを意味します。jquery.jsPrimeFaces.xxx()primefaces.jsonload$ is undefinedPrimeFaces is undefined

しかし、技術的には可能なはずです。サイトのカスタム スクリプトの多くもそれに依存しているため、jQuery のみを延期する必要がないことを考えると、JSF が PrimeFaces スクリプトを強制的に自動インクルードするのをどのようにブロックして、それらを延期できるようにすることができますか。インラインPrimeFaces.xxx()呼び出し?

4

2 に答える 2

33

使用する<o:deferredScript>

はい、OmniFaces 1.8.1<o:deferredScript>以降の新しいコンポーネントで可能です。技術的に興味のある方のために、関連するソース コードを次に示します。

基本的に、コンポーネントはpostAddToViewイベント中 (つまり、ビューのビルド時間中)UIViewRoot#addComponentResource()に、最後に新しいスクリプト リソースとしてそれ自体を追加し、スクリプト リソースが既にレンダリングされていることを JSF に通知します (標準の JSF API アプローチがないため、クラスを使用し<body>ますHacks#setScriptResourceRendered()) 。それは(まだ?))、JSFがスクリプトリソースを強制的に自動インクルード/レンダリングしないようにします。Mojarra と PrimeFaces の場合、リソースの自動組み込みを無効にするために、キーと値を持つコンテキスト属性を設定する必要があります。Hacksname+librarytrue

レンダラーは、JSF によって生成されたリソース URL が渡される<script>要素を書き込みます。この JS ヘルパーは、リソース URL を収集し、イベント中にそれぞれのOmniFaces.DeferredScript.add()新しい要素を動的に作成します。<script>onload

使い方は<o:deferredScript>かなり簡単<h:outputScript>です。コンポーネントをどこに配置しても問題ありませんが、ほとんどの自己文書化はのようなものです。libraryname<h:head>

<h:head>
    ...
    <o:deferredScript library="libraryname" name="resourcename.js" />
</h:head>

それらを複数持つことができ、それらは最終的に宣言されたのと同じ順序でロードされます。


<o:deferredScript>PrimeFacesでの使用方法は?

これは、PrimeFaces によって生成されたすべてのインライン スクリプトが原因で、少し注意が必要ですが、ヘルパー スクリプトを使用して実行可能jquery.jsであり、延期されないことを受け入れます (ただし、CDN を介して提供することはできます。後で参照してください)。ほぼ 220KiB の大きさのファイルへのインラインPrimeFaces.xxx()呼び出しをカバーするprimefaces.jsには、0.5KiB 未満の縮小されたヘルパー スクリプトを作成する必要があります

DeferredPrimeFaces = function() {
    var deferredPrimeFaces = {};
    var calls = [];
    var settings = {};
    var primeFacesLoaded = !!window.PrimeFaces;

    function defer(name, args) {
        calls.push({ name: name, args: args });
    }
    
    deferredPrimeFaces.begin = function() {
        if (!primeFacesLoaded) {
            settings = window.PrimeFaces.settings;
            delete window.PrimeFaces;
        }
    };

    deferredPrimeFaces.apply = function() {
        if (window.PrimeFaces) {
            for (var i = 0; i < calls.length; i++) {
                window.PrimeFaces[calls[i].name].apply(window.PrimeFaces, calls[i].args);
            }

            window.PrimeFaces.settings = settings;
        }

        delete window.DeferredPrimeFaces;
    };

    if (!primeFacesLoaded) {
        window.PrimeFaces = {
            ab: function() { defer("ab", arguments); },
            cw: function() { defer("cw", arguments); },
            focus: function() { defer("focus", arguments); },
            settings: {}
        };
    }

    return deferredPrimeFaces;
}();

として保存します/resources/yourapp/scripts/primefaces.deferred.js。基本的に、それが行うことはすべてPrimeFaces.ab()cw()およびfocus()呼び出し (スクリプトの下部にあるように) をキャプチャし、それらをDeferredPrimeFaces.apply()呼び出しに延期することです (スクリプトの途中にあるように)。延期する必要がある関数がさらにある可能性があることに注意してくださいPrimeFaces.xxx()。それがアプリの場合である場合は、自分でそれらを内部に追加できますwindow.PrimeFaces = {}(いいえ、JavaScript では未決定をカバーする「キャッチオール」メソッドを使用することはできません)。機能)。

このスクリプトと を使用する前に<o:deferredScript>、まず、生成された HTML 出力に自動的に含まれるスクリプトを決定する必要があります。質問に示されているテスト ページの場合、生成された HTML に次のスクリプトが自動的に含まれます(Web ブラウザーでページを右クリックし、 [ソースの表示]<head>を選択すると、これを見つけることができます)。

<script type="text/javascript" src="/playground/javax.faces.resource/jquery/jquery.js.xhtml?ln=primefaces&amp;v=4.0"></script>
<script type="text/javascript" src="/playground/javax.faces.resource/jquery/jquery-plugins.js.xhtml?ln=primefaces&amp;v=4.0"></script>
<script type="text/javascript" src="/playground/javax.faces.resource/primefaces.js.xhtml?ln=primefaces&amp;v=4.0"></script>
<script type="text/javascript" src="/playground/javax.faces.resource/layout/layout.js.xhtml?ln=primefaces&amp;v=4.0"></script>
<script type="text/javascript" src="/playground/javax.faces.resource/watermark/watermark.js.xhtml?ln=primefaces&amp;v=4.0"></script>
<script type="text/javascript" src="/playground/javax.faces.resource/fileupload/fileupload.js.xhtml?ln=primefaces&amp;v=4.0"></script>

jquery.jsファイルをスキップし<o:deferredScripts>て、残りのスクリプトをまったく同じ順序で作成する必要があります。リソース名はJSFマッピング/javax.faces.resource/ を除い.xhtmlた後の部分です(私の場合)。lnライブラリ名はリクエストパラメータで表されます。

したがって、これは次のようにする必要があります。

<h:head>
    ...
    <h:outputScript library="yourapp" name="scripts/primefaces.deferred.js" target="head" />
    <o:deferredScript library="primefaces" name="jquery/jquery-plugins.js" />
    <o:deferredScript library="primefaces" name="primefaces.js" onbegin="DeferredPrimeFaces.begin()" />
    <o:deferredScript library="primefaces" name="layout/layout.js" />
    <o:deferredScript library="primefaces" name="watermark/watermark.js" />
    <o:deferredScript library="primefaces" name="fileupload/fileupload.js" onsuccess="DeferredPrimeFaces.apply()" />
</h:head>

これで、合計サイズが約 516KiB のこれらすべてのスクリプトがonloadイベントに延期されます。はofで呼び出す必要があり、それはofDeferredPrimeFaces.begin()で呼び出す必要があることに注意してくださいonbegin<o:deferredScript name="primefaces.js">DeferredPrimeFaces.apply()onsuccess <o:deferredScript library="primefaces">

がandにprimefaces.js置き換えられたPrimeFaces 6.0 以降を使用している場合は、代わりに以下を使用します。core.jscomponents.js

<h:head>
    ...
    <h:outputScript library="yourapp" name="scripts/primefaces.deferred.js" target="head" />
    <o:deferredScript library="primefaces" name="jquery/jquery-plugins.js" />
    <o:deferredScript library="primefaces" name="core.js" onbegin="DeferredPrimeFaces.begin()" />
    <o:deferredScript library="primefaces" name="components.js" />
    <o:deferredScript library="primefaces" name="layout/layout.js" />
    <o:deferredScript library="primefaces" name="watermark/watermark.js" />
    <o:deferredScript library="primefaces" name="fileupload/fileupload.js" onsuccess="DeferredPrimeFaces.apply()" />
</h:head>

パフォーマンスの向上に関して重要な測定ポイントは、Chrome の開発者ツールの [ネットワークDOMContentLoaded] タブの下部にある時間です。3 年前のラップトップで Tomcat が提供した質問に示されているテスト ページでは、約 500 ミリ秒から約 270 ミリ秒に減少しました。これは比較的大きく (ほぼ半分!)、HTML のレンダリングが比較的遅く、DOM コンテンツが読み込まれるまでタッチ イベントが完全にブロックされるため、モバイル/タブレットで最も大きな違いが生じます。

JSFリソース管理のルール/ガイドラインに従うかどうかに依存する(カスタム)コンポーネントライブラリの場合であることに注意してください。たとえば、RichFaces はそうではなく、その上に別のカスタム レイヤーを自作したため、その<o:deferredScript>上では使用できませんでした。リソース ライブラリとは何か、またどのように使用する必要があるかについても参照してください。

警告:後で同じビューに新しい PrimeFaces コンポーネントを追加し、JavaScriptundefinedエラーに直面している場合、新しいコンポーネントにも独自の JS ファイルが付属している可能性が高く、これも延期する必要がありますprimefaces.js。適切なスクリプトを簡単に判断するには、生成された HTML で新しいスクリプトを確認し、上記の手順に基づいて<head>別のスクリプトを追加します。<o:deferredScript>


ボーナス:CombinedResourceHandler認識<o:deferredScript>

OmniFaces を使用している場合は、同じ属性を持つすべての遅延スクリプトをCombinedResourceHandler透過的に認識して結合し、単一の遅延リソースにすることを知っておくとよいでしょう。たとえば、これ...<o:deferredScript>group

<o:deferredScript group="essential" ... />
<o:deferredScript group="essential" ... />
<o:deferredScript group="essential" ... />
...
<o:deferredScript group="non-essential" ... />
<o:deferredScript group="non-essential" ... />

... 互いに同期してロードされる 2 つの結合された遅延スクリプトになります。注:group属性はオプションです。何もない場合、それらはすべて単一の遅延リソースに結合されます。

<body>実際の例として、ZEEFサイトの下部を確認してください。すべての重要な PrimeFaces 関連のスクリプトと一部のサイト固有のスクリプトは最初の延期スクリプトにまとめられ、必須ではないすべてのソーシャル メディア関連のスクリプトは 2 番目の延期スクリプトにまとめられます。ZEEF のパフォーマンス向上に関しては、最新のハードウェア上の JBoss EAP サーバーのテストでは、時間がDOMContentLoaded~3 秒から ~1 秒になりました。


ボーナス #2: PrimeFaces jQuery を CDN に委任する

いずれにせよ、すでに OmniFaces を使用CDNResourceHandlerしている場合は、次のコンテキスト パラメータによって PrimeFaces jQuery リソースを真の CDN に委任するためにいつでも使用できweb.xmlます。

<context-param>
    <param-name>org.omnifaces.CDN_RESOURCE_HANDLER_URLS</param-name>
    <param-value>primefaces:jquery/jquery.js=http://code.jquery.com/jquery-1.11.0.min.js</param-value>
</context-param>

jQuery 1.11 は、PrimeFaces 4.0 で内部的に使用されているように、1.10 よりも大幅にパフォーマンスが改善されており、完全な下位互換性があることに注意してください。ZEEF でドラッグ アンド ドロップを初期化するときに、数百ミリ秒節約できました。

于 2014-04-19T18:07:17.430 に答える