17 Optimize Performance of Oracle JET Apps

Oracle JET applications are client-side HTML5 applications. Most performance optimization recommendations relating to client-side HTML applications also apply to applications developed using Oracle JET or to Oracle JET components. In addition, some Oracle JET components have performance recommendations that are specific to the component.

About Performance and Oracle JET Apps

In general, you can optimize an Oracle JET app the same way that you would optimize performance for any client-side HTML5 app.

There are many online resources that provide tips for performance optimization. For example, the Google Developers website describes their tools for improving the performance of the app.

Most of the recommendations made by the Google tools are up to you to implement, but Oracle JET includes features that can reduce the payload size and the number of trips to retrieve the Oracle JET app's CSS. In general, strive to follow these guidelines.

  1. Always minify and bundle app resources to reduce the number of requests from the server.
  2. Configure the app to load resources from Oracle CDN to minimize network usage.
  3. Configure the app to use the Oracle JET library on CDN so that the bundles-config.js script will load minified bundles and modules by default.
  4. Compress the app with gzip to reduce the size (and enable compression on the web server.)
  5. Enable HTTP caching on web server so that some requests can be served from the cache instead of from the server. Use ETags on files that should always be served from the server.
  6. Take advantage of HTTP/2 to serve page resources faster than is possible with HTTP/1.1.
  7. Use a single page app, so that the browser isn’t forced to tear down and rebuild the whole app.
  8. Avoid putting too many data-centric components into a single page.
  9. Optimize graphic images: prefer vector format; choose the appropriate image format based on the best overall compression available.

For more information about these optimization tips and others, see Add Performance Optimization to an Oracle JET App.

Add Performance Optimization to an Oracle JET App

Most tips for optimizing performance of web apps also apply to Oracle JET apps. However, there are some steps you can take that apply specifically to Oracle JET apps to optimize JavaScript, CSS, Oracle JET components, REST calls, and images.

JavaScript Performance Tips

Performance Tip Details

Maintain the expected JavaScript folder structure

Use folder organization generated by the Oracle JET tooling and maintain all JavaScript files inside the js folder of the app root directory.

Send only the JavaScript code that your app needs.

Oracle JET includes modules that you can load with RequireJS. For additional information, see Use RequireJS for Modular Development. One approach is to preload the JavaScript modules that your app will use. The following sample shows how to modify the require function in the app main.js.
require(['ojs/ojbootstrap', 'knockout', 'ojs/ojknockout'],
  function (Bootstrap) {
    Bootstrap.whenDocumentReady().then(
      function () {
        function init() {
        }

        // If running in a hybrid (e.g. Cordova) environment, we need to wait for the deviceready
        // event before executing any code that might interact with Cordova APIs or plugins.
        if (document.body.classList.contains('oj-hybrid')) {
          document.addEventListener('deviceready', init);
        } else {
          init();
        }

        //after main doc is done preload some js
        require(['ojs/ojbutton', 'ojs/ojdvt-base', 'ojs/ojtree', 'ojs/ojaccordion', 'ojs/ojtreemap'], //example of what can be preloaded
          function()
          {
          }
        );
      }
    );
  }
);

Send minified/obfuscated JavaScript.

Oracle JET provides minified versions of the Oracle JET library as well as third-party libraries when available. By default, the path mappings for the minified versions of these libraries in path_mapping.json will be injected into the Oracle JET RequireJS bootstrap file included with all Oracle JET distributions when you build a release version of the app. The following sample shows a single library from path_mappings.json where the minified library is available for release mode.

"jquery": {
   "cdn": "3rdparty",
   "cwd": "node_modules/jquery/dist",
   "debug": {
     "src": "jquery.js",
     "path": "libs/jquery/jquery-#{version}.js",
     "cdnPath": "jquery/jquery-3.x.x"
   },
   "release": {
     "src": "jquery.min.js",
     "path": "libs/jquery/jquery-#{version}.min.js",
     "cdnPath": "jquery/jquery-3.x.x.min"
   }
},

For additional information about using the RequireJS bootstrap file in your Oracle JET app, see About RequireJS in an Oracle JET App.

Minimize the number of trips to retrieve the JavaScript.

Oracle JET doesn't provide support for minimizing the number of trips, but RequireJS has an optimization tool that you can use to combine modules. For additional detail, see the documentation for the RequireJS optimizer. Alternatively, use a JavaScript code minifier, such as Terser to bundle and minify the JavaScript source.

Use lazy loading for JavaScript not needed on first render.

You can lazy load content that is not needed on first render. For example, you can configure the oj-film-strip component to retrieve child node data only when requested. For an example, see the Lazy Loading (oj-film-strip) Oracle JET Cookbook example.

Compress or zip the payload.

Oracle JET has no control over the server, and this recommendation is up to you to implement. For some additional information and tips, see https://developers.google.com/speed/docs/best-practices/payload#GzipCompression.

Set cache headers.

JET has no control over the server, and this recommendation is up to you to implement. For additional information about cache optimization, see https://developers.google.com/speed/docs/best-practices/caching.

CSS Performance Tips

Performance Tip Details

Maintain the expected CSS folder structure

Use folder organization generated by the Oracle JET tooling and maintain all CSS files inside the css folder of the app root directory.

Render pages for all the needed CSS once

Link to style sheets inside the HEAD section of the HTML page and do not use CSS links in the page body to avoid rerendering an already loaded page.

Send only the CSS that your app needs.

To control the CSS content that goes into your app, create a custom theme and limit what it includes to the CSS that your app needs. See Optimize the CSS in a Custom Theme.

Also, if you're using the Oracle JET grid system, you can also control which responsive classes get included in the CSS. For details, see Control the Size and Generation of the CSS.

Send minified/obfuscated CSS.

By default, Oracle JET includes minified CSS. However, if you want to modify the CSS to send only what your app needs, you can use Sass to minimize your output. For additional information, see the :compressed option at: http://sass-lang.com/documentation/file.SASS_REFERENCE.html#output_style.

Oracle JET App and Component Performance Tips

Performance Tip Details
Configure your app to load bundled JET modules and libraries using Oracle CDN and the bundle configuration support it provides for JET. Leveraging the Oracle Content Delivery Network (CDN) and bundle configuration support optimizes the app startup performance of enterprise apps and also ensures that your app builds with the module and library versions required for a particular Oracle JET release. Referring directly to the bundles within the app is not recommended and that includes adding or modifying links that make direct reference to the configuration of the bundles. For additional information, see About Configuring the App for Oracle CDN Optimization.
Consider using plain HTML components were possible. For stamping components (like Table or ListView) or declarative components (like a composite component), if you embed a simple Oracle JET component like a read-only input component or button, consider using a plain HTML component instead. The plain HTML component can be lighter weight (less DOM), and if the component is stamped that can add up. However, note that with plain HTML, you also won't have access to any built-in accessibility support, such as converters and validators, which the Oracle JET component provides.

Follow Oracle JET component best practices.

Consult the API documentation for the Oracle JET component. The API Reference for Oracle® JavaScript Extension Toolkit (Oracle JET) includes a performance section for a component when applicable. For example, see the ojDataGrid—Performance section.

Limit number of Oracle JET components per page.

The number of components on the page will impact the page load time. If you want to reduce the load time, place fewer data-centric components in the page.

Use the oj-defer element to delay binding execution

When a component contains hidden content to display, the oj-defer element delays the process of applying bindings to the child components until the hidden content becomes visible. For additional information on oj-defer, see the oj-defer API documentation.

Limit the fetch size for collection components

Set the collection component scroll-policy-options.fetch-size attribute equal to the number the number of items to display in the component viewport. Also set scroll-policy="loadMoreOnScroll" to ensure that the fetch for additional items occurs only when the user scrolls toward the end of the fetched list.

REST Request Performance Tips

Performance Tip Details

Reduce the number of roundtrips between client and server.

There are a number of techniques that you can use to reduce the number of roundtrips, and here are some examples:

  • The number of REST requests on the page will impact the page load time. If you need to reduce the load time, simplify your page and make fewer REST requests.

  • A REST call that needs data from a previous REST call creates a dependency that triggers serialization and increases the data fetch response time. To reduce response time, minimize the number of dependent REST calls by redefining your REST calls to fetch only the data your UI requires. Ideally, the REST endpoint supports fetching of the exact data or can be redesigned as needed.

  • All REST operations should be executed asynchronously. To manage async state, invoke REST endpoints with a method to return a Promise from the start.

  • If the REST endpoint is slow to respond, and the data is essential to the app, consider prefetching the data in a non-blocking REST endpoint invocation.

  • The REST endpoint should be designed to support pagination.

