Angular中如何在不配置路由的前提下实现组件预加载?
结论
这个需求完全可以实现,不需要为懒加载组件分配不可访问的路由,也不需要把这类组件声明在AppModule中。
实现逻辑说明
Angular自带的路由预加载策略,本质是对框架底层动态导入能力的上层封装,核心依赖的是动态import()语法和运行时的组件/模块解析能力,这套能力并没有和路由模块强绑定,完全可以脱离路由单独使用。
所谓“配置不可达路由+路由守卫实现预加载”,只是早期开发者为了复用内置预加载逻辑的取巧方案,存在污染路由表、预加载时机不灵活、额外增加守卫维护成本等问题,不是必须的实现路径。
具体实现步骤
- 第一步:拆分懒加载单元
把需要预加载的组件从AppModule及其他主包引入的模块中移除,单独组织:- Angular 14+版本可以直接将组件标记为
standalone: true,不需要额外包裹模块 - 低版本可以将组件封装在独立的特性模块中,将组件声明在模块的
declarations和exports数组中即可,不需要在AppModule中引入这个模块
- Angular 14+版本可以直接将组件标记为
- 第二步:编写自定义预加载服务
不需要实现Angular路由内置的PreloadingStrategy接口,直接在根级服务中维护预加载组件的映射表,自主控制预加载时机。参考实现:
import { Injectable } from '@angular/core'; // 预加载组件映射表,直接使用动态import声明,打包工具会自动识别为懒加载chunk const PRELOAD_COMPONENT_LOADERS = { 'user-popup': () => import('./lazy-components/user-popup/user-popup.component').then(m => m.UserPopupComponent), 'data-panel': () => import('./lazy-components/data-panel/data-panel.component').then(m => m.DataPanelComponent) }; @Injectable({ providedIn: 'root' }) export class ComponentPreloadService { // 预加载完成的组件缓存 private componentCache = new Map<string, any>(); /** * 触发预加载,默认在浏览器空闲时段执行,不阻塞首屏 */ startPreload(scheduleTask = window.requestIdleCallback || ((cb) => setTimeout(cb, 1200))) { Object.entries(PRELOAD_COMPONENT_LOADERS).forEach(([compKey, loader]) => { scheduleTask(async () => { try { const compRef = await loader(); this.componentCache.set(compKey, compRef); } catch (err) { console.error(`组件${compKey}预加载失败`, err); } }); }); } /** * 业务中需要使用组件时,直接从缓存取已加载的组件类 */ getComponent(compKey: string) { return this.componentCache.get(compKey); } }
- 第三步:选择合适的时机触发预加载
你可以完全自主控制预加载触发点,不需要和路由导航绑定:- 首屏渲染完成后,在AppComponent的
ngAfterViewInit钩子中调用startPreload() - 监听到用户产生可能打开对应懒加载组件的交互行为(比如鼠标悬停到对应按钮上)时,提前触发对应组件的加载
- 非首屏路由导航完成、浏览器网络空闲时触发批量预加载
- 首屏渲染完成后,在AppComponent的
- 第四步:使用预加载的组件
需要渲染组件时,直接从服务的缓存中拿到组件类,配合ViewContainerRef.createComponent()方法动态创建即可,和普通动态组件的渲染逻辑完全一致。Ivy编译模式下不需要额外做模块编译、工厂解析的操作。
注意事项
- 只要你是通过动态
import()引入这些组件,无论是否配置路由,Angular CLI对应的打包工具(Webpack/Esbuild)都会自动将这些组件拆分为独立的懒加载chunk,不会打入主包,也不会影响首屏包体积。 - 如果是Angular 13之前使用ViewEngine的旧版本,预加载NgModule封装的组件时,需要注入
Compiler服务,调用compileModuleAsync()方法编译导入的模块后,再从模块中取出对应组件使用。
内容的提问来源于stack exchange,提问作者Igino Boffa
相关产品推荐
相关产品推荐

