Inspired by following blogs/posts:
Why I want my Fiori to run like a Ferrari
With a SAPUI5 mobile working via 3G you may need to wait up to a couple of seconds, before you get a first screen of the app. The reason is, that SAPUI5 is relative heavy library and need to load some megabytes of data to start working.
If you check the most of sapui5 examples – you’ll see something like
This code will be processed by your browser as followed.
SAPUI5 content consists of 2 libraries: sap.ui.commons and sap.m. That’s why library-preload.json, library.css and library-parameters.json loaded 2 times.
In the browser it looks like
Blue vertical line at almost 2 seconds mean the user will first see some content after loading about 500KB and 2 seconds awaiting (even more on mobile device with 3G).
The idea to show the loading screen as soon as possible to the user, and load SAPUI5 stuff after that. If user have to wait a couple of seconds before start to work – you may want to show some kind of a loading progress. For example, first show a spinner and then a content.
I desided to move all js code into a separate file application.js and include a spinner js code from github.
App logic should look as following
First page load time should reduce from 2+ seconds to 100-200 milliseconds. Initial load size from 500 KB should reduce to about 5 KB.
So, the new index.html consists of 3 js scripts: spinner, application.js and call of initialization.
And now the magic begins. There is no loading of SAPUI5 here.
Let’s check the code of application.js.
oApplication = { // Application is an object
views: {}, // Application views
load: function(src, id, libs, theme, callback) {
var opts = {
length: 12, // The length of each line
width: 4, // The line thickness
radius: 12, // The radius of the inner circle
};
var target = document.getElementById('content');
this.spinner = new Spinner(opts).spin(target);
setTimeout(this.loadSAPUI5(src, id, libs, theme, callback));
},
loadSAPUI5: function (src, id, libs, theme, callback)
{
var s,r,t;
r = false;
s = document.createElement('script');
s.type = 'text/javascript';
s.src = src;
s.id = id;
s.setAttribute("data-sap-ui-libs", libs);
s.setAttribute("data-sap-ui-theme", theme);
s.onload = s.onreadystatechange = function() {
//console.log( this.readyState ); //uncomment this line to see which ready states are called.
if ( !r && (!this.readyState || this.readyState == 'complete') ){
r = true;
callback();
}
};
t = document.getElementsByTagName('script')[0];
t.parentElement.insertBefore(s, t);
},
onSAPUI5Loaded: function(){
oApplication.initializeUI5();
$("body").fadeOut("slow",function(){
$("#content").empty();
$("#content").removeAttr('style');
oApplication.app.placeAt("content");
$(this).fadeIn("slow");
});
},
initializeUI5: function(){
var oApp = new sap.m.App( "mApp" );
var oPage = new sap.m.Page({
id : "mPage", // sap.ui.core.ID
title : "Mobile page", // string
showFooter : false, // boolean, since 1.13.1
});
var oContent = new sap.m.ObjectHeader({
id : "mObjHeader", // sap.ui.core.ID
title : "Title", // string
number : "250", // string
numberUnit : "EUR", // string
markFavorite : true, // boolean, since 1.16.0
markFlagged : true, // boolean, since 1.16.0
showMarkers : true, // boolean, since 1.16.0
attributes : [ new sap.m.ObjectAttribute({
id : "mAttribute", // sap.ui.core.ID
visible : true, // boolean
text : "This is a test attribute of ObjectHeader", // string
}) ], // sap.m.ObjectAttribute
});
oPage.addContent(oContent);
oApp.addPage(oPage);
this.app = oApp;
}
}
So, whole file is a definition of the js object, which has following methods
load – to call from index.html
loadSAPUI5 – to include SAPUI5 into document and load if asynchronously
onSAPUI5Loaded – to hide a spinner and show the app
initializeUI5 – to create SAPUI5 user interface (consists of the code from previous version of index.html).
Logic look like
LoadSAPUI5 is called asynchronously from oApplication.load via setTimeout() function. If you test your app in browser – you’ll get following.
at the bottom of page you can see, that DOMContentLoaded is in 21 ms (it’s due to local test) with 3 files. Let’s upload it to the hosting and make a fair test.
So, before show the application is just 5kb size and 146ms time.
In comparison with the first test:
P.S.: This idea helps to speedup only a first application response. SAPUI5 libraries have to be loaded in any case. Synchron or asynchron – it’s your choice.
P.P.S.: English language is not my native language, and any person is not insured from mistakes and typing errors. If you have found an error in the text, please let me know – I’ll correct the post.
P.P.P.S.: If you have some ideas, how to correct/improve this post – please don’t hesitate to leave a comment.
Thanks for sharing! Very useful!
Kind regards,
Wouter
Thanks for the great blog..
I see “sap-ui-core.js” has been compressed in your screenprint. But on testing this demo POA app the same core js file is not compressed(still 396 KB). Can you suggest?
Thanks
Prabaharan
Hi,
the same works fine for me.
I see, you have a very big response times. Do you work via Proxy?
Regards
Konstantin
Hi
I disabled web security in chrome browser for overcoming cross origin requests to test some apps. Also in my corporate antivirus has Been installed and I don’t know whether we work via proxy.
Lastly I checked in my personal pc , and I had less responsive times like you had.
Thanks for the help.
Prabaharan
Hi, have you got a compression on your PC?
Hi
Can you tell me how to test the application you gave on jsbin. When I test it ,I get cross origin issues.
Thanks
Prabaharan
Works fine for me, just click the button.
Just wondering if this is still working for you? Doesn’t work for me locally; nor when I use the jsbin provided (with Chrome and IE9). It seems that the onload event for the inserted script gets fired BEFORE the script has executed and therefore “initializeUI5()” method fails.
There is another method – simpler but there is still a small delay… You can load the UI library by specifying data-sap-ui-preload=async in the bootstrap. Then add a script tag at the bottom of the html body and attach to the cores Init event. In here you can access all the UI5 functions and effectively start your app. Before the event is fired you can display anything else in the body, including the spinner you’ve used.
Hi Jason,
works fine for me (Chrome, Firefox, IE11)… try the address http://ui5.anikeev.eu/
I’m not sure the data-sap-ui-preload=async works the same way.
At least the core should be preloaded synchron, what is already almost 0,5 MB
Regards
Konstantin
This website does work for me on my mac at home. No matter what I do it will not work for me on Chrome or IE9 on my Windows laptop at work. Weird. Thanks anyway…
The other method that I specified in the previous comment also works well. You are correct that the core loads synchronously from its header script tag; however all the library modules load asynchronously. So you get a smaller delay waiting for the core and you can show a spinner or splash page and then on the core init event you can display you UI5 app.
oh I should add as well… that I’ve found using preload=async actually gives a faster app load time. Considerably faster on a desktop in Chrome. I wonder how it would go in a container like cordova…
Hi Jason,
may you could write a blog post about your investigations. It would be interesting for me. and sure not only for me.
Regards
Konstantin
not sure about that. AFAIK js is a “single thread” language. It means, it loads initially all static defined scripts and only then (after DOMContentLoaded) starts execution.
If you use static script tag even with async preload of libraries your spinner should not start before SAPUI5 Core is loaded.
It could be XSS Settings, which are not allow load OpenUI5 from for https://openui5.hana.ondemand.com on http://ui5.anikeev.eu
https://sapui5.hana.ondemand.com/sdk/#docs/guide/XSSinAppDev.html
No its not XSS. I use those hana.ondemand.com sources all the time.
Its some sort of bug in the browsers. From my research relying on the onload event (chrome) or onreadystatechange event (IE < 10) on older browsers – even just a year or two old) is unpredictable.
jQuery caters for it better. However to use jQuery before sapui5 is loaded would mean separately loading the jQuery library and then using the NoJquery sapui5 sources.
I upgrade chrome to the latest portable edition for windows (33) and it works fine. My previous version on teh windows laptop was 26 and the onload event when inserting a script fires immediately after its been placed into the document instead of after the javascript has executed. This causes the page load to fail. The same issue happens in IE9. this is fixed in IE10 I believe.
Stack Overflow answers to these script-onload issues are to simple call your callback function from inside the script but this is not possible for us as sap as we dont want to edit sap-ui-core.js.
So… beware if using IE9 or Chrome 26!
Regards…Jason.
Yes, I had the latest version on Chrome on Win and Mac.
Thanks for the hint.
Hi Konstantin,
Thanks for putting this together, I followed this blog and got the spinner running. But I found now, if I add parameter in the URL: ?sap-ui-debug=true.
I got below error message:
2014-10-02 18:03:57 Device API logging initialized – DEVICE
Failed to execute ‘write’ on ‘Document’: It isn’t possible to write into a document from an asynchronously-loaded external script unless it is explicitly opened.
Do you have the same issue? I tried version 1.22.4, 1.24.1 even 1.25.0-SNAPSHOT.
Best Regards,
Siyu
Hi Siyu,
unfortunatelly not. I haven’t tried the DEBUG Options.
Regards
Konstantin
In case anybody is still interested in this, I just needed to dig into that issue myself as well.
The problem is the way sap-ui-core.js loads the debug-files. It does so by doing document.write(“<script src=”sap-ui-core-dbg.js” />”); which asynchroniously loaded files cannot do.
The work-around for me was to beautify sap-ui-core.js and modify it. The trick is to add the script-tag to the body specifically rather than to say document.write:
//document.write(“<script type=\”text/javascript\” src=\”” + D + “\”></script>”);
var scriptTag = document.createElement(“script”);
scriptTag.type = “text/javascript”;
scriptTag.src = D;
document.body.appendChild(scriptTag);
This is alright for me, since I develop locally and provision the library via bower.
In case you are interested, I put the file onto github.You can pick it up here.
Edit:
Since the initial script is loaded but does not load the library, I do not bind an event handler to the readystate of the script-tag, but go:
this.ui5LoadingChecker = window.setInterval(function(){
if(sap.ui.core){
self.initializeUI5();
clearInterval(self.ui5LoadingChecker);
}, 1000);
Hope this helps.
Cheers
Michel
Hi Konstantin,
Extremely helpful post! great to see you taking efforts to put this across. I wanted to ask if you can post lifecylce hooks sequence of a typical UI5 application? That’d be great.
I shall try this example and reply in case I face any problems. Thanks again, great work!!
Ameya
The settimeout does not seem to work in IE. Specially in IE9.
The statement: setTimeout(this.loadSAPUI5(src, id, libs, theme, callback)); does not seem to work because IE is stating that there is an invalid argument.
Hi Konstantin Anikeev ,
I’m getting below issue, when I tried your code. Is there any word around for this other than Michael Luther suggestion.
Thanks.
For me, only works on first page, when you click in a tile the app crash.
bye