I'd like to suggest new way of approaching minification and concatenation of 
our JS code.

Currently, our strategy at build time is to gather all synchronous <scripts> in 
./{appname}/index.html and jsmin them into a single "gaia_build_index.js", then 
gather all deferred <scripts> and jsmin them into a single 
"gaia_build_defer_index.js". In result instead of:

  <script src="/js/endpoint.js"></script>
  <script src="/js/config.js"></script>
  <script src="/js/app.js"></script>

  <script defer src="/components/bridge/bridge.js"></script>
  <script defer src="/shared/js/intl/l20n-service.js"></script>
  <script defer src="/shared/js/intl/l20n-client.js"></script>
  <script defer src="/shared/js/intl_helper.js"></script>
  <script defer src="/components/gaia-header/dist/gaia-header.js"></script>
  <script defer src="/components/gaia-toolbar/gaia-toolbar.js"></script>
  <script defer src="/js/db.js"></script>
  <script defer src="/shared/js/lazy_loader.js"></script>
  <!-- to be lazy loaded:
  <script defer src="/js/async_db.js"></script>
  <script defer src="/js/thumbnails.js"></script>
  <script defer src="/js/mediadb.js"></script>
  <script defer src="/elements/music-overlay.js"></script>
  <script defer src="/elements/music-scan-progress.js"></script>
  <script defer src="/elements/music-tab-bar.js"></script>
  <script defer src="/elements/music-view-stack.js"></script>
  -->


we have this:

<script src="./gaia_build_index.js"></script>
<script src="./gaia_build_defer_index.js"></script>

and then some LazyLoader is loading the remaining files one by one.


That's not a bad strategy, but it means that we have a lot of JS code that is 
unnecessary during app startup that has to be loaded/parsed at the same time as 
the code required for startup.
That leads us to use a "third stage" code that is commented out in <head> and 
loaded using LazyLoader class inside JS.

=============== Proposal ==================

I'd like to suggest that we tie our JS code bundling to our bootstrap stages[0].

In that world, we assign a role="" to each <script> tag. That role allows us to 
group scripts that are needed together. It also allows us to bundle the 
lazyloaded groups of scripts and remove the need for LazyLoader completely.

Here's an example of how it might work (details to be decided):


  <script role="navigationLoaded" src="/js/endpoint.js"></script>
  <script role="navigationLoaded" src="/js/config.js"></script>
  <script role="navigationLoaded" src="/js/app.js"></script>

  <script role="visuallyLoaded" defer 
src="/components/bridge/bridge.js"></script>
  <script role="visuallyLoaded" defer 
src="/shared/js/intl/l20n-service.js"></script>
  <script role="visuallyLoaded" defer 
src="/shared/js/intl/l20n-client.js"></script>
  <script role="visuallyLoaded" defer src="/shared/js/intl_helper.js"></script>
  <script role="visuallyLoaded" defer 
src="/components/gaia-header/dist/gaia-header.js"></script>
  <script role="visuallyLoaded" defer 
src="/components/gaia-toolbar/gaia-toolbar.js"></script>
  <script role="visuallyLoaded" defer src="/js/db.js"></script>
  <!-- to be lazy loaded:
  <script role="editView" src="/js/async_db.js"></script>
  <script role="editView" src="/js/thumbnails.js"></script>
  <script role="editView" src="/js/mediadb.js"></script>
  <script role="settingsView" src="/elements/music-overlay.js"></script>
  <script role="settingsView" src="/elements/music-scan-progress.js"></script>
  <script role="settingsView" src="/elements/music-tab-bar.js"></script>
  <script role="albumView" src="/elements/music-view-stack.js"></script>
  -->

results in:

<script role="navigationLoaded" 
src="./js-opt/index_navigationLoaded.js"></script>
<script role="visuallyLoaded" defer 
src="./js-opt/index_visuallyLoaded.js"></script>
<script role="editView" data-lazy-src="./js-opt/index_editView.js"></script>
<script role="settingsView" 
data-lazy-src="./js-opt/index_settingsView.js"></script>
<script role="albumView" data-lazy-src="./js-opt/index_albymView.js"></script>

And then the only thing you need in your code is:

scriptElement.addEventListener('load', () => {
  console.log('My lazy loaded JS view is ready!');
});

if (scriptElement.dataset.lazySrc) {
  scriptElement.setAttribute('src', scriptElement.dataset.lazySrc);
  scriptElement.dataset.lazySrc = undefined;
}

or if we maintain some LazyLoader:

LazyLoader.load(['settings', 'editView']).then();

That enables pretty flexible per-app decisions on which scripts are loaded with 
each group, and how is that group loaded (maybe navigationLoaded should be 
deferred? Maybe it should be synchronous? maybe it should be async?).

You can go more granular, and have separate script group for 
navigationInteractive, or, if you don't need to alter HTML at all, only have 
navigationInteractive group for binding chrome to your glue code.

It would be easy to adjust webapp-optimize to handle that, and would allow us 
to load the right JS at the right time. It also degrades gracefully to work the 
way it does right now.

Thoughts? Opinions?

zb.
p.s.

1) We may still need some lazy_loader.js which would take the scripts with a 
given role from head comments and load them for non optimized scenario. But in 
the production code we could just remove it.

2) There may be lazy loaded scripts needed by multiple lazyloaded groups. In 
those cases we could separate them into their own group the way we would with 
shared CSS or L10n resources. In the worst case, each lazy loaded script would 
have its own group which is basically what we do now.

[0] 
https://developer.mozilla.org/en-US/Apps/Build/Performance/Firefox_OS_app_responsiveness_guidelines
_______________________________________________
dev-fxos mailing list
[email protected]
https://lists.mozilla.org/listinfo/dev-fxos

Reply via email to