Image Optimization

Performance Tip Details

Maintain the expected images folder structure

Use folder organization generated by the Oracle JET tooling and maintain all image files inside the images folder of the app root.

Reduce image size.

Reducing the size of the images will result in faster downloads and reduce the time it takes to render the content on the screen. For example, Scalable Vector Graphics (SVG) images are usually smaller than Portable Network Graphics (PNG) images and scale on high resolution devices.

There are also a number of third-party tools that you can use to reduce the size of your images. The tool that you select will depend on the image type, for example:

  • imagemin: Utility to compress PNG images

  • svgomg: Utility to compress SVG images. You can use this tool online or download svgo to work with the images on your own system.

Reduce the number of roundtrips between client and server.

There are a number of techniques that you can use to reduce the number of roundtrips, and here are some examples:

  • Icon fonts

    Icon fonts are useful when your icon uses a single color.

    Oracle JET uses icon fonts, and you can see examples of them at: Icon Fonts.

    You can find utilities on the Internet such as IcoMoon that you can use to generate icon fonts.

  • Image Sprites

    An image sprite is a collection of images combined into a single image, reducing the number of server requests. You can find examples of them at http://www.w3schools.com/css/css_image_sprites.asp.

  • Lazy loading

    You can use lazy loading to defer the loading of images not in the user’s viewport. You can find many examples and utilities on the Internet that use this technique.

  • Base64 Encoding

    You can use Base64 Encoding to inline image data. They are commonly used in data Uniform Resource Indicators (URIs), and you can find additional information about them at https://developer.mozilla.org/docs/Web/HTTP/data_URIs.

For additional performance tips, see the Google Developers documentation for improving the quality of web pages, including how to audit for performance.

About Configuring the App for Oracle CDN Optimization

You can configure the Oracle JET app to minimize the network load at app startup through the use of Oracle Content Delivery Network (CDN) and the Oracle JET distributions that the CDN supports.

The local loading of the required Oracle JET libraries and modules, as configured by default when you create the app, is not recommended for a production app since it does not use Oracle CDN and requires each app running in the browser to load libraries and modules in a standalone manner. This default configuration can be used when you build and serve the app locally until you need to stage the app in a test environment to simulate network access.

To enable Oracle CDN optimization, you configure the path_mapping.json file in your app. The choices you make in the path mapping file determine the libraries and modules settings for the entire app. With this file, you will not need to edit the path URL of the required libraries and modules in any other app file. When you configure path mapping for CDN optimization, you will determine how the tooling updates the require block of the main.js file and how the app will load modules and libraries as follows:

  • If you configure path mappings to use CDN without accessing the bundles configuration, so that modules and libraries will be loaded individually from CDN, the tooling injects the URLs from the path mapping file into the requirejs block of main.js.

  • If you configure the path mappings to use CDN bundle loading, the tooling updates the index.html file to execute the bundles configuration script file (bundles-config.js) from the following script reference:

    <body>
      <script type="text/javascript" src="https://static.oracle.com/cdn/jet/16.0.0/default/js/bundles-config.js"></script>
    ..
    </body>

    Note: Starting in JET release 9.0.0, the convention of using the leading character "v" to identify the release number has changed. As the above sample shows, the release identifier is now a semver value specified as 16.0.0 with no leading character.

The bundles configuration file specifies its own require block that the app executes to load as a set of bundled modules and libraries from CDN. When you configure the app this way, the main.js is updated by the tooling to display only a URL list composed of third-party libraries. In the bundles configuration scenario, the injected require block in main.js becomes a placeholder for any app-specific libraries that you want to add to the list. Where URL duplications may occur between the require block of the bundles configuration file and the app main.js, the bundles configuration takes precedence to ensure bundle loading from CDN has the priority.

Tip:

Configuring your app to reference the bundles configuration script file on Oracle CDN is recommended because Oracle maintains the configuration for each release. By pointing your app to the current bundles configuration, you will ensure that your app runs with the latest supported library and module versions.

