Skip to content

Prefetch and preload from entry chunk. - #7444

Merged
sokra merged 17 commits into
webpack:masterfrom
MLoughry:prefetch-from-entry
Jun 4, 2018
Merged

sokra merged 17 commits into
webpack:masterfrom
MLoughry:prefetch-from-entry

Conversation

@MLoughry

@MLoughry MLoughry commented May 29, 2018

Copy link
Copy Markdown
Contributor

Following up on @sokra's work in #7056, this change addresses #7084 to have webpack prefetch and preload designated chunks from the entry chunk. It adds a function (with its name configurable by output.entryPrefetchFunction) that can be called by consuming code to trigger the prefetch/preload.

  • Add tests

What kind of change does this PR introduce?

I would call it a bugfix; @sokra may disagree.

Did you add tests for your changes?

I have not yet added tests; I will work on them, but wanted to get feedback on the design first.

Does this PR introduce a breaking change?

No

What needs to be documented once your changes are merged?

Any documentation (which I could not locate in this repro) that references webpackPreload or webpackPrefetch should no longer add the caveat about not working from the entry chunk.

Additionally, with my second commit, need to document the output.entryPrefetchFunction option.

Following up on @sokra's work in webpack#7056, this change addresses webpack#7084 to have webpack prefetch and preload designated chunks from the entry chunk.
@jsf-clabot

jsf-clabot commented May 29, 2018

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@webpack-bot

Copy link
Copy Markdown
Contributor

For maintainers only:

  • This need to be documented (issue in webpack/webpack.js.org will be filed when merged)

@MLoughry

Copy link
Copy Markdown
Contributor Author

I'm having some trouble adding tests, since the code executes during bootstrap. The tests in #7056 work because the FakeDocument is installed before the associated code that adds prefetch links executes.

Rather than calling the prefetch function on its own during bootstrap, this change exposes the function as a function that can be called from the compiled code.
@MLoughry

Copy link
Copy Markdown
Contributor Author

Even changing the code to not automatically call the prefetch itself, I can't access the function from the prefetch-preload tests, since it clears out the window object before the test.

As far as I can find, none of the existing tests test anything like this (a publicly exposed function), so I don't have good examples to base my tests off of.

@sokra

sokra commented May 30, 2018

Copy link
Copy Markdown
Member

Ok I see what you want to do. Let's solve this a bit different. I'm not happy with exposing an extra function, but I would be ok to enable triggering prefetch on bootstrap. preload has a different meaning and triggering it on bootstrap would change the semantics (it only makes sense as <link> tag).

We can add this behavior in a back-compat way without extra option.

  • verify that adding prefetch <link> twice doesn't cause two requests
    • if that doesn't work: read existing <link> tags on bootstrap to fill up chunkPrefetchMap
  • move prefetching code from requireEnsure into webpackJsonp
    • This will also trigger prefetching for initial chunks (without runtime)
    • Relevant
  • after the startup code trigger the prefetching of chunks for the entry chunk.
    • This will trigger prefetching for the entry chunk
  • getChildIdsByOrdersMap only takes async chunks. You can add an option to include initial chunks too.
  • Take a not of this line. prefetching is only triggered for the last chunk of a ChunkGroup.
    • This avoid over-fetching for shared chunks. It also means that a runtimeChunk: "single" will not trigger any prefetching.

Why is this backward-compatible?

  • Using it in the existing way with <link> tags added for prefetch will continue to work.
  • When not adding <link> tags we now added the feature to trigger prefetching. This was also the users intend as she/he added webpackPrefetch hints.
  • bundle size doesn't increase significantly.

@MLoughry

MLoughry commented May 30, 2018

Copy link
Copy Markdown
Contributor Author

@sokra I updated the change to match what (I interpreted) you suggested. I even added a small optimization to split the chunkPrefetchMap among all the chunks, rather than loading it all upfront in the entry chunk.

However, I'm still running into test issues, because the bootstrap code is called before the prefetch-preload, hash-length, or commons-chunk-plugin test cases can install their FakeDocument instances.

@sokra

sokra commented Jun 1, 2018

Copy link
Copy Markdown
Member

I even added a small optimization to split the chunkPrefetchMap among all the chunks, rather than loading it all upfront in the entry chunk.

