How to find loading bottleneck of a slow-loading SAPUI5 app - javascript

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.
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.
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.

Related

Built Neutralino app cannot open localhost?

I have a very peculiar issue, I have a Neutralino app built with Preact that runs perfectly fine when run with neu run, however, once I build the project (with enableServer on or off), the built application will not load anything.
With enableServer on, it will complain that it cannot connect to localhost:
"This localhost page cannot be found"
and with it off it gives me a completely white screen/DOM.
The GitHub repository is here: https://github.com/SpikeHD/XeniaLauncher
I suspect it may be the way I am building the project, but I find it weird that neu run works completely fine.
Below is my configuration:
{
"applicationId": "js.xenia_launcher.app",
"version": "1.0.0",
"defaultMode": "window",
"port": 0,
"documentRoot": "/build/",
"url": "/",
"enableServer": true,
"enableNativeAPI": true,
"tokenSecurity": "one-time",
"logging": {
"enabled": true,
"writeToLogFile": true
},
"nativeAllowList": [
"app.*",
"os.*",
"filesystem.*",
"storage.*",
"window.*",
"debug.log"
],
"modes": {
"window": {
"title": "XeniaLauncher",
"width": 1000,
"height": 800,
"minWidth": 400,
"minHeight": 200,
"fullScreen": false,
"alwaysOnTop": false,
"icon": "/resources/icons/appIcon.png",
"enableInspector": true,
"borderless": false,
"maximize": false,
"hidden": false,
"resizable": true,
"exitProcessOnClose": true
},
"browser": {},
"cloud": {
"url": "/resources/#cloud",
"nativeAllowList": [
"app.*"
]
},
"chrome": {
"width": 1000,
"height": 800,
"args": "--user-agent=\"Neutralinojs chrome mode\""
}
},
"cli": {
"binaryName": "XeniaLauncher",
"resourcesPath": "/resources/",
"extensionsPath": "/extensions/",
"clientLibrary": "/resources/js/neutralino.js",
"binaryVersion": "4.4.0",
"clientVersion": "3.3.0"
}
}
Fixed it! I ended up just setting up some scripts that will put all the built Preact code into the resources folder and re-setup my config to only use the resources folder, like the original Neutralino template. I am sure there could have been a more elegant way, but I am still just learning Neutralino and Preact so this works perfectly well enough for me.
It looks like the resourcesPath needs to be set to the directory where your index.html is found. I have a public dir, and moved the icons over there, then set resourcesPath to /public/ and I was able to neu build --release and run.

Angular PWA - Why browser cache keeps growing? How to stop it?

I'm wondering is there a way to manage the browser cache and keep it as less as possible in Angular PWA apps.
Growing browser cache in IOS devices fills the cache limit in website data and the app stops working because of that!
Is there a specific configuration for service workers to achieve this?
My current configuration:
{
"index": "/",
"assetGroups": [{
"name": "app",
"installMode": "prefetch",
"resources": {
"files": [
"/*.css",
"/*.js"
]
}
}, {
"name": "assets",
"installMode": "lazy",
"updateMode": "prefetch",
"resources": {
"files": [
"/assets/**",
"/profiles/**",
"/*.(eot|svg|cur|webp|png|gif|otf|ttf|woff|woff2|ani)"
]
}
}],
"dataGroups": [{
"name": "api",
"version": 1,
"urls": ["/api/**"],
"cacheConfig": {
"strategy": "freshness",
"maxSize": 1,
"maxAge": "7d",
"timeout": "1s"
}
}]
}
I dont think you can stop the caching growing because as I can see right now you are caching everything include static resource and api caching
What you can do is to tell it not to cache your page, by sending the appropriate headers or using these meta tags:
<meta http-equiv='cache-control' content='no-cache'>
<meta http-equiv='expires' content='0'>
<meta http-equiv='pragma' content='no-cache'>
Update remove this and you should be good. This is for API caching
"dataGroups": [{
"name": "api",
"version": 1,
"urls": ["/api/**"],
"cacheConfig": {
"strategy": "freshness",
"maxSize": 1,
"maxAge": "7d",
"timeout": "1s"
}
}]

