Skip to content

[web] Optimize network requests on page load #118115

@mdebbar

Description

@mdebbar

In this issue, I wanted to analyze the network activity that happens during page load of a sample flutter web app.

Problem

image

Bigger apps typically have more fonts and/or assets (more requests), but the basic dependency tree between these requests looks something like this:

                                          +--> canvaskit.js --> canvaskit.wasm
                                          |
index.html -> flutter.js -> main.dart.js -+                       +--> MaterialIcons-Regular.otf
                                          |                       |
                                          +--> FontManifest.json -+--> CupertinoIcons.ttf
                                                                  |
                                                                  +--> Roboto.ttf

This tree is not ideal, for 2 main reasons:

  1. We are loading too many things for a basic app!
  2. The tree is narrow and deep, i.e. leaf nodes have a long chain of dependencies before they can be loaded.

Better?

A better tree should be wider and shallower to get more parallelization and fewer dependencies:

                         +--> main.dart.js ---> canvaskit.js
                         |
-------------------\     +--> canvaskit.wasm
index.html          \    |
(flutter.js)         +---+--> MaterialIcons-Regular.otf
(FontManifest.json) /    |
-------------------/     +--> CupertinoIcons.ttf
                         |
                         +--> Roboto.ttf

How?

The idea can be split into 2 parts:

1. Inlining

There's no reason to load flutter.js and FontManifest.json (and potentially others) from the network. They could be inlined into index.html so that they are available immediately and don't require an extra hop to the server.

Flutter generates the index.html file but we don't own it anymore after creation. The app developer owns the file and can change it as they see fit. This means we can't make any assumptions about the structure of the file, and we can't force-inline anything. The flutter tools should support inlining but the user should have full control.

One way to achieve this is to use placeholders, similar to $FLUTTER_BASE_HREF:

<!--
If you are serving your web app in a path other than the root, change the
href value below to reflect the base path you are serving from.
The path provided below has to start and end with a slash "/" in order for
it to work correctly.
For more details:
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
This is a placeholder for base href that will be replaced by the value of
the `--base-href` argument provided to `flutter build`.
-->
<base href="$FLUTTER_BASE_HREF">

We can define a set of placeholders that the user can put in. The flutter tool substitutes the placeholders at build-time. The placeholders may look something like this:

<script>$FLUTTER_JS_SCRIPT</script>

They can be placed anywhere the user chooses. E.g:

<script>
  const fontManifest = $$FLUTTER_FONT_MANIFEST; // This will be replaced with the actual json.

  _flutter.loader.loadEntrypoint({
    fontManifest: fontManifest,
    serviceWorker: {
      serviceWorkerVersion: serviceWorkerVersion,
    },
    onEntrypointLoaded: async function(engineInitializer) {
      const appRunner = await engineInitializer.initializeEngine({
        fontManifest: fontManifest,
      });
      appRunner.runApp();
    }
  });
</script>

2. Auto prefetching

Since flutter.js is now inlined in index.html, it runs immediately. It can fire requests for main.dart.js and canvaskit.wasm in parallel.

At the same time, it can also prefetch all the needed fonts since it has immediate access to the inlined FontManifest.json.

This ensures maximum parallelization. All the requests are sent immediately as index.html loads.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work listc: proposalA detailed proposal for a change to Flutterengineflutter/engine related. See also e: labels.platform-webWeb applications specificallyteam-webOwned by Web platform teamtriaged-webTriaged by Web platform team

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions