aspnet / JavaScriptServices

[Archived] This repository has been archived
Apache License 2.0
3.04k stars 521 forks source link

How to get Lazy Loading working #465

Closed JohnGalt1717 closed 7 years ago

JohnGalt1717 commented 7 years ago

I've tried this: https://medium.com/@daviddentoom/angular-2-lazy-loading-with-webpack-d25fe71c29c1#.b2e5z0v00

I followed the directions to add the npm modules etc.

in webpack.config.js on the .ts$ loader I added this to the loaders array: 'angular2-router-loader'

I then updated the app.module.ts file like this:

`import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; import { UniversalModule } from 'angular2-universal'; import { AppComponent } from './components/app/app.component' import { NavMenuComponent } from './components/navmenu/navmenu.component';

@NgModule({ bootstrap: [ AppComponent ], declarations: [ AppComponent, NavMenuComponent ], imports: [ UniversalModule, // Must be first import. This automatically imports BrowserModule, HttpModule, and JsonpModule too. RouterModule.forRoot([ { path: '', redirectTo: 'home', pathMatch: 'full' }, { path: 'home', loadChildren: './components/home/home.component#HomeComponent' }, { path: 'counter', loadChildren: './components/counter/counter.component#CounterComponent' }, { path: '**', redirectTo: 'home' } ]) ] }) export class AppModule { } `

When I run this in the /wwwroot/dist folder there is now a 1.1.js and a 2.2.js file. And I get the following:

Exception: Call to Node module failed with error: Error: Uncaught (in promise): Error: No NgModule metadata found for 'HomeComponent'. Error: No NgModule metadata found for 'HomeComponent'.

What am I missing?

Also, when this is done, will pre-render still work and pre-render the pages with the lazy loaded page's HTML there?

Thanks!

(And if I'm not missing something, please share how to get lazy loading working in a supported way because beyond a trivial example any serious site will require lazy loading otherwise you'll be loading megabytes of data on first run.)

boemer1973 commented 7 years ago

you need to convert your loadChildren-routes, there are a few npm packages that do that for you, I'm personally using this one: https://github.com/Quramy/angular2-load-children-loader

JohnGalt1717 commented 7 years ago

Right which is what I'm doing if you follow the link above. (slightly different loader but the same none-the-less) My issue is that it's not working and I don't know why and I can't find a concrete guide on how to do this based on what MS has provided and given that this is required for any significant application I'm asking that guidance be provided with examples (or better yet, it should be added to the base templates)

boemer1973 commented 7 years ago

Oh I see I am also using the following (so you can forget about the children loader):

https://www.npmjs.com/package/angular2-router-loader

And I have the following in my webpack.config.js to activate it:

