Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Explore support for async/dynamic chunk names (POC) #9

Closed
wants to merge 1 commit into from

Conversation

addyosmani
Copy link

This plugin does a great job of preloading and prefetching resources already defined as part of your static HTML 👍 Where it doesn't quite work as well is for script chunks generated on-the-fly by Webpack as part of code-splitting. Let's say I generate a few chunks that I want to lazy-load later on in my user-journey and so I'd like to preload or prefetch them in the current view to warm up the cache.

These chunks do not have names known ahead of time can often have a build-time generated hashname, such as chunk.31132ae6680e598f8879.js.Due to the way the plugin is currently setup, it requires assets specified in the preload or prefetch arrays to match HTML tags. This means, even if you wanted to supply an array of build-time generated chunk names to the plugin, it wouldn't really work (from what I can tell) unless you first inject them into your HTML via a separate html-webpack-plugin add-on.

Ideally, there would be a clean way to say you want to preload or prefetch dynamically generated chunks and have script-ext-html-webpack-plugin handle that for you. @numical would you be up for discussing support for that, or is that a use-case this plugin doesn't really want to target?

To see how feasible this might be, I put together a POC that effectively does the following:

In your script-ext-html-webpack-config, you can now define the following field:

dynamicChunks: { 
  preload: true
}

In this simplest of forms, it will inject into the <head> any dynamic chunks extracted from the compilation step as link rel=preload elements with type as=script. To customize it to inject such chunks into the <body>, one can do:

dynamicChunks: {
  preload: true,
  position: 'body'
}

The above will also work for prefetch. For a quick demo of the output, here's a sample config running against a project where two dynamic chunks are generated. I'm specifying the defaultAttribute just to show that things should still work fine with the rest of the plugin.

  new ScriptExtHtmlWebpackPlugin({
    dynamicChunks: {
      preload: true,
      position: 'head'
    },
    defaultAttribute: 'defer'
  })

And the output:

<html>
  <head>
    <title>...</title>
  <link rel="preload" href="chunk.31132ae6680e598f8879.js" as="script"></link>
  <link rel="preload" href="chunk.d15e7fdfc91b34bb78c4.js" as="script"></link>
</head>
  <body>
    <div id="root"></div>
  <script type="text/javascript" src="/vendor.bundle.js" defer></script>
  <script type="text/javascript" src="/bundle.js" defer></script>
</body>
</html>

The current PR isn't meant to be a proposal for a final API to intro support for this feature, but it's just something to get the discussion started. I'm curious if there might be interest in supporting something like this. Happy to keep working on it or hand-off work on it if there's a better way of supporting the use-case.

Thanks! 💘

This POC adds support for preloading and prefetching JS chunks
generated as part of code-splitting. Usage is as follows:

// Preload dynamic chunks in the document <head>
dynamicChunks: {
  preload: true
}

// Prefetch chunks on the document <head>
dynamicChunks: {
  prefetch: true
}

// Preload chunks in the document <body>
dynamicChunks: {
  preload: true,
  position: ‘body’
}

same goes for prefetch.
@numical
Copy link
Owner

numical commented Jan 23, 2017

Absolutely! Many thanks for the thorough POC.
In answer:

  • it's a great use case to bring into the plugin's functionality;
  • your POC implementation seems perfectly valid;
  • a little concerned about 'all or nothing' approach to which chunks are included, so we would probably want to configure the chunkRegex used to filter;
  • not sure of the need to to specify resource hints in either head or body: why would one want to resource hints in the body?
  • the additions to the config are a little inconsistent with the existent API so we need to finesse this (not sure how yet).

I'm currently dealing with an issue in the sister plugin style-ext-html-webpack-plugin and my bandwidth/work ethic does not compare to yours...

Let me cogitate on an API proposal to bounce off you and then we can get onto writing some tests.

@numical
Copy link
Owner

numical commented Jan 31, 2017

Hiya,
Finally - here's my idea of a new API - backwardly compatible but with room to grow in the future.
As of now, each of inline, sync, async, defer, module, preload, prefetch can be passed an array of Strings and Regexs.
I propose to extend this so that each one of these options can now be passed one of

  • a String
  • a Regex
  • an array of String & Regex (as now)
  • an hash with a single property of test that can have any of the above values.

In addition, individual options may have additional properties in their hashes.
For now this would be limited to preload and prefetch which would have an additional, optional property of chunks with values of all or initial. The latter is default behaviour and what happens now. All would offer the behaviour you are looking for.
Thoughts?

@addyosmani
Copy link
Author

I propose to extend this so that each one of these options can now be passed one of

a String
a Regex
an array of String & Regex (as now)
an hash with a single property of test that can have any of the above values.

I think this would offer a sufficiently robust level of flexibility that it should capture a lot of the usecases I can think of.

For now this would be limited to preload and prefetch which would have an additional, optional property of chunks with values of all or initial

This SGTM and would solve the use case I was originally trying to solve.

@taion
Copy link

taion commented Feb 2, 2017

cc @KyleAMathews @jquense

I might be misreading what this PR does, but I'd argue that statically preloading all your async chunks is often not what you want to do when using code splitting on larger sites.

Often in these cases the more semantically correct thing is to integrate with the routing framework and pull down chunks for linked routes.

At least in cases where I've worked with this, the behavior I've reached for was at that level, and I explicitly prefer to not load chunks that the user cannot access through a single link interaction.

@KyleAMathews
Copy link

What Gatsby does is cache chunks in a service worker and for browsers w/o SWs, it pulls in chunks for every linked page which seems like a good compromise.

@KyleAMathews
Copy link

Oh it does use preload the chunks needed for each page as well.

@taion
Copy link

taion commented Feb 2, 2017

Sorry, to clarify – I'm saying that, for large SPAs, where code splitting seems most relevant to me, the preload decisions need to be made dynamically based on the rendered content (specifically where links go in most cases) rather than at build time.

@numical
Copy link
Owner

numical commented Feb 3, 2017

Sure - for many SPA's this functionality would not be appropriate. However it might be for others. Much in the spirit of the dns-prefetch this could be used when it is likely that the user will want one or more dynamic chunks. This PR will simply add another tool in the toolbox for devs.
Once I get around to coding it, that is.

@jquense
Copy link

jquense commented Feb 3, 2017

This PR will simply add another tool in the toolbox for devs.

🎉 ya definitely. Maybe the bigger question is to @addyosmani about when each strategy is appropriate. I'll admit that despite spending a lot of time around these issues. I don't always have a good sense of when preload/prefetch is a good idea vs "fetch when I ask for this page/component/foo".

@numical
Copy link
Owner

numical commented Feb 7, 2017

Progress - working implementation available on the dynamic-chunks branch.
Not sure it will work on multiple entry configurations so not yet released to master.

@numical
Copy link
Owner

numical commented Feb 8, 2017

This functionality released as v1.7.0 - do please let me know if you have any difficulty with your use cases.

@numical numical closed this Feb 8, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants