vaadin / flow

Vaadin Flow is a Java framework binding Vaadin web components to Java. This is part of Vaadin 10+.
Apache License 2.0
608 stars 166 forks source link

BootstrapHandler loads all webpack chunks even intended for dynamic loading. #10579

Open xdenser opened 3 years ago

xdenser commented 3 years ago

How to reproduce:

just use var dynamicImport = await import('dynamic-module'); in the code. And see that 'dynamic-module' is loaded immediatelly with boostrap page. Did not test it with Vaadin15+ way of boostraping. Is BootstrapHandler still supported in Vaadin19? It should be if it is public class in jar.

Legioth commented 3 years ago

How do I reproduce this? Where should I put the line of code with the dynamic import?

xdenser commented 3 years ago

in js/ts file in frontend folder.

xdenser commented 3 years ago

And a hint how to fix this:
see webpack documentation https://webpack.js.org/api/stats/ BootstrapHandler looks only in field "assetsByChunkName" but there is also "initial":boolean field in "chunks" array. If this is true then chunk should be loaded during boostrap, otherwise this is dynamic chunk and webpack should load it in runtime. Well I am not sure about this last part - how webpack will detect proper url - this needs further investigation. But as first try I would just filter out chunks that have initial == false, and rely on webpack for dynamic loading.

pleku commented 3 years ago

Is BootstrapHandler still supported in Vaadin19? It should be if it is public class in jar.

Yes, but since Vaadin 15+ the modules are injected in a different way, writing the initial chunk import to the index.html page and that chunk loads all the other chunks - there are several since even the client engine is now processed by webpack.

When I add the initial flag to our stats plugin, only one chunk, with id-0, gets the "initial": "true" property in json. HtmlWebpackPlugin injects a scriptloading this chunk to the index.html. But I see that in addition to that, the chunk-0 loads the chunks 1-3 too even though those have "initial": "false". But I'm quite sure that those would have to be loaded anyway after first chunk. And maybe this is doing what you are describing - loading non-initial dynamic chunks when needed? I have not found the place which decides what other chunks are loaded by the chunk-0, but at least it is not loading everything as there are actually more chunks.

For Flow 2.x (Vaadin 14 series), I think there are only two chunks - "the optimized everything that is used chunk" and the "fallback chunk". I can edit the code to make it so that the initial property is output to stats.json and then it is considered when adding the chunks to the bootstrap page, but I'm not sure if it really makes a difference or will everything be still to the same chunk - unless we add support to 14-series for splitting the bundle further. I doubt that it would be a priority - but I can help you get forward in trying that if you want to @xdenser

xdenser commented 3 years ago

OK I will try to create reduced example. I just want to load big library only when needed. In my case this is ace editor - which otherwise would add 500Kb for initial load and is only used in some rare scenarios in our application. But just for example we, of course, do not need ace editor. Some alert() in dynamically imported module will be enough.

To have impression how it looks like: my code is inspired by this addon https://vaadin.com/directory/component/ace/overview but this one does not fit our needs and actually created for Vaadin 14
But ace is loaded similar way like here : https://github.com/F0rce/lit-ace/blob/c6e8d5de7b72d72bcfca2491e4d7e899f4f8d788/lit-ace.js#L152 but in our case without this base path thing( we refer to 'ace-builds' as normal npm module) - and by this trick imho author of the addon tries to workaround this bug of initial loading - probably webpack just ignores this import - i did not try to use this addon in our code.

But again it is better to create reduced example. I have more difficulties how to simulate our bootstrap there. The example itself is rather simple.

xdenser commented 3 years ago

Ok here is example. run, Open About view and press the button. And actually dynamic module loading works correctly unless webpack chunk name directive is used. See frontend/dynamicModuleLoader.ts line 11 my-app.zip w/o directive the js file is loaded when you press button, with directive it is loaded (but not executed) at boostrap.