Defer loading and parsing of PrimeFaces JavaScript files

前端 未结 2 1065
不思量自难忘°
不思量自难忘° 2020-12-02 08:10

While analyzing the performance of a JSF 2.1 + PrimeFaces 4.0 webapp with Google PageSpeed, it recommends among others to defer parsing of JavaScript files. On a test page w

2条回答
  •  执笔经年
    2020-12-02 08:24

    Use

    Yes, it is possible with the component which is new since OmniFaces 1.8.1. For the technically interested, here's the involved source code:

    • The UI component: DeferredScript
    • The HTML renderer: DeferredScriptRenderer
    • The JS helper: deferred.unminified.js

    Basically, the component will during the postAddToView event (thus, during the view build time) via UIViewRoot#addComponentResource() add itself as a new script resource in end of and via Hacks#setScriptResourceRendered() notify JSF that the script resource is already rendered (using Hacks class as there's no standard JSF API approach for that (yet?)), so that JSF won't forcibly auto-include/render the script resource anymore. In case of Mojarra and PrimeFaces, a context attribute with key of name+library and a value of true has to be set in order to disable auto-inclusion of the resource.

    The renderer will write a

    You need to skip the jquery.js file and create in exactly the same order for the remaining scripts. The resource name is the part after /javax.faces.resource/ excluding the JSF mapping (.xhtml in my case). The library name is represented by ln request parameter.

    Thus, this should do:

    
        ...
        
        
        
        
        
        
    
    

    Now all those scripts with a total size of about 516KiB are deferred to onload event. Note that DeferredPrimeFaces.begin() must be called in onbegin of and that DeferredPrimeFaces.apply() must be called in onsuccess of the last .

    In case you're using PrimeFaces 6.0 or newer, where the primefaces.js has been replaced by core.js and components.js, use the below instead:

    
        ...
        
        
        
        
        
        
        
    
    

    As to performance improvement, important measuring point is the DOMContentLoaded time as you can find in bottom of Network tab of Chrome's developer tools. With the test page as shown in the question served by Tomcat on a 3 year old laptop, it decreased from ~500ms to ~270ms. This is relatively huge (almost the half!) and makes the most difference on mobiles/tablets as they render HTML relatively slow and touch events are fully blocked until the DOM content is loaded.

    Noted should be that you're in case of (custom) component libraries dependent on whether they obey the JSF resource management rules/guidelines or not. RichFaces for example didn't and homebrewed another custom layer over it, making it impossible to use on it. See also what is the resource library and how should it be used?

    Warning: if you're adding new PrimeFaces components on the same view afterwards and are facing JavaScript undefined errors, then the chance is big that the new component also comes with its own JS file which should also be deferred, because it's depending on primefaces.js. A quick way to figure the right script would be to check the generated HTML for the new script and then add another for it based on the above instructions.


    Bonus: CombinedResourceHandler recognizes

    If you happen to use OmniFaces CombinedResourceHandler, then it's good to know that it transparently recognizes and combines all deferred scripts with the same group attribute into a single deferred resource. E.g. this ...

    
    
    
    ...
    
    
    

    ... will end up in two combined deferred scripts which are loaded synchronously after each other. Note: the group attribute is optional. If you don't have any, then they will just all be combined into a single deferred resource.

    As a live example, check the bottom of of the ZEEF site. All essential PrimeFaces-related scripts and some site-specific scripts are combined in the first deferred script and all non-essential social media related scripts are combined in the second deferred script. As to performance improvement of ZEEF, on a test JBoss EAP server on modern hardware, the time to DOMContentLoaded went from ~3s to ~1s.


    Bonus #2: delegate PrimeFaces jQuery to CDN

    In any case, if you're already using OmniFaces, then you can always use CDNResourceHandler to delegate the PrimeFaces jQuery resource to a true CDN by the following context param in web.xml:

    
        org.omnifaces.CDN_RESOURCE_HANDLER_URLS
        primefaces:jquery/jquery.js=http://code.jquery.com/jquery-1.11.0.min.js
    
    

    Note that jQuery 1.11 has some major performance improvements over 1.10 as internally used by PrimeFaces 4.0 and that it's fully backwards compatible. It saved a couple of hundred milliseconds when initializing drag'n'drop on ZEEF.

提交回复
热议问题