I'm building a custom SAPUI5 app which consists of a seven diagrams (sap.viz.ui5.controls.VizFrame
) in the page's header content (nested inside a sap.suite.ui.commons.ChartContainer
) and a grid table (sap.ui.table.Table
) in the main content area. The data for the charts and the table is provided by an OData V2 Service and the app is running stand-alone on the latest version (1.81.0).
The problem is the long loading time of the app. It takes between 7 and 20 seconds. Is this common for a "more complex" app? I tried to find the bottleneck but everything looks fine. Many network requests are cached (they take 0ms), however, there is a slight delay in between them and I can't see why. Additionally, there is the following warning in the console, although I'm using the data-sap-async="true"
in my index.html
file:
[Deprecation] Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help, check https://xhr.spec.whatwg.org/. [syncXHRFix-dbg.js:211:15]
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Loading - Customer Fact Sheet</title>
<script id="sap-ui-bootstrap"
src="resources/sap-ui-core.js"
data-sap-ui-theme="sap_fiori_3"
data-sap-ui-resourceroots='{"com.schott.fiori.customerfactsheet.customerfactsheet-fiori3": "./"}'
data-sap-ui-compatVersion="edge"
data-sap-ui-oninit="module:sap/ui/core/ComponentSupport"
data-sap-ui-async="true"
data-sap-ui-frameOptions="trusted">
</script>
<link href="https://www.schott.com/static/assets/gfx/favicon/SCHOTT_16.png" rel="shortcut icon" type="image/png" />
</head>
<body class="sapUiBody">
<div data-sap-ui-component data-name="com.schott.fiori.customerfactsheet.customerfactsheet-fiori3" data-id="container" data-settings='{"id" : "customerfactsheet-fiori3"}'></div>
</body>
</html>
{
"_version": "1.12.0",
"sap.app": {
"id": "com.schott.fiori.customerfactsheet.customerfactsheet-fiori3",
"type": "application",
"i18n": "i18n/i18n.properties",
"applicationVersion": {
"version": "1.0.0"
},
"title": "{{appTitle}}",
"description": "{{appDescription}}",
"sourceTemplate": {
"id": "servicecatalog.connectivityComponentForManifest",
"version": "0.0.0"
},
"dataSources": {
"YODATA_SD_CFS_MATRIX_SRV": {
"uri": "/sap/opu/odata/sap/YODATA_SD_CFS_MATRIX_SRV/",
"type": "OData",
"settings": {
"localUri": "localService/metadata.xml"
}
}
}
},
"sap.ui": {
"technology": "UI5",
"icons": {
"icon": "",
"favIcon": "",
"phone": "",
"phone@2": "",
"tablet": "",
"tablet@2": ""
},
"deviceTypes": {
"desktop": true,
"tablet": true,
"phone": true
}
},
"sap.ui5": {
"flexEnabled": false,
"rootView": {
"viewName": "com.schott.fiori.customerfactsheet.customerfactsheet-fiori3.view.Main",
"type": "XML",
"async": true,
"id": "Main"
},
"dependencies": {
"minUI5Version": "1.65.6",
"libs": {
"sap.ui.layout": {},
"sap.ui.core": {},
"sap.m": {}
}
},
"contentDensities": {
"compact": true,
"cozy": false
},
"models": {
"i18n": {
"type": "sap.ui.model.resource.ResourceModel",
"settings": {
"bundleName": "com.schott.fiori.customerfactsheet.customerfactsheet-fiori3.i18n.i18n"
}
},
"": {
"type": "sap.ui.model.odata.v2.ODataModel",
"settings": {
"defaultOperationMode": "Client",
"defaultBindingMode": "OneWay",
"defaultCountMode": "Request"
},
"dataSource": "YODATA_SD_CFS_MATRIX_SRV",
"preload": true
}
},
"resources": {
"css": [{
"uri": "css/style.css"
}]
},
"routing": {
"config": {
"routerClass": "sap.m.routing.Router",
"viewType": "XML",
"async": true,
"viewPath": "com.schott.fiori.customerfactsheet.customerfactsheet-fiori3.view",
"controlAggregation": "pages",
"controlId": "app",
"clearControlAggregation": false
},
"routes": [{
"name": "RouteMain",
"pattern": "RouteMain",
"target": ["TargetMain"]
}],
"targets": {
"TargetMain": {
"viewType": "XML",
"transition": "slide",
"clearControlAggregation": false,
"viewId": "Main",
"viewName": "Main"
}
}
}
},
"sap.platform.hcp": {
"uri": "webapp",
"_version": "1.1.0"
}
}
As the Network tab shows, there are many modules loading sequentially one by one and many of them even via sync XHR. The most important task is to avoid use of sync XHRs.
Ensure that no debug mode, sap-ui-debug
, sap-ui-xx-componentPreload
or sap-ui-xx-libraryPreloadFiles
is active unnecessarily.
Ensure that asynchronous module loading is active:
data-sap-ui-async="true"
in the bootstrap script (id="sap-ui-bootstrap"
).I see in the manifest.json
that only a small number of libraries are declared. According to the Network tab, however, the app uses controls from other libs which aren't declared in the dependencies
. Refer to How to avoid loading UI5 controls as single modules? for the resolution.
Do not use deprecated APIs; especially those known to send sync XHRs such as jQuery.sap.require
, oCore.createComponent
, sap.ui.*fragment
, sap.ui.*view
, sap.ushell.Container.getService
, sap.ui.model.odata.ODataModel
, etc.. Make use of the UI5 linter.
For standalone apps: preload the third party JS files, that are not part of the app bundle or library bundle, asynchronously beforehand. If you inspect the Initiator column within the Network tab, you can detect more modules that are loaded via sync XHR. Adding those modules to the data-sap-ui-modules
should avoid it:
<script id="sap-ui-bootstrap"
src="..."
data-sap-ui-modules="sap/ui/thirdparty/datajs,sap/ui/thirdparty/require"
data-sap-ui-...="..."
></script>
The sap/ui/thirdparty/datajs
is required by v2.ODataModel
. The sap/ui/thirdparty/require
module by the sap.viz
library. Both modules are usually fetched via loadSyncXHR
. The above snippet fixes it. You might find more such modules.
Overall, the above points should should already improve the initial loading time noticeably. For more performance guidelines, go through the Performance Checklist and other Best Practices for Developers.
In order to reduce the number of requests consider dropping the i18n-support altogether if the app targets only a certain group of people speaking the same language. Multiple requests for i18n text bundles are expensive and may cause synchronous XHRs if not configured correctly. There is a way to load the i18n resources asynchronously and also specifying which locales the app supports, but that's for another topic.
sap.ui.model.odata.ODataModel
which uses sync XHR to fetch $metadata.countMode
to "None"
in aggregation binding infos where the $count
is not really needed since the $count
calculations tend to be costly in backend.defaultOperationMode
is set to "Client"
in your case which fetches all entities at once and slowing down the initial app load. Instead, make use of lazy loading app contents, $filter
queries, the growing
feature in sap.m.ListBase
controls, and sap.ui.table.Table
controls if applicable. Refer to "Tables: Which One Should I Choose?"$metadata
and annotation as early as possible by adding preload: true
to the /sap.ui5/models/<model name>/
section.
"": {
"dataSource": "MyV2Source",
"settings": {
"defaultBindingMode": "TwoWay",
"preliminaryContext": true
},
"preload": true
}
Build the app bundle via UI5 Tooling which reduces the number of server requests and, in case of a self-contained build, the application size as well.