Configure Bundled Loading of Libraries and Modules

For Oracle CDN optimization, you may use the bundles configuration script that loads a set of bundled libraries and modules from the CDN.

To configure bundle loading of the libraries and modules using the bundles configuration script, perform the following steps.

  1. Open the path_mapping.json file in the js subfolder of your app and change the use element to cdn:
    "use": "cdn"
  2. Leave the cdns element unchanged. It should show the following as the default path definitions for Oracle JET and third-party libraries:
    "cdns": {
        "jet": {
           "prefix": "https://static.oracle.com/cdn/jet/16.0.0/default/js",
           "css": "https://static.oracle.com/cdn/jet/16.0.0/default/css",
           "config": "bundles-config.js"
        },
        "3rdparty": "https://static.oracle.com/cdn/jet/16.0.0/3rdparty"
    },
    

    Note: Starting in JET release 9.0.0, the convention of using the leading character "v" to identify the release number has changed. As the above sample shows, the release identifier is now a semver value specified as 16.0.0 with no leading character.

  3. Optionally, in the case of bundle loading, for each third-party library, update the cdn element from 3rdparty to jet. For example, this knockout library path definition shows "cdn": "jet" to prevent the knockout URL from being injected into main.js:
    "libs": {
    
      "knockout": {
        "cdn": "jet",
        "cwd": "node_modules/knockout/build/output",
       ...
     },

    Setting "cdn": "jet" for each third-party library prevents these librarys’ URL from being injected into the require block of main.js. This update is not necessary to ensure bundle loading of third-party libraries since the bundles configuration script overrides duplicate URL paths that appear in main.js.

  4. Save the file and either build or serve app to complete the bundle loading configuration.

Configure Individual Loading of Libraries and Modules

You can configure path mappings to use CDN without accessing the bundles configuration script, so that modules and libraries are loaded individually from the CDN.

To configure individual loading of the libraries and modules based on the require block of the main.js (without the use of the bundles configuration script), perform the following steps.
  1. Open the path_mapping.json file in the js subfolder of your app and change the use element to cdn.
    "use": "cdn"
  2. Edit the cdns element to remove the config element so cdns path definitions for Oracle JET and third-party libraries are formatted as following:
    "cdns": {
        "jet": "https://static.oracle.com/cdn/jet/16.0.0/default/js",
        "css": "https://static.oracle.com/cdn/jet/16.0.0/default/css",
        "3rdparty": "https://static.oracle.com/cdn/jet/16.0.0/3rdparty"
    }

    Note: Starting in JET release 9.0.0, the convention of using the leading character "v" to identify the release number has changed. As the above sample shows, the release identifier is now a semver value specified as 16.0.0 with no leading character.

  3. Save the file and either build or serve app to complete the main.js require block configuration.
When you need to make a change to the list of required libraries, for example, to specify a different release version, do not edit the main.js; edit instead the path_mapping.json file and, in the case of bundle loading, also edit the bundles-config.js URL in your app’s index.html file. You will need to rebuild the app to apply the changes. For details about the path_mapping.json file and the configuration updates performed by the tooling, see Understand the Path Mapping Script File and Configuration Options.

Understand the Path Mapping Script File and Configuration Options

When you build the application, the tooling invokes the path_mapping.json configuration file and determines the URL path for the Oracle JET modules and libraries based on the settings you configured for the use element.

The path mapping use element, set to local by default, specifies location of these require libraries:

  • Core Oracle JET libraries (appear as ojs, ojL10n, ojtranslations)

  • Third-party dependency libraries (for example, knockout, jquery, hammerjs, and other)

When you build your app, by default, Oracle JET will load the libraries from the local app as specified in the require block of the app's main.js. Each library URL is assembled from the baseUrl element and the path attribute of the lib element as specified by the path mapping file.

For example, the path mapping definition for the knockout library shows the following details.

"baseUrl": "js"
"use": "local"

"libs": {
   ...
     "path": "libs/knockout/knockout-3.x.x.debug"
}

And, after build or serve, the main.js require block contains the following URI:

/js/libs/knockout/knockout-3.x.x.debug

When configured for Oracle Content Delivery Network (CDN), the main.js require block is determined either entirely by the path mapping file local to the app or, in the case of the bundle loading optimization, partially from the path mapping file and partially from the require block of the bundles-config.js file maintained by Oracle on Oracle CDN. Path injector markers in main.js indicate where the release specific URLs appear.

CDN Scenario 1: To load libraries and modules as bundles from CDN, by default only the path mappings for third-party libraries will appear in the URL library list in the require block of main.js.

For example, the path mapping definition for the knockout library shows the following details. Note that the config attribute specifies the name of the bundles configuration script file as bundles-config.js.

"baseUrl": "js" <==ignored
"use": "cdn"

"cdns": {
    "jet": {
       "prefix": "https://static.oracle.com/cdn/jet/16.0.0/default/js",
       "css": "https://static.oracle.com/cdn/jet/16.0.0/default/css",
       "config": "bundles-config.js"
     },
    "3rdparty": "https://static.oracle.com/cdn/jet/16.0.0/3rdparty"
},

"libs": {
   ...
     "cdnPath": "knockout/knockout-3.x.x"
}

And, after build or serve, the main.js require block contains a list of third-party library URLs as a placeholder, and, as the following code snippet shows, the index.html file references the script to load libraries and modules as bundles from CDN.

<body>
  <script type="text/javascript" src="https://static.oracle.com/cdn/jet/16.0.0/default/js/bundles-config.js"></script>
..
</body>

Note that loading libraries and module as specified in the require block of the bundles-config.js file takes precedence over any duplicate libraries that may appear in the main.js require block. However, if you prefer, you can configure the third-party library path mapping so their URLs do not appear in the main.js require block. To accomplish this, set "cdn": "3rdparty" in the path_mapping.json file to show "cdn": "jet" for each third-party library path definition.

CDN Scenario 2: To load the libraries individually from CDN using the path mapping URLs to specify the location, the list of library URLs will appear entirely in the require block of main.js.

For example, the path mapping definition for the knockout library shows the following details after you edit the cdns element to remove the bundles configuration script reference.

"baseUrl": "js" <==ignored
"use": "cdn"

"cdns": {
    "jet": "https://static.oracle.com/cdn/jet/16.0.0/default/js",
    "css": "https://static.oracle.com/cdn/jet/16.0.0/default/css",
    "3rdparty": "https://static.oracle.com/cdn/jet/16.0.0/3rdparty"
}

"libs": {
   ...
     "cdnPath": "knockout/knockout-3.x.x"
}

And, after build or serve, the main.js require block contains the following URL (along with the URLs for all other base libraries and modules):

"knockout":"https://static.oracle.com/cdn/jet/16.0.0/3rdparty/knockout/knockout-3.x.x"

CDN Scenario 3: If your app needs to access libraries that reside on a non-Oracle CDN, you can update the path-mapping.js file to specify your own CDN endpoint and library definition.

Depending on whether you use the bundles configuration script, add your CDN name and endpoint URI to the cdns definition as follows.

When using the bundles configuration script to load libraries and modules:

"cdns": {
    "jet": {
      "prefix": "https://static.oracle.com/cdn/jet/16.0.0/default/js",
      "css": "https://static.oracle.com/cdn/jet/16.0.0/default/css",
      "config": "bundles-config.js"
    },
    "3rdparty": "https://static.oracle.com/cdn/jet/16.0.0/3rdparty"
    "yourCDN": "endPoint to your own CDN"
},
...

Or, when loading libraries and modules individually (not using the bundles configuration script):

"cdns": {
    "jet": "https://static.oracle.com/cdn/jet/16.0.0/default/js",
    "css": "https://static.oracle.com/cdn/jet/16.0.0/default/css",
    "3rdparty": "https://static.oracle.com/cdn/jet/16.0.0/3rdparty",
    "yourCDN": "endPoint to your own CDN"
},
...

Then, in the list of libraries, define your library entry similar to the following sample.

"yourLib": {
  "cdn": "yourCDN",
  "cwd": "node_modules/yourLib",
  "debug": {
    "src": "yourLib.js",
    "path": "libs/yourLib/yourLib.js",
    "cdnPath": "yourLib/yourLib.js"
  },
  "release": {
    "src": "yourLib.min.js",
    "path": "libs/yourLib/yourLib.min.js",
    "cdnPath": "yourLib/yourLib.min.js"
  }
},