Enable dynamically loading npm dependencies
See original GitHub issueThere are cases when a dependency needs to be loaded based on some condition that is evaluated at runtime rather than based on which components are attached to the component tree. Some examples:
- A polyfill that only needs to be loaded when some specific feature is used with some specific browser.
- Generic connector functionality related to some specific feature but still not tied to any specific component, e.g. for handling generic DnD.
As long as the file can be used as-is without transpiling, minifying or bundling, then page.addJavaScript("myFile.js")
can be used for this purpose (with the file located in /src/main/webapp
or corresponding instead of /frontend
). Something more sophisticated is needed if the imported file is to be processed by webpack.
The most crude approach is some way that always includes a dependency in generated-flow-imports.js
whenever some Java dependency is on the classpath. This could be implemented e.g. by an extra parameter in @JsModule
that requires that the module is always included instead of the default that only includes it if it’s referenced. This would mean that the polyfill or DnD connector would always be loaded in all browsers if the corresponding add-on is on the classpath.
This situation can be slightly improved by only including the dependency if the code that needs it is actually referenced in the application. This could work e.g. by allowing @JsModule
also on methods. In this situation, the DnD connector would only be included if the application’s UI logic actually runs one of the methods that enable DnD functionality. For the other case, the polyfill would still have to be downloaded by all browsers.
To avoid increasing the production mode bundle size, the conditionally loaded dependency needs to be in a separate chunk. This also means that in addition to the compile-time generation of generated-flow-imports.js
, there also needs to be some logic at runtime to determine when some specific chunk should be loaded.
The original @Chunk
plan as described in #5537 would only decide which chunks to load at runtime based on on which component classes are currently attached to the UI. This is not enough to avoid loading the polyfill in all browsers. Also, the idea for a fallback chunk (https://github.com/vaadin/flow/issues/5537#issuecomment-486653467) could not be triggered by something like a @JsModule
annotated method since the runtime cannot know when the method has been invoked (without ugly hacks such as proxies or bytecode rewriting).
This means that there would have to be an explicit runtime trigger for loading a specific chunk, and also a way for an add-on to indicate that a specific dependency (or set of dependencies) should be loadable as a separate chunk instead of being included in the main chunk.
One potential approach here would be to make things work similarly to Bower mode so that the metadata from the chunking process is read by the runtime to dynamically translate the URL of an imported resource into the corresponding chunk. You would thus somehow declaratively configure a specific dependency to be included in a separate chunk, and then the runtime would take care of translating page.addJavaScript("myFile.js")
into loading that particular chunk (unless it’s already loaded).
Rather than separately configuring chunking of resources and then importing by URL, we could reuse the idea from https://github.com/vaadin/flow/issues/5094 to use a class literal as an identifier. In this way, the same mechanism would be usable both during compile time when determining how to write generated-flow-imports.js
and at runtime to actually import the resources.
Issue Analytics
- State:
- Created 4 years ago
- Reactions:4
- Comments:7 (7 by maintainers)
Top GitHub Comments
I verified that the problem with
UserItem
is indeed caused by the method reference. I created https://github.com/vaadin/flow/issues/6524 for tracking that defect.Flow 2.1 adds
Page.addDynamicImport
which will take care of waiting for the import to complete before applying changes. This means that it’s not necessary to do a separate round trip to wait for the result, but instead the button click listener in my example can be like this: