有没有办法在 Angular 中延迟加载组件而不是模块?

Posted

技术标签:

【中文标题】有没有办法在 Angular 中延迟加载组件而不是模块?【英文标题】:Is there a way to lazy load components, not modules, in Angular? 【发布时间】:2018-06-15 22:04:46 【问题描述】:

延迟加载是一种常用的技术。然而,在 Angular 中,这种技术的粒度级别似乎停止在模块级别。

这意味着您可以在浏览器中加载模块 main,然后在特殊情况下可以延迟加载模块 B、C 和 D。

几乎所有网络教程都解释了这一点。但是,有没有办法懒加载组件呢?

考虑这个简单的例子,用户进入应用程序,当点击“发票”链接时,URL 更改为新路由 /invoice/list 并且进度条显示正在加载,而包含 html 和 JS 的 invoices 组件正在在浏览器中加载,在主模块中动态注册,并显示在相关插座中。 Angular 有可能吗?

【问题讨论】:

将它们作为单独的模块 你的意思是每个组件的模块?这太超载了。效率不高。 不,没有直接的做法,但您可以加载一个组件类,然后动态生成一个模块和组件,see this tutorial 也许这会在 Angular 7 中添加,see this issue 是的,从 Ivy 的 Angular 9 开始。看到这个post。 【参考方案1】:

不能延迟加载组件。 这种行为的原因是角度的结构。角度组件必须是模块的一部分。 Angular 模块是一个容器,您可以在其中定义所有组件、服务、管道等。呈现在里面。 在构建应用程序 dist 时,包含在模块中声明的所有依赖项以组成一个块(转译的 js 代码)。 所有直接导入的模块一起形成主块,而标记为延迟加载的模块形成一个单独的块,该块位于服务器上,直到相应的路由被命中并从客户端发出对其的调用。 现在组件不会形成块,因此不可能

【讨论】:

我创建了一个服务,可以动态加载我的惰性模块,然后加载组件——仅在我需要时才加载 @FabioLux 谁愿意分享如何实现它? @KLMN 是的,如果你需要的话【参考方案2】:

目前(Angular 8 时间框架)有两个 3rd 方库可以更轻松地延迟加载组件:

ngx 可加载:https://github.com/mohammedzamakhan/ngx-loadable 英雄加载器:https://www.npmjs.com/package/@herodevs/hero-loader

注意:这两个库都是基于 NgModuleFactoryLoader 构建的,在 Angular 8 中已弃用,但目前还没有其他选择...

标记为已弃用的类:https://angular.io/api/core/NgModuleFactoryLoader Angular 团队 Rob Wormald 的解释:https://twitter.com/robwormald/status/1143981312237137925

Angular 团队已经宣布,使用 Ivy(Angular 9?)组件的延迟加载应该会变得更容易,但目前还不清楚这会是什么样子......

【讨论】:

Igor Minar 在今年的 AngularConnect 上展示了 Ivy 中组件延迟加载的样子。关于不推荐使用的 NgModuleFactoryLoader:请参阅我的最新博客文章:juristr.com/blog/2019/10/lazyload-module-ivy-viewengine【参考方案3】:

有点 hacky ...但可能(Angular 的未来版本(超过版本 7)将实现更简单的方法)

https://blog.angularindepth.com/dynamically-loading-components-with-angular-cli-92a3c69bcd28

【讨论】:

【参考方案4】:

更新,使用 Angular 9 和 Ivy 是可能的并且相对容易。 首先,你需要在你的应用程序中有一些锚点来加载你的组件,如果你想要多个不同的组件,它可以命名为templateRef,或者ng-template 一个。为此(我将在您的 component.html:

中使用ng-template
<ng-template>

component.ts:

export class ExampleComponent 
    @ViewChild(TemplateRef,  read: ViewContainerRef )
    private anchorRef: ViewContainerRef;

    constructor(
        private readonly componentFactoryResolver: ComponentFactoryResolver
    ) 

    private async loadComponent() 
        this.anchorRef.clear(); // clear if some components are already there
        const  component  = await import(
            'path to your component'
        );
        const componentFactory = this.componentFactoryResolver.resolveComponentFactory(
            component
        );
        // We could attach @Input and react to @Output by using below instance if needed.
        // Remember we should handle the ngOnChanges lifecycle hook.
        // For that implementation needed.
        this.anchorRef.createComponent(componentFactory);
    

剩下的基本上就是在需要时调用loadComponent

如果你需要一些模块,例如你想使用 Angular Material。您需要在您的lazyLoaded 组件中创建简单的 ngModule 而不导出。像这样:

@Component(...)
export class Component implements OnInit 
    constructor() 

    ngOnInit() 


@NgModule(
    declarations: [Component],
    imports: [MatIconModule],
)
class TasksListModule 

此外,您可以遵循 Angular 团队在他们的文档中提供的方法: https://angular.io/guide/dynamic-component-loader

【讨论】:

我可以按照这种方法延迟加载属于更大响应式表单的自定义表单控件吗?或者说,对于什么样的组件可以像上面描述的那样惰性加载有什么限制吗? 基本上,延迟加载组件与其他任何组件都是一样的。您只需在引擎盖下使用工厂 Angular 使用任何其他组件和异步函数来加载文件本身。所以没有限制。请注意,当您尝试以编程方式与其交互时,该组件可能不在页面上 为什么这是 0 票?不行吗?

以上是关于有没有办法在 Angular 中延迟加载组件而不是模块?的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法检查延迟加载的组件(使用 React.Lazy)是不是已完成加载?

有没有办法在 Angular 中延迟加载 CSS 文件?

TailwindCSS 在 Angular(延迟加载)子组件中不起作用

角度 5 延迟加载与动态加载

延迟加载Angular / Ionic 3 Component AOT“不是已知元素:错误”

Angular 9 嵌套延迟加载模块,带有嵌套路由器出口