Good idea.

However, I'm still running into test issues, because the bootstrap code is called before the prefetch-preload, hash-length, or commons-chunk-plugin test cases can install their FakeDocument instances.

We could move this into the ConfigTestCases.test.js file.

Comment thread lib/web/JsonpMainTemplatePlugin.js Outdated
`webpackJsonpCallback([${JSON.stringify(
chunk.ids
)}, {}, null, ${JSON.stringify(
chunk.getChildIdsByOrders().prefetch

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check of chunk.getChildIdsByOrders().prefetch is not empty

Comment thread lib/web/JsonpChunkTemplatePlugin.js Outdated
if (entries.length > 0) {
source.add(`,${JSON.stringify(entries)}`);
} else {
source.add(`,null`);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be omitted when chunk.getChildIdsByOrders().prefetch is empty.

I don't want to increase chunk size if prefetching isn't used.

Also use 0 instead of null, that's shorter. (a bit)

Comment thread lib/web/JsonpMainTemplatePlugin.js Outdated
"var chunkIds = data[0];",
"var moreModules = data[1];",
withDefer ? "var executeModules = data[2];" : "",
"var prefetchChunks = data[3] || []",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to withDefer check if we need this code.

return source;
}
);
mainTemplate.hooks.beforeStartup.tap(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trigger prefetching after the startup code runs. This would omit prefetching when import() is used in initialization code.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where? There's no afterStartup hook, and startup includes a return statement that prevents later code from executing.

Asset Size Chunks Chunk Names
9ac13fb7087e9ff1b93e.js 1.05 KiB 0 [emitted]
f2e891598128a57b072c.js 11 KiB 1 [emitted]
f2e891598128a57b072c.js 11.6 KiB 1 [emitted]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bundle size of most of these test cases shouldn't increase, since prefetching isn't used. We don't want to users to pay for not using this feature.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Following most of the other comments, I was able to eliminate most of the differences. However, to not emit blank lines when not using prefetching, I followed a different pattern than the rest of the code:

"var moreModules = data[1];",
withDefer ? "var executeModules = data[2];" : "",
...(withPrefetch ? ["var prefetchChunks = data[3] || []"] : []),
'// add "moreModules" to the modules object,',

@sokra

sokra commented Jun 1, 2018

Copy link
Copy Markdown
Member

PR looks good

MLoughry added 2 commits June 1, 2018 09:35
This reverts commit 23ab98d.
- [x] Don't emit prefetch code if not needed
- [x] Only affect StatsTestCase dealing with prefetching
- [x] Fix existing prefetch test case
- [ ] Trigger prefetch after startup code runs
- [ ] Add new test(s)
@webpack-bot

Copy link
Copy Markdown
Contributor

@MLoughry Thanks for your update.

I labeled the Pull Request so reviewers will review it again.

@sokra Please review the new changes.

@MLoughry

MLoughry commented Jun 1, 2018

Copy link
Copy Markdown
Contributor Author

Trying to add webpackPrefetch: true to the existing import in the prefetch-preload test case still results in the issue where the document isn't found. I tried moving the beforeEach/afterEach instalaation of the FakeDocument to ConfigTestsCases, but that just caused the various test cases to be unable to find document. So, I still don't have new test cases.

I was also unclear on your statement about:

Trigger prefetching after the startup code runs. This would omit prefetching when import() is used in initialization code.

as I replied to above.

Lastly, I'm headed out on vacation for the next week, so I may not be able to pick this back up until the week after next.

This makes more sense as startup could take a while
and we can use the time to fetch some resources
@sokra
sokra force-pushed the prefetch-from-entry branch from 85d5228 to c77ec39 Compare June 4, 2018 16:10
@webpack-bot

Copy link
Copy Markdown
Contributor

Thank you for your pull request! The most important CI builds succeeded, we’ll review the pull request soon.

@sokra
sokra merged commit bc6b5b0 into webpack:master Jun 4, 2018
@sokra

sokra commented Jun 4, 2018

Copy link
Copy Markdown
Member

Thanks

@webpack-bot

Copy link
Copy Markdown
Contributor

I've created an issue to document this in webpack/webpack.js.org.

@theKashey

Copy link
Copy Markdown

Could this feature be used, or better not, as long as it still undocumented?

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