Using defer for JS Bundle | Experiment | Performance

Hi,

It was mentioned in docs that it is good practise to put scripts in the head of the page with defer attribute. That gives a very good performance boost since the loading of the main scripts is not blocking the rendering of the HTML on a first load.

We are using the hot-controls and have them inside pages and partials as js[] = “path/to/controls.js”

These scripts are loaded prior framework, causing errors then first loaded. Is there a way to defer load them something like: {% scripts defer %} or {% put scripts defer %} ? Through the compoment imho that was possible.

If there is another approarch please assist with idea.

Best, Max

P.s.: We tried to put all the scripts into one js file and defer load it. Works great, but having few third party libs in there is not an option for pages without these functionality.

Hey Max

With {% put %} you can put <script defer />, however, with js[] that is an interesting point, we may need to find a way to support it.

So we don’t lose track of this, could you create an issue for it on GitHub? You can link to this post too if you like.

Best regards

1 Like

Done: Using defer on frontend for JS Bundle, Libs and custom scripts | Hot-controls and Turbo router · Issue #5833 · octobercms/october · GitHub

1 Like

Im using {% put %} with and it is working just fine.

Although inline scripts in HTML will not work, since the loading order will be different. Inline scripts will be executed before the core, so avoid using them.

1 Like

Hi @mrmax

I have the same problem as you.

I include the js in the head this way

<script defer data-turbo-permanent src="{{ [ 
'assets/js/app.js', 
'@framework', 
'@framework.extras', 
'@framework.turbo', 
]|theme }}"></script> 
{% scripts %} 

and in a partial I use

{% put scripts %}
<script>
oc.registerControl('home-video', class extends oc.ControlBase {
init() {
}
connect() {

}
});
</script>
{% endput %}

But I’m getting this error in the browser: “Uncaught ReferenceError: oc is not defined” because the code in {% put scripts %} is executed before the JavaScript framework is loaded. Do you have a solution to counter this problem?

Thanks

You could either move the controls into a separate JS file and append it to the list of scripts in your head, integrate them into your app.js or do the classic “wait until window or DOM are ready”.

There are a few ways to do it, I think this one should work fine for your case:

Hi @marco.grueter

Thanks for the reply, yes that’s a solution, but I want to put the JavaScript in the HTML because I want to use Twig variables in the JavaScript.

1 Like

Here is how I do it with data attributes:

element-id=“123” data-total-images=“5” data-template=“preorder” data-control=“open-product” data-selected-qty=“null” data-is-nested=“true” data-params1-data=“{even_a_json: “object is possible”}” etc.

For larger object, it is way better to use json as a twig output for whatever needed:

<div data-control="your-control" data-id="123"></div>
<!--  Object from twig -->
<script type="application/json" id="object-model-data-123" hidden>
{
    "any": "thing"
}
</script>

in your your-control.js:

    init(){
        this.dataId = this.options.id;
    },
    connect(){
        this.pageDataElement = document.querySelector('#object-model-data-' + this.dataId);
    	try {
            this.pageData = JSON.parse(this.pageDataElement.textContent);
        } catch (err) {
            console.warn('Invalid page data JSON', err);
        }
    }
2 Likes

This would probably not work as well or will not be stable because of the racing conditions. Right after the document is ready the script code from the page will be executed first prior to defer scripts loaded.

@mrmax

Thanks it will help me.