Skip to content

[router] Recommend Dynamic Import Expressions instead of string for loadChildren #7147

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
sclausen opened this issue Jul 26, 2017 · 6 comments

Comments

@sclausen
Copy link

sclausen commented Jul 26, 2017

Bug Report or Feature Request (mark with an x)

- [ ] bug report -> please search issues before submitting
- [ ] feature request
- [x] suggestion/idea

Desired functionality.

Currently, it's recommended to use loadChildren with the module location as a string and the class name after a # like following:

const appRoutes: Routes = [
  {
    path: 'crisis-center',
    loadChildren: 'app/crisis-center/crisis-center.module#CrisisCenterModule'
  }
];

Since Typescript 2.4 we can use the feature "Dynamic Import Expressions", which allows to dynamically import modules without using external module loaders like SystemJS.

I would suggest to deprecate the string loading functionality and switch to something like the following, not only because this is a nifty feature Typescript now supports, but we can get rid of additional logic to ensure webpack builds the lazy loaded chunks (/packages/@ngtools/webpack/src/lazy_routes.ts):

export function loadCrisisCenterModule() {
  return import('app/crisis-center/crisis-center.module').then(mod => mod.CrisisCenterModule);
}

const appRoutes: Routes = [
  {
    path: 'crisis-center',
    loadChildren: loadCrisisCenterModule
  }
];

I've tried the code after switching modules in tsconfig from es2015 to commonjs and with @angular/[email protected]

Here is a plunkr example

@clydin
Copy link
Member

clydin commented Jul 26, 2017

Unfortunately, this won't work with AOT without additional logic.

@deebloo
Copy link
Contributor

deebloo commented Jul 26, 2017

Should be doable. I don't imagine we would deprecate the current way though

@tytskyi
Copy link

tytskyi commented Jul 26, 2017

Simply using dynamic import will not respect PreloadingStrategy

@sclausen
Copy link
Author

@clydin Yes, I can't get it to work with AoT. Such a pity!

@majo44
Copy link
Contributor

majo44 commented Sep 13, 2017

Hi.
I also think that standard dynamic import should be at least supported within full aot/prod build, without any extra effort.

Currently I found some workaround which works fine in universal application (the standard lazyload do not work on ng universal apps) .

let server = false;

if (typeof window === "undefined") {
    server = true;
}

export async function preloadPageModule(): Promise<any> {
    return (await import(/* webpackChunkName: "page" */ './pages/page.module')).PageModule;
}

@NgModule({
    imports: [
        // ...
        RouterModule.forRoot([
            // ...
            { path: 'page', loadChildren: server ? preloadPageModule : './pages/page.module#PageModule'}
        ])
    ],
    declarations: [AppComponent],
    bootstrap: [AppComponent]
})
export class AppModule { }

Setup:

  • client side tsconfig.json modules is esnext
  • client side build can be with --aot --prod
  • server side build have to be jit ! (aot/prod not supported)

Still there is one problem, application on server side is prerendering the PageModule (component from it) , then after application start this component is removed from dom, chunk for the PageModule component is loading, after load is rendered again. What can be frustrating for the user but can works eg for SEO.

BTW
Webpack and Typescript plays well with import statement. Especially that Webpack provides two interesting features webpackChunkName and webpackMode. Especially this second feature is very powerful because is gives you a tuning option (you can preload some chunk without evaluation). I'm using this for loading polyfills conditionally eg:

const promises: Array<Promise<any>> = [];
if (typeof Object.assign === 'undefined') {
    // Object.assign is available in most of browsers - standard lazy load if needed
    promises.push(import(/* webpackChunkName: "objectAssignPolyfill" */ 'object.assign'));
}
if (typeof (window as any).Zone === 'undefined') {
    // Zone is not in any browsers - eager (bundled in same bundle as this file, but lazy evaluate)
    promises.push(import(/* webpackMode: "eager", webpackChunkName: "zonePolyfill" */ 'zone.js/dist/zone'));
}
Promise.all(promises).then(() => {
    // And after preload polyfills evaluate and execute the app code.
    return import(/* webpackMode: "eager", webpackChunkName: "bootstrap" */ './app/app.bootstrap');
});

@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Sep 7, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants