Last active
August 28, 2017 13:22
-
-
Save niieani/ce388f9fcf668da4040b4cbec09f4351 to your computer and use it in GitHub Desktop.
MoveChunkExecutionWebpackPlugin
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* eslint-disable no-param-reassign */ | |
/* | |
You should inline the chunk that this outputs into your HTML | |
then execute window.onReady() when all chunks are loaded, e.g. | |
<script async onload="executeWhenAllChunksLoaded()" src="..."> | |
where 'executeWhenAllChunksLoaded' runs 'onReady()' once all chunks have loaded | |
You may use HtmlWebpackPlugin in combination with ScriptExtHtmlWebpackPlugin to do the inlining. | |
*/ | |
export default class MoveChunkExecutionPlugin { | |
static NextIdent = 0 | |
ident = `${__filename}/${MoveChunkExecutionPlugin.NextIdent++}` | |
constructor(toChunkName) { | |
this.toChunkName = toChunkName | |
this.toChunkFileName = `${toChunkName}.js` | |
} | |
apply(compiler) { | |
const self = this | |
compiler.plugin('this-compilation', (compilation) => { | |
compilation.plugin('before-chunk-assets' /* ['before-chunk-ids'] */, function apply() { | |
// extract only once | |
if (compilation[self.ident]) return | |
compilation[self.ident] = true | |
const {chunks} = this | |
const entryChunks = chunks.filter((chunk) => chunk.hasEntryModule()) | |
const entryModuleIds = entryChunks.map((chunk) => { | |
const {id} = chunk.entryModule | |
// remove entryModule | |
chunk.entryModule = undefined | |
// but lie about having one (for JsonpMainTemplatePlugin's entryPointInChildren()): | |
chunk.hasEntryModule = () => true | |
return id | |
}) | |
const onReady = `window.onReady = function(){ webpackJsonp([], {}, ${JSON.stringify(entryModuleIds)}); }` | |
compilation.assets[self.toChunkFileName] = { | |
source: () => onReady, | |
size: () => onReady.length, | |
} | |
}) | |
// alter list of chunks in HtmlWebpackPlugin: | |
compilation.plugin('html-webpack-plugin-alter-chunks', (chunks, {plugin} = {}) => { | |
const assets = compilation.assets | |
const pseudoChunk = { | |
names: [this.toChunkName], | |
files: [this.toChunkFileName], | |
size: assets[this.toChunkFileName].size(), | |
hash: undefined, | |
} | |
return [ | |
...chunks.filter((chunk) => chunk.entry), | |
pseudoChunk, | |
...chunks.filter((chunk) => !chunk.entry), | |
] | |
}) | |
}) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
We’ve tested
<script async src=...>
execution for entry chunks, but it turned out the performance is actually a tiny bit worse (at least in Chrome) than when using non-async script tags. This is the plugin I wrote to support this scenario.To run this, we’d add ‘onload’ hooks for each async script to count how many we have loaded and run: ‘window.onReady()’ as soon as all chunks were ready.
We think the reason for lower performance is that Chrome gives a lower priority to async scripts, while non-async
<script src=...>
still download and parse in parallel. So there seems to be no real benefit to using the ‘async’ option when blocking the execution until all entry chunks are ready.Another weird thing we noticed with ‘async’ entry chunks is that even with HTTP/2.0 Chrome seems to download only 6 chunks simultaneously and only start downloading more after the first batch is ready.