且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

如何找到加载缓慢的 SAPUI5 应用程序的加载瓶颈

更新时间:2023-12-04 22:55:22

正如网络"选项卡所示,有许多模块一一依次加载,其中许多甚至通过同步 XHR 加载.最重要的任务是尽可能减少同步 XHR.

  1. 我在 manifest.json 中看到只声明了少量库.但是,根据网络选项卡,该应用程序使用来自其他库的控件,这些控件未在 dependencies 中声明.
    所以应该是:

    sap.ui5":{依赖关系":{库":{sap.ui.core":{},sap.m":{},sap.ui.table":{},sap.f":{},sap.ui.unified":{},sap.ui.layout":{},sap.viz":{},sap.suite.ui.commons":{}},

    一些库被其他库传递需要(例如sap.ui.table需要sap.ui.unified).src 然后您可以添加 "sap.ui.unified": { lazy: true } 如果该库没有直接使用.

  2. 预先异步预加载通常通过 loadSyncXHR 加载的第三方模块.

    如果您检查 Network 选项卡中的 Initiator 列,您可以检测到更多通过同步 XHR 加载的模块.将这些模块添加到 data-sap-ui-modules 应该避免它:

    v2.ODataModel 需要 sap/ui/thirdparty/datajs.sap.viz 库的 sap/ui/thirdparty/require 模块.这两个模块通常通过 loadSyncXHR 获取.上面的代码片段修复了它.您可能会发现更多此类模块.

总的来说,以上几点应该已经显着改善了初始加载时间.如需更多性能指南,请查看性能清单.


其他需要考虑的事项

I18n

为了减少请求数量考虑完全放弃对 i18n 的支持,如果该应用仅针对使用相同语言的特定人群.多次请求i18n 文本包不仅大小昂贵,而且在加载时会阻止其他请求,因为默认情况下它们也是通过同步 XHR 加载的. 有一种方法可以异步加载它们并指定应用支持的区域设置,但这是另一个主题.

OData 模型

如果不需要,请考虑将计数模式设置为 None,因为 $count 计算在后端往往成本很高.此外,操作模式 Client 获取所有实体.考虑改为延迟加载它们.

对于所有聚合绑定

":{数据源":MyV2Source",设置":{defaultOperationMode":默认",defaultCountMode":无",默认绑定模式":双向",preliminaryContext":真},预加载":真},

关于 preliminaryContext:请参阅items: {//例如路径:'/MySet',参数: {计数模式:'无',操作模式:'客户端' |'默认' |服务器"(参见 API 参考)}}

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

UI5 工具

在部署应用程序之前,通过以下命令构建应用程序应该会大大减少应用程序的大小:

ui5 build self-contained -a

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

这目前仅适用于独立应用.

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

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.