ES module preloading and integrity

  • ES module preloading and integrity
  • Module optimization in production environment
  • Module preloading
  • Module preloading examples:
  • The Polyfilling module is preloaded
  • Integrity limit
  • Call to action
  • JSPM Generator – Module preloaded Generator
  • reference
  • Social Links:

Module optimization in production environment

When using ES modules in a production environment, there are currently two major performance optimizations to apply – code splitting and preloading.

Code splitting optimizations can be used for native ES modules in packers such as EsBuild or RollupJS. Code splitting ensures that for any two modules that are always loaded together, they will always be inlined into the same module file as block modules for network optimization (or even, if possible, into the entry module itself).

Then preloading solves the problem of modules being loaded in reference order in module dependency diagrams – modules are executed only after each module in the static import diagram has been loaded, and modules are loaded only after their parent module has been loaded. Reference: execution order of modules before and after introduction of top-level await in ESM of practical inquiry

Module preloading

The ES module is preloaded in the browser as follows:

<link rel="modulepreload" href="..."/> 

Copy the code

There was a great article about it in the Google Developers 2017 update when it was first released in Chrome.

It is recommended to inject the ModulePreload tag as far as possible for all deep dependencies in order to completely eliminate the delayed cost of module loading, which is also a major benefit of static preloading.

Another major benefit of using ModulePreload is that it is currently the only mechanism that supports the complete integrity of all loaded modules using the “integrity” attribute. (I don’t understand, wait for me to find the information)

For example, app.js loads dependency. Js loads library.js:

<link rel="modulepreload" href="/src/app.js" integrity="sha384-Oe38ELlp8iio2hRyQiz2P4Drqc+ztA7jb7lONj7H3Cq+W88bloPxoZzuk6bHBHZv"/>



<link rel="modulepreload" href="/src/dependency.js" integrity="sha384-kjKb2aJJUT956WSU7Z0EF9DZyHy9gdvPOvIWbcEGATXKYxJfkEVOcuP1q20GT2LO"/>



<link rel="modulepreload" href="/src/library.js" integrity="sha384-Fwh0dF5ROSVrdd/vJOvq0pT4R6RLZOOvf6k+ijkAyUtwCP7B0E3qHy8wbot/ivfO"/>



<script type="module" src="/src/app.js"></script>

Copy the code

Module lazy loading is eliminated because preloading causes app.js, dependency. Js, and library.js to be loaded immediately in parallel [and we can fully protect the module execution environment with the integrity of all scripts]. (I don’t understand, wait for me to find the information)

Module preloading examples:

Code: index. HTML:


       

<html lang="en">

<head>

    <meta charset="UTF-8">

    <title>Document</title>

    <! -- <link rel="modulepreload" href="./index.js"/>

    <link rel="modulepreload" href="./init.js"/>

    <link rel="modulepreload" href="./a.js"/>

    <link rel="modulepreload" href="./b.js"/> -->


</head>

<body>

    <script src="./index.js" type="module"></script>

</body>

</html>

Copy the code

index.js:

import './init.js'

import {a, Person, obj, b} from './a.js'



console.log(a, Person, obj, b)





console.log('index')

Copy the code

init.js:

import './b.js'



console.log('init')



export let str = 'init'



const a = 1



export {a}

Copy the code

a.js:

import * as bb from "./b.js"



console.log('b:', bb)



export let a = 1