OpenComponents returns TEMPLATE_NOT_SUPPORTED_ERROR for custom compiler

I'm trying to setup OpenComponents with custom compiler (based on oc-template-react).
My component's package.json:
{
"name": "hi-there",
"description": "Hello World OC",
"version": "1.0.0",
"oc": {
"files": {
"data": "server.js",
"template": {
"src": "app.js",
"type": "oc-my-template"
}
},
"parameters": {
"name": {
"default": "World",
"description": "Your name",
"example": "Jane Doe",
"mandatory": false,
"type": "string"
}
}
},
"devDependencies": {
"oc-my-template-compiler": "*"
}
}
oc-my-template-compiler is installed. Packaging works fine, but I'm getting following error from registry:
GET http://localhost:3030/hi-there/1.0.0/?__oc_Retry=0
{
code: "TEMPLATE_NOT_SUPPORTED"
error: "oc-my-template is not a supported oc-template"
name: "hi-there"
requestVersion: "1.0.0"
}
I think I should register template with oc.registerTemplate but I'm not sure where should I do it. Should not dev registry take care about it?
If you look into the "oc-client" component (visiting http://localhost:3030/oc-client) you should have a snippet of what is required to correctly render your component with the custom template.
One common thing to do if you are using OC in a dynamic web app, is to server-side render the oc-client component in order to serve the html page with the oc-client already initialised with all the supported templates.

How to retrieve a content of Alfresco workflow?

I have a workflow where I have audio content. I need to access for this content in other application (with javascript). I am trying with a GET method to this URL:
http://localhost:8086/alfresco/service/cmis/s/SpacesStore/i/1a7be6f8-0c50-4995-a211-1736642db06a/children?alf_ticket=TICKET_f9906d69befbc49668b92ddf372d62532a29ce7d
(In this URL, the id "1a7be6f8-0c50-4995-a211-1736642db06a" is the identificator of the package of the workflow task.)
But, the response is the next XML:
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:app="http://www.w3.org/2007/app" xmlns:cmisra="http://docs.oasis-open.org/ns/cmis/restatom/200908/" xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/" xmlns:alf="http://www.alfresco.org" xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/">
<author><name>admin</name></author>
<generator version="4.2.0 (r56674-b4848)">Alfresco (Community)</generator>
<icon>http://localhost:8086/alfresco/images/logo/AlfrescoLogo16.ico</icon>
<id>urn:uuid:1a7be6f8-0c50-4995-a211-1736642db06a-children</id>
<link rel="service" href="http://localhost:8086/alfresco/service/cmis"/>
<link rel="self" href="http://localhost:8086/alfresco/service/cmis/s/SpacesStore/i/1a7be6f8-0c50-4995-a211-1736642db06a/children?alf_ticket=TICKET_f9906d69befbc49668b92ddf372d62532a29ce7d"/>
<link rel="via" href="http://localhost:8086/alfresco/service/cmis/s/workspace:SpacesStore/i/1a7be6f8-0c50-4995-a211-1736642db06a"/>
<link rel="up" href="http://localhost:8086/alfresco/service/cmis/s/workspace:SpacesStore/i/13dd8d00-4ccd-4894-87fc-0b055cf41a4b/children" type="application/atom+xml;type=feed"/>
<link rel="down" href="http://localhost:8086/alfresco/service/cmis/s/workspace:SpacesStore/i/1a7be6f8-0c50-4995-a211-1736642db06a/descendants" type="application/cmistree+xml"/>
<link rel="http://docs.oasis-open.org/ns/cmis/link/200908/foldertree" href="http://localhost:8086/alfresco/service/cmis/s/workspace:SpacesStore/i/1a7be6f8-0c50-4995-a211-1736642db06a/tree" type="application/atom+xml;type=feed"/>
<title>1a7be6f8-0c50-4995-a211-1736642db06a Children</title>
<updated>2015-05-27T11:18:13.600-04:00</updated>
<opensearch:totalResults>0</opensearch:totalResults>
<opensearch:startIndex>0</opensearch:startIndex>
<opensearch:itemsPerPage>-1</opensearch:itemsPerPage>
<cmisra:numItems>0</cmisra:numItems>
</feed>
I don't know how to use this XML for my purpose. I need to hear the content (mp3 audio file) and modify its properties in my custom application.
Also I am trying with the next URL (GET):
http://localhost:8086/alfresco/service/api/node/content/workspace/SpacesStore/1a7be6f8-0c50-4995-a211-1736642db06a
But the result is: Web Script Status 404 - Not Found
How to retrieve a content of a workflow? There is some RESTful URL for this ?
Thanks for any help.
Greetings,
Pablo.
Finally, I resolve my questions using other RESTful URL. So, the steps to retrieve a content of a workflow is the next:
1. I get the package ID (this is a folder node) of the task of workflow:
GET /alfresco/service/api/task-instances/activiti$taskID
{
"data":
{
"id": "activiti$taskID",
"url": "api\/task-instances\/activiti$taskID",
"name": "wf:taskName",
"title": "Task for this",
"description": "Hello World !",
"state": "IN_PROGRESS",
"path": "api\/workflow-paths\/activiti$workflowID",
"isPooled": false,
"isEditable": true,
"isReassignable": true,
"isClaimable": false,
"isReleasable": false,
"outcome": null,
"owner":
{
"userName": "admin",
"firstName": "Admin",
"lastName": "istrator"
},
"properties":
{
"bpm_percentComplete": 0,
"bpm_description": "Hello World !",
"bpm_hiddenTransitions": [],
"bpm_package":"workspace:\/\/SpacesStore\/1a7be6f8-0c50-4995-a211-1736642db06a",
...........................................................
}
So, the package ID is: 1a7be6f8-0c50-4995-a211-1736642db06a
2. With the package ID, I get the content which I need of this package:
GET /alfresco/service/slingshot/node/workspace/SpacesStore/1a7be6f8-0c50-4995-a211-1736642db06a
.............................................
"children": [
{
"name": {
"name": "{http:\/\/www.alfresco.org\/model\/content\/1.0}grabacion1.mp3",
"prefixedName": "cm:grabacion1.mp3"
},
"nodeRef": "workspace://SpacesStore/9ed7905d-7017-40e9-9514-93244b0a9a6a",
"type": {
"name": "{http:\/\/www.alfresco.org\/model\/content\/1.0}content",
"prefixedName": "cm:content"
},
"assocType": {
"name": "{http:\/\/www.alfresco.org\/model\/bpm\/1.0}packageContains",
"prefixedName": "bpm:packageContains"
},
"primary": false,
"index": 0
}
],
.............................................
So, the content ID is: 9ed7905d-7017-40e9-9514-93244b0a9a6a
3. Finally, I get the content which I need:
GET /alfresco/service/api/node/content/workspace/SpacesStore/9ed7905d-7017-40e9-9514-93244b0a9a6a
If you need access to the properties of the content, you can use the URL of the step two with the content ID (in this example: 9ed7905d-7017-40e9-9514-93244b0a9a6a).
Greetings :)

Include JS file after Ext in Sencha CMD

I have a Sencha Touch 2 application (non MVC), it works well, except for one JS file. In there I define a store.
In app.json I included sencha-touch.js and this file also. It is building properly, but when I open the page it is saying that
Object has no method 'create'
My app.json:
{
"path": "touch/sencha-touch.js",
"x-bootstrap": true
},
{
"path": "res/mystore.js"
},
{
"path": "bootstrap.js",
"x-bootstrap": true
},
{
"path": "app.js",
"bundle": true, /* Indicates that all class dependencies are concatenated into this file when build */
"update": "delta"
}
I tried many things, but nothing seems to be working, change the sequence, x-bootstrap to true for mystore.js, but nothing. Any idea?
Thanks in advance!
It seems like the problem was the order. After I changed like this:
{
"path": "touch/sencha-touch.js",
"x-bootstrap": true
},
{
"path": "bootstrap.js",
"x-bootstrap": true
},
{
"path": "app.js",
"bundle": true, /* Indicates that all class dependencies are concatenated into this file when build */
"update": "delta"
},
{
"path": "res/mystore.js"
}
it works perfectly.

Categories