var sharedConfig = { ... module: { loaders: [ { test: /.ts$/, include: /Scripts/, loaders: ['ts?silent=true', 'angular2-template', 'angular2-router-loader'] },

JohnGalt1717 commented 7 years ago

So I did that, but then I get the files in the dist folder as mentioned above and it doesn't load properly with the error above which makes me think I'm missing something?

boemer1973 commented 7 years ago

Sorry I see now, that you have been using the same package.

Here my entry for lazy-loading routing:

{ path: 'settings', loadChildren: './+settings/settings.module#SettingsModule' },

The only difference I can see, is that I am using lazy-loading for a module (NgModule), not for a single component, at least that could explain the error you are getting... I would make a feature-module throw in the component you want, and then use lazy-loading.

In that feature module you can add normal routing for your components...

MarkPieszak commented 7 years ago

@SteveSandersonMS This is actually something I wanted to talk to you about, not sure if you have some time today or this week? I believe it's something in the way we're handling webpack on the .NET side, because we can get lazy-loading working with Universal on a regular Node server.

But it comes up:

Microsoft.AspNetCore.NodeServices:Error: { Error: Uncaught (in promise): Error: Cannot find module './0.webpack-output.js'

Although the file in actuality is called, 0.js (located /dist/ like the other webpack files)


Let me know if you have some time I have a repo that can reproduce it, it works once the client-bootstraps, but that initial server rendered view has it missing.

@JohnGalt1717 The loaders help make it so that you don't need to put in boilerplate code, but they are essentially doing this:

{   // ** LAZY-LOADING EXAMPLE **
        // Notice we don't reference the file anywhere else, imports, declarations, anywhere
        // Webpack2 will provide the separate "chunk" due to System.import
        path: 'faq', 
        loadChildren : () => System.import('../containers/+faq/faq.module').then((file: any) => {
            // I'm using .default here since we use `export default` 
            // in the FAQModule NgModule
            return file.default;
        }) 
    },

The problem is something else (once you get that part working), in that we're unable to find the Chunk file during the server-render portion (in .NET). Hopefully we'll be able to patch that up soon!

SteveSandersonMS commented 7 years ago

Strange - I don't know about the errors you've mentioned, but I tried setting it up in the simple manner described at the provided link, and it all just worked fine (including server-side prerendering).

The exact set of changes I made to Angular2Spa are here: https://github.com/aspnet/JavaScriptServices/commit/7ded0a0b60ce3a5356417a5d6102a376fe5f6802

How does this compare with what you were doing? Do you agree that the example I've posted here covers the requirements?

@MarkPieszak If you can describe exactly how to get the Cannot find module './0.webpack-output.js' error, I'd have a look. In particular, where does the string webpack-output come from? Is that in your Webpack config somewhere? Or does it come from an older (and possibly buggy) version of angular2-router-loader?

SteveSandersonMS commented 7 years ago

BTW I'm closing because it looks like maybe it was just a config issue, but if anyone can give repro steps for something they think is a bug in this repo, please reopen or file an issue with those repro steps. Thanks!

JohnGalt1717 commented 7 years ago

I'm still not clear why I need to use a feature module when this is a single page that I'm lazy loading and not a sub feature set or area. I want EVERY page in the site to lazy load.

SteveSandersonMS commented 7 years ago

@JohnGalt1717 Please tell the Angular team :)

JohnGalt1717 commented 7 years ago

Where can I tell them? It would seem obvious that this should just work because there's no case with any app beyond hello world where you'd want it to work like the default does nor would you only want to batch up areas and this appears to require that every area has it's own routing instead of sharing the main routing.

SteveSandersonMS commented 7 years ago

I'd start at https://github.com/angular/angular/issues

MarkPieszak commented 7 years ago

@SteveSandersonMS It comes from your aspnet-webpack package inside of the loadViaWebpackNoCache method: https://github.com/aspnet/JavaScriptServices/blob/46966322b7509f4ae01ec11870707ee2153267cf/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/src/LoadViaWebpack.ts#L55

I spent a while yesterday trying to see if I could fix it, but it looks like it doesn't pass in the correct name of the Chunk, in my case 0.

I just have a module being manually lazy-loaded, essentially doing what angular2-router-loader is doing anyway: (https://github.com/MarkPieszak/aspnetcore-angular2-universal/blob/master/Client/app/app.routes.ts#L23-L32)

Were you able to successfully get Lazy loading working yourself?

MarkPieszak commented 7 years ago

@JohnGalt1717 Unfortunately it's just one of those things, that if you want that section to LazyLoad, you need to make it into it's own NgModule. Prior to rc5 you didn't need one, which I believed to be much less boilerplate, but the NgModule creates ways of doing things that you previously couldn't do, hence why they brought it into the picture. Also most individual "pages" will be SUCH small files, what you want to do is create Modules for "feature sections", such as "Admin", "Payment", etc. When you put 1 page in a file, it'll be like 3kb, there's not much of a benefit of chunking it. This way, you'll also have less Modules in general!

SteveSandersonMS commented 7 years ago

@MarkPieszak Yes, lazy loading works fine with the code at https://github.com/aspnet/JavaScriptServices/commit/7ded0a0b60ce3a5356417a5d6102a376fe5f6802

It comes from your aspnet-webpack package inside of the loadViaWebpackNoCache method

Ah, I see! Recent templates don't use loadViaWebpack - that feature is being deprecated. Try setting up lazy loading on a more recently-generated project; it should just work.

MarkPieszak commented 7 years ago

Ahh okay, should we remove that old code, or is it still needed for other things? I was still using it because I thought we needed to :( haha

Thanks for the quick response!

SteveSandersonMS commented 7 years ago

should we remove that old code, or is it still needed for other things?

It's marked with [Obsolete] on the .NET side, so hopefully people will migrate away from it. It can stay for a while to give people a good chance to move on. Eventually yes, we should remove it.

MarkPieszak commented 7 years ago

Sounds good to me! I didn't notice the Obselete, my own fault! :(