export function Person({



}



export const obj = {

    name"sss"

}



let b = 1



export {b}

Copy the code

b.js:

export let a = 1



export function Person({



}



export const obj = {

    name"sss"

}



let b = 1



export {b}



console.log('b')

Copy the code

No module preloading:

Modules are loaded serially, in the order in which they are resolved

Preloading with modules :(remove comments on index.html code)

All modules are loaded in parallel (loading time is significantly shortened by 200ms)

However, if the project has many modules, it is not a good idea to manually add them one by one, so it is best to get the entire module dependency diagram first and then script the module preloaded tags to the page automatically.

But then again, if the entire module dependency graph is available, then the entire module dependency is already loading. Refer to ES Module and analyze the principle

The Polyfilling module is preloaded

One problem with this feature is that it is currently only implemented in the Chromium browser, but polyfill can be built using the following code:

<script>

  function processPreload ({

    const fetchOpts = {};

    if (script.integrity)

      fetchOpts.integrity = script.integrity;

    if (script.referrerpolicy)

      fetchOpts.referrerPolicy = script.referrerpolicy;

    if (script.crossorigin === 'use-credentials')

      fetchOpts.credentials = 'include';

    else if (script.crossorigin === 'anonymous')

      fetchOpts.credentials = 'omit';

    else

      fetchOpts.credentials = 'same-origin';

    fetch(link.href, fetchOpts);

  }

  for (const link of document.querySelectorAll('link[rel=modulepreload]'))

    processPreload(link);

</script>

Copy the code

Also, if we wanted to add dynamic preloading support to this polyfill, we could use mutation observer:

new MutationObserver(mutations= > {

  for (const mutation of mutations) {

    if(mutation.type ! = ='childList'continue;

    for (const node of mutation.addedNodes) {

      if (node.tagName === 'LINK' && node.rel === 'modulepreload')

        processPreload(node);

    }

  }

}).observe(document, { childListtrue.subtreetrue });

Copy the code

This does not fill depth dependent on preloading, but covers most use cases and enables “integrity” checking in all module browsers by enabling internal integrity mapping. (I don’t understand, wait for me to find the information)

In this way, we can get full preloading and integrity support in all browsers’ production module environments.

The polyfill above is included in the latest 0.12.1 version of ES Module Shims, which provides a combination of polyfills for various ES Module functions, especially for import maps.

Integrity limit

One major problem with using modulePreload as the primary integrity method is that there is no easy way to provide pre-integrity for lazy-loaded modules without performing preloading immediately. For today’s production way of working, the best approach is to build a custom dynamic import wrapper that delays injection preloading until the dynamic import is triggered.

I would be surprised if this approach was adopted, even though it would be a considerable amount of work and could lead to considerable disruption. However, module integrity is an absolutely key feature of CDN using ES module.

In the future, possible specifications related to module preloading and integrity include:

  • Import assertions: Import ‘mod’ assert {integrity: ‘… ‘} syntax, but not yet specified or implemented for import assertions.

    Unfortunately, this feature has the following problems: It removes the main performance advantage of importing maps, which allows all modules in the graph to have a longer expiration time to be cached independently. So while it’s useful for some specific situations, it’s a step backward as a general solution to this problem.

  • Import Map Integrity: I have suggested an Integrity attribute specification for imported maps that allows them to act as choreography points here. The difficulty here is getting support from browsers, but so far it hasn’t been successful.

  • Renamed: This is an experimental performance method that uses the current Proposal Doc and Chrome CL to allow specifying some information about script load conditions for finer grained load optimization. Unfortunately, there are currently no plans to support this feature for preloading, so this means that it does not solve the problem of depth map content integrity for lazy module loading in its current form.

  • Lazy Preloads: Another design might have a property on the preload tag indicating that it should not be preloaded, but its integrity value should still apply. I suggested this on WebAppSec, but the combination of preloading and completeness seems a bit confusing here.

  • Web Bundles: From the FAQ, it seems that the current integrity state of Web Bundles is considered a follow-on proposal due to the high bandwidth cost of hashing validation.

Perhaps the concept of generic module integrity can be folded into a centralized Web integrity checklist, possibly combined with other permissions/security features in the security checklist. Instead, validation can benefit from a wide range of methods we use to optimize integrity, including Merkle trees for chunking boundary optimization, and even more exotic things.

Call to action

The basic principle that people online should be able to access sites on the Internet and that all code executed is hashed for integrity is an absolutely fundamental security attribute.

Doing this in the best way requires some new work, and we need to actively work with browser vendors and standards bodies to ensure that this security attribute can be fully enabled for future Web platforms without friction.

JSPM Generator – Module preloaded Generator

JSPM Generator is a low-level tool that I’ve been working on to generate import maps for module CDNS or local package paths. The latest version now includes support for static tracking of module dependencies, allowing these preloaded labels to be built for module diagrams. Work is ongoing to improve these generator apis and workflows.

Jspm.io’s online generator also includes support for these features by toggling the “preload” and “Integrity” boxes in the upper right corner of the application.

Here is a demonstration of the actual operation, preloading for individual CDN dependency switches:

reference

  • ES Module Preloading & Integrity

Social Links:

Wechat official account: Joshua, upperclassman in front