eik-lib / issues

Issues and discussions that span all of Eik
https://eik.dev
1 stars 0 forks source link

Discussion: Global library waterfall loading performance #17

Open digitalsadhu opened 2 years ago

digitalsadhu commented 2 years ago

The following outlines a typical situation when using import mapping and a related performance issue

In our project we import a dependency

// source file
// scripts.js
import '@fabric-ds/elements';

We run a build that uses an Eik plugin to map the dependency to the global library version

// after build
// dist/scripts.js
import 'https://assets.finn.no/pkg/@fabric-ds/elements/v1/index.js';

And then we publish it to Eik

// gets published to eik
https://assets.finn.no/pkg/my-package/1.0.0/scripts.js

This will then be included in the page. In the Podium world, this looks like:

// loaded in the layout with
incoming.podlets = podlets;

And produces a script file in the page body and a link preload tag in the head

// adds script tags and preload tags
<head>
    ...snip...
    <link
        rel="preload"
        as="script"
        href="https://assets.finn.no/pkg/@fabric-ds/elements/v1/index.js"
        crossorigin
    />
    ...snip...
</head>
<body>
    ...snip...
    <script type="module" src="https://assets.finn.no/pkg/@fabric-ds/elements/v1/index.js"></script>
    ...snip...
</body>

But there's an issue! scripts.js gets preloaded but https://assets.finn.no/pkg/@fabric-ds/elements/v1/index.js does not. It won't get loaded until the browser has had a chance to parse scripts.js which happens pretty late in the process. This delays the fetching from Eik by a good margin. What we want, is to have a preload for the global dependency in the document head so that by the time its needed, the script is already loaded and ready for parsing.

// Uh oh! document really should have a preload for Fabric elements to avoid perf issues! Something like:
<head>
    <link
        rel="preload"
        as="script"
        href="https://assets.finn.no/pkg/@fabric-ds/elements/v1/index.js"
        crossorigin
    />
    ...snip...
</head>

Every global lib we maintain is essentially falling into this category of issue and there are only 2 suboptimal solutions I know of currently:

  1. Manually add a preload in the page which requires knowing what's required ahead of time.
  2. Add the global dependency directly (not imported as a dependency)

How can we streamline/improve this?

digitalsadhu commented 2 years ago

(This might be more of a Podium discussion than an Eik discussion now that I think about it)

trygve-lie commented 2 years ago

This might be more of a Podium discussion than an Eik discussion now that I think about it

This is definitely an Eik feature.

I think this is something that the @eik/node-client can provide and which it is already doing to a certain degree.

Each code base which is using Eik will have a reference to the map(s) to apply to its source code through the Eik config and the @eik/node-client has a feature to load these maps from the server. This can be used and it could be as simple as follow:

const client = new EikNodeClient({
    development: false,
    loadMaps: true,
});
await client.load();

const maps = client.maps();

// Get URL to ex React lib
console.log(map[0].imports.react);

This will give us a reference to libraries being applied as mappings in the code which we can use to build a pre load tag from.

Though; this is a bit cumbersome and it also might be that the project is referencing a lot more mappings than the code base actually use. Iow; if a code base pull in a mapping which holds 20 mappings but the code base only use 1, there is a danger that we generate 19 pre load tags to libraries not in use.

So it might be that we want to add a feature to the build tool plugins which registers which mappings is actually applied and store that somewhere and we can use this to create pre load tags.