In Jira, Web Resource Manager (WRM) handles the preparation of necessary JavaScript and CSS resources for particular pages to ensure apps function correctly. When creating an app, you define its dependencies in <web-resource>
and set the context where your app will run in Jira. According to this definition, WRM creates batch.js
and batch.css
files by collating all required dependencies for all apps that are running in that particular context.
Depending on the context registry and how the dependencies are defined, the batch files may end up with very large file sizes. Simply downloading and loading the batch file might take seconds, which results in very low page load performance.
There are some steps you can follow to improve page load performance:
Code splitting
Using Atlassian Web-Resource webpack Plugin in your app allows you to easily reuse libraries provided by Jira. This plugin generates <web-resource>
definitions for Webpack code chunks, helping to reduce batch file sizes.
Deferring script loading
Not all resources in apps need to be part of the render-blocking version of the batch script. In many cases, apps can load after the critical resources for the page have been loaded lazily. This improves the performance and perceived loading speed for the users.
WRM supports loading phases for loading web resources starting from version 9.0. These phases are listed below.
Execution order | Resource phase | Description |
---|---|---|
1 | Sync phase |
Code is put inline and runs immediately when the browser parses the page. A product uses this phase to inject any bootstrap code. |
2 | Require phase |
Code loads and runs immediately when the browser parses the <script> or <link> tag. |
3 | Defer phase |
Code loads asynchronously and runs in order when the page has finished parsing. |
4 | Interaction phase |
Code loads and runs after the require and defer phase scripts as well as DomContentLoaded (DCL) handlers have been completed. |
To use loading phases in your apps, complete the following updates (if you haven’t already):
- Use WRM 5 API or a newer version (the latest version is 8 at the time this document was created) to defer the scripts your plugin requires on the pages with deferred
.js
resources. - Use inline scripts (no
src
attribute) withtype="module"
attribute. - Postpone the execution of the script until
DOMContentLoaded
or later event occurs. - It’s even more effective to use the interaction phase when the logic can be initialized after the
DOMContentLoaded
event.
For a general phased loading approach, only load the application init scripts in the require
phase. Then, prefer async loading of your critical resources in defer
phase. Put all remaining code to interaction
page to lazy load them until they’re requested with the user’s interaction.
To execute
.js
resources on pages with deferred scripts, use methodscom.atlassian.webresource.api.assembler.RequiredResources
that acceptcom.atlassian.webresource.api.assembler.resource.ResourcePhase
.To get the
ResourcePhase
that’s currently used on the page, usecom.atlassian.jira.web.RequestAssetPhaseStore
so that the proper default resource phase will be used for a given page.For these APIs usage examples, check the
com.atlassian.jira.web.action.Dashboard#doExecute
.On the client-side, you can use checkpoints to execute your front-end code in the proper resource phrase:
DEFER
phase resources are available. To execute logic once defer checkpoint is hit, useresourcePhaseCheckpoint.defer.then(deferPhaseCallback);
.- During the
INTERACTION
resource phase. To execute logic once interaction checkpoint is hit, use:resourcePhaseCheckpoint.interaction.then(interactionPhaseCallback);
.The checkpoint promises are provided by Jira pages by default. However, if your newly created page doesn’t have them, you can add these checkpoints as follows:
requireWebResource(ResourcePhase.INLINE, "jira.webresources:resource-phase-checkpoint-init");
. This should be added at the top of the page so that it starts executing as soon as possible.requireWebResource(ResourcePhase.DEFER, "jira.webresources:resource-phase-checkpoint-hit");
. This should be added at the bottom of the page so that it starts executing as late as possible.
This phased usage will also reduce the initial batch file sizes since majority of your code will go to the defer
or async
loading stage. As a result, the render-blocking execution time may become negligible..
Compressing files
With the changes mentioned above, the batch file sizes can already be reduced. Currently, by default, the batch files we analyzed from various instances are served with Gzip compression, reducing their size with a range from 2:1
to 7:1
. Reducing the size will improve both download and script execution times. Furthermore, we can explore Brotli compression, which may further reduce file sizes by an additional 10
to 20%
with respect to Gzip.
More about compression and Brotli
Further optimisation for custom apps
Instead of using web resource context, the custom apps can require a web resource key and run their code through defined web panels.
In this scenario, the steps could include creating a soy
file, registering the template file in the appropriate panel location, and calling the needed webResourceManager_requireResource
inside this soy
file.
Some of the panels you can use are:
atl.header
that adds code at the beginning of the headeratl.header.after.scripts
that adds code Jira/WRM-created scripts, at the end of the headeratl.footer
that adds code to the footer
If all the above optimization actions aren’t enough, try serving essential resources for custom apps from a CDN as an alternative approach.
You can achieve this programmatically by using downloadable plugin resources. Instead of adding your resources to batch.js and increasing the batch file, you can serve those static resources separately.
<!-- Resources may contain arbitrary key/value pairs -->
<resource type="download" name="custom.app.entry.js" location="com/custom-app/app/entry.js">
<property key="content-type" value="application/js"/>
</resource>
By this, some common files shared across custom apps can be shared locally or from a CDN.
Additionally, if you wish to add javascript to every page, you can add your script tag code with proper CDN URL to following files
stylesheettag.jsp
<atlassian-jira>/includes/decorators/header.jsp
<atlassian-jira>/atlassian-jira/includes/decorators/aui-layout/header.jsp
<script src="<path-to-cdn-file>" defer />
This makes your JS
and CSS
resources available for every page and reduces the need for re-downloading similar resources from different batch files on different pages. Please make sure to use defer
or async
when using CDN for common files. This will prevent render-blocking script executions.