Open DorianGrey opened 8 years ago
Hey @DorianGrey,
in general I like the idea of lazy loading. But I think atm it would break the hot module replacement. Since we don't have direct imports within the modules, the hot reloader would have to track which imports are done dynamically/lazy here. See https://github.com/capaj/systemjs-hot-reloader/issues/97.
Yes, that would be unlikely. I'll give it a try anyway - maybe it's possible to work around this issue using the direct import.
€dit: Ok, I've had a look at it:
__reload
hook on the loaded modules to properly respond to the reload call. These hooks would be separated from their root counterpart.See this branch.
Tbh, with the current state of how modules can be loaded lazily and how these are treated within SystemJS and systemjs-hot-reloader (esp. how they are "linked" to each other), I'm not sure if we can get this working. Sad story ...
What about importing all lazy modules statically inside main.dev.ts (either directly or indirectly)? Maybe this way we can get around this issue. In production the code would be loaded lazily with multiple disconnected dependency graphs. And in dev mode we would directly bundle it when bundling main.dev.ts. Since the main.dev.ts would be the entry point of a single connected dependency graph again, it's __reload callback should be called again, right?
Hm, yes, that works. I've added
import "lazyTestModule";
to main.dev.ts
, and a proper link is created. I don't really like this, but it seems to be the only possible workaround for now.
I'll go this way when setting up the lazy loading examples.
Update: Still in progress - setting up the production mode for this currently raises various problems, esp. since SystemJS was not included in the prod build step until now. There are still problems with the dependencies - it's not detected that they are already loaded, thus, an attempt is made to pick them up:
core.umd.js:3462 EXCEPTION: Uncaught (in promise): Error: (SystemJS) Unexpected token <
SyntaxError: Unexpected token <
Evaluating http://localhost:9988/plugin-typescript/plugin.js
Error loading http://localhost:9988/plugin-typescript/plugin.js
Error loading http://localhost:9988/@angular/router/index.d.ts!http://localhost:9988/plugin-typescript/plugin.js as "@angular/router/index.d.ts!plugin-typescript/plugin.js" from http://localhost:9988/lazyTest.bundle.js
Update: With a more-than-unlikely workaround for overwriting a SystemJS map entry (did not work with the regular stuff, dunno why), loading the lazy module is initiated, but fails with:
core.umd.js:3468 Error: Uncaught (in promise): TypeError: Cannot read property 'annotations' of undefined
at resolvePromise (zone.js:418)
at zone.js:395
at ZoneDelegate.invoke (zone.js:192)
at Object.onInvoke (core.umd.js:6242)
at ZoneDelegate.invoke (zone.js:191)
at Zone.run (zone.js:85)
at zone.js:451
at ZoneDelegate.invokeTask (zone.js:225)
at Object.onInvokeTask (core.umd.js:6233)
at ZoneDelegate.invokeTask (zone.js:224)ErrorHandler.handleError @ core.umd.js:3468
zone.js:344 Unhandled Promise rejection: Cannot read property 'annotations' of undefined ; Zone: angular ; Task: Promise.then ; Value: TypeError: Cannot read property 'annotations' of undefined(…) TypeError: Cannot read property 'annotations' of undefined
at ReflectionCapabilities.annotations (http://localhost:9988/bundle.js:49810:41)
at Reflector.annotations (http://localhost:9988/bundle.js:50016:56)
at NgModuleResolver.resolve (http://localhost:9988/bundle.js:16564:52)
at CompileMetadataResolver.getNgModuleMetadata (http://localhost:9988/bundle.js:16813:55)
at RuntimeCompiler._compileComponents (http://localhost:9988/bundle.js:19486:55)
at RuntimeCompiler._compileModuleAndComponents (http://localhost:9988/bundle.js:19421:45)
at RuntimeCompiler.compileModuleAsync (http://localhost:9988/bundle.js:19412:29)
at ModuleBoundCompiler.compileModuleAsync (http://localhost:9988/bundle.js:19723:39)
at MergeMapSubscriber.project (http://localhost:9988/bundle.js:23160:130)
at MergeMapSubscriber._tryNext (http://localhost:9988/bundle.js:22364:31)consoleError @ zone.js:344
Seems that the module itself has to be loaded after the corresponding bundle was loaded. However, that would require a loading strategy which is different from the dev mode. Dev atm.:
SystemJS.import("lazyTestModule").then(module => module.LazyTestModule)
Required for prod atm.:
SystemJS.import("lazyTestModule").then(() =>
SystemJS.import("src/app/+lazyTest").then((module: any) => module.LazyTestModule)
)
€dit: Maybe it is possible to define to load this module from a bundle with specialized config entry in production mode. That might also eliminate the need for the "workaround" I mentioned above.
Moving on ...
See here for a description on how to lazy load modules with the default router.
In general, lazy loading appears rather easy, especially with SystemJS. However, from what I've seen up to now, this only holds true for development mode, since live-transpilation is possible in that case. For production mode, a bundle created from the target module has to be loaded and thus its creation has to be set up accordingly. Suppose it will end up like with something like webpack's code splitting ability, with the only difference of having to set this up manually.
We should provide at least two examples:
(see the docs)
(Not in the docs, but it's definitely possible)
Note: The first version seems to end up using
System.import
within the current loading implementation in angular. I'm not sure if loading via an alias works properly in that case, thus, the second version should be included as well (using an alias for loading would be useful when providing multiple bundles).