2

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]

Code snippets of my index.html and manifest.json

<!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"
    }
}

Screenshots of my network tab

Network 1

Network 2

Network 3

Network 4

mxmlndml
  • 144
  • 2
  • 13

1 Answers1

6

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 reduce sync XHRs as much as possible.

  1. 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.
    So it should be:

    "sap.ui5": {
      "dependencies": {
        "libs": {
          "sap.ui.core": {},
          "sap.m": {},
          "sap.ui.table": {},
          "sap.f": {},
          "sap.ui.unified": {},
          "sap.ui.layout": {},
          "sap.viz": {},
          "sap.suite.ui.commons": {}
        },
    

    Some libs are required by other libs transitively (e.g. sap.ui.table requires sap.ui.unified).src You may then add "sap.ui.unified": { lazy: true } if that lib is not directly in use.

  2. Preload thirdparty modules asynchronously beforehand that are usually loaded via loadSyncXHR.

    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"
      data-sap-ui-modules="sap/ui/thirdparty/datajs,sap/ui/thirdparty/require"
      ...>
    

    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.


Other things to consider

I18n

In order to reduce the number of requests consider to drop 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 not only costly in size but also blocking other requests while loading as they're also loaded via sync XHRs by default. There is a way to load them asynchronously and also specifying which locales the app supports, but that's for another topic.

OData Model

Consider to set the count mode to None if not required since $count calculations tend to be costly in the backend. Also the operation mode Client fetches all entities. Consider to lazy-load them instead.

For all aggregation bindings

"": {
  "dataSource": "MyV2Source",
  "settings": {
    "defaultOperationMode": "Default",
    "defaultCountMode": "None",
    "defaultBindingMode": "TwoWay",
    "preliminaryContext": true
  },
  "preload": true
},

About preliminaryContext: see Optimizing Dependent Bindings.

For a single aggregation binding

items: { // e.g.
  path: '/MySet',
  parameters: {
    countMode: 'None',
    operationMode: 'Client' | 'Default' | 'Server' (see API ref)
  }
}

API reference: https://openui5.hana.ondemand.com/api/sap.ui.model.odata.v2.ODataListBinding

UI5 tooling

Before deploying the app, building the app via the following command should reduce the application size drastically:

ui5 build self-contained -a

From https://github.com/SAP/openui5-sample-app#option-2-self-contained-build

This is currently applicable to stand-alone apps only.

Boghyon Hoffmann
  • 17,103
  • 12
  • 72
  • 170