Authors note: I’m posting this proposal in the name of OpenCraft GmbH.
Proposal
I’m proposing a method to optimize the LMS (perceived) webpage loading time, by enabling asynchronous Javascript and deferring the render-blocking CSS.
Deferring the render-blocking CSS
Deferring the render-blocking CSS with <link rel="preload">
is known to be quite an effective technique for minimizing the perceived time it takes for a webpage to start rendering its content.
Enabling asynchronous Javascript
Asynchronously loading (and executing) JavaScript (with async
or defer
) is known to be an effective technique for eliminating parser-blocking JavaScript. Normally the browser would have to stop/block parsing the webpage’s HTML, and instead download and evaluate JavaScript scripts before continuing.
Based on these selected references on async
vs defer
vs “normal” loading of JavaScript files, defer
seems to be a better option than async
for this proposal:
Proof Of Concept (POC)
To keep this proposal short(ish) I’ll allow myself a few provisions about this POC and its performance analysis:
-
edx:ironwood.master
github branch will be used as the baseline, andopen-craft:petarmaric/bb-2221-lms-lazy-loading
for its POC -
focus will be given to the LMS dashboard page
-
a heavily customized LMS (non-public) theme will be used when benchmarking, as this proposal is a direct result of work previously done for an OpenCraft’s client
-
all client-identifiable information have been redacted from the Lighthouse audit reports, as the OpenCraft’s client wishes to remain unnamed
The crux of this POC is relatively simple and straightforward, as seen in this JavaScript-focused commit, as well as its CSS counterpart.
The results of LMS dashboard mini-benchmarks are quite promising - please see the attached Lighthouse audit reports (baseline, poc) for details. I’d like to point out a few keypoints WRT the POC’s (perceived) performance:
-
overall Lighthouse performance score has jumped from 13 to 62
-
First Contentful Paint time has gone down from 8 seconds to 0.8
-
all of the render-blocking resources (both CSS and JavaScript) have been eliminated
I don’t expect (nor want) the POC to be merged in its current state, I’m merely using it as a reference point when discussing the potential performance gains obtainable by implementing this proposal in full.
Potential issues
Deferring the render-blocking CSS
Although the render-blocking CSS technique has shown to be quite effective, it’s also as important to consider possible issues we may encounter:
-
it’s known to cause temporary visual degradation while the page is being loaded, the so-called flash of unstyled content. This can be resolved by employing the so-called critical CSS technique to identify the minimal subset of CSS responsible for styling the initial above-the-fold content, and inline it directly into the HTML document. I do have to warn that this needs to be done for every LMS rendered webpage - ultimately it’s an extensive, manual and error-prone process suffering from similar issues as the JavaScript solution discussed in this proposal.
-
<link rel="preload">
may not be as well supported in all modern browsers, and at the moment of this writing about 85% of global browser users can use this feature directly. There exists a JavaScript polyfill that can be used to enable this feature in the browsers that don’t yet support it natively.
Enabling asynchronous Javascript
When the POC was first implemented there were several Uncaught ReferenceError
JavaScript errors on the LMS dashboard alone, but I’ve managed to resolve them manually.
Actually using defer
blindly when downloading JavaScript files will be challenging at best, as there are quite a few of “eager” (IOW not waiting for DOM to load) inline JavaScript codes that make use of the (yet-to-be) downloaded JavaScript files, which is causing said Uncaught ReferenceError
JavaScript errors.
Resolving this would require a large enough effort of manually going through each of the LMS rendered webpages, reviewing their inline JavaScript codes to check if they’re raising Uncaught ReferenceError
JavaScript errors, and then fixing them on a case-by-case basis.
I don’t really see an automatic option to safely defer
JavaScript inclusions in a simple nor partial manner - everything affects everything and there appears to be no single point of responsibility for generating/rendering JavaScript code in HTML within the edX codebase.
Therefor, if we do decide to proceed with this we’ll need to carefully review all of the LMS’s HTML and Python files for any potential emission of JavaScript code and then find a way of “lazyfying” it.
This “lazyfying” process is simple, albeit manual. The “discovery” process of what needs to be “lazyfyed” is what’s extensive, manual and error-prone IMHO.
Verifying (automatically) that the entire process was done properly is an issue in and of itself.
Conclusion
Implementing this proposal properly and fully will definitely be challenging, however the reward may very well be worth it.
Questions
-
should this proposal be implemented into Open edX?
-
do you see any issues with this proposal?
-
are there better alternatives you have in mind?
-
does this proposal have enough impact to require an OEP?
Please let me know if you’d like me to expand with more details.