如何懒加载 Scripts 与 Styles

你可以使用@abp/ng.core包中的 LazyLoadService 以简单明了的方式延迟加载脚本和样式.

入门

你不必在模块或组件/指令级别提供 LazyLoadService,因为它已经在根中中提供了. 你可以在组件,指令或服务中注入并使用它.

  1. import { LazyLoadService } from '@abp/ng.core';
  2. @Component({
  3. /* class metadata here */
  4. })
  5. class DemoComponent {
  6. constructor(private lazyLoadService: LazyLoadService) {}
  7. }

用法

你可以使用 LazyLoadServiceload 方法在DOM中的所需位置创建 <script><link> 元素并强制浏览器下载目标资源.

如何加载 Scripts

load 方法的第一个参数需要一个 LoadingStrategy. 如果传递 ScriptLoadingStrategy 实例,LazyLoadService 将使用给定的 src 创建一个 <script> 元素并放置在指定的DOM位置.

  1. import { LazyLoadService, LOADING_STRATEGY } from '@abp/ng.core';
  2. @Component({
  3. template: `
  4. <some-component *ngIf="libraryLoaded$ | async"></some-component>
  5. `
  6. })
  7. class DemoComponent {
  8. libraryLoaded$ = this.lazyLoad.load(
  9. LOADING_STRATEGY.AppendAnonymousScriptToHead('/assets/some-library.js'),
  10. );
  11. constructor(private lazyLoadService: LazyLoadService) {}
  12. }

load 方法返回一个 observable,你可以在组件中或通过 AsyncPipe 订阅它. 在上面的示例中仅当脚本成功加载或之前已经加载脚本时, NgIf 指令才会呈现 <some-component>.

你可以使用 async 管道在模板中多次订阅,脚本将仅加载一次

请参阅LoadingStrategy查看所有可用的加载策略以及如何构建自己的加载策略.

如何加载 Styles

如果传递给 load 方法第一个参数为 StyleLoadingStrategy 实例,LazyLoadService 将使用给定的 href 创建一个 <link> 元素并放置在指定的DOM位置.

  1. import { LazyLoadService, LOADING_STRATEGY } from '@abp/ng.core';
  2. @Component({
  3. template: `
  4. <some-component *ngIf="stylesLoaded$ | async"></some-component>
  5. `
  6. })
  7. class DemoComponent {
  8. stylesLoaded$ = this.lazyLoad.load(
  9. LOADING_STRATEGY.AppendAnonymousStyleToHead('/assets/some-styles.css'),
  10. );
  11. constructor(private lazyLoadService: LazyLoadService) {}
  12. }

load 方法返回一个 observable,你可以在组件中或通过 AsyncPipe 订阅它. 在上面的示例中仅当样式成功加载或之前已经加载样式时, NgIf 指令才会呈现 <some-component>.

你可以使用 async 管道在模板中多次订阅,样式将仅加载一次

请参阅LoadingStrategy查看所有可用的加载策略以及如何构建自己的加载策略.

高级用法

你有很大的自由度来定义延迟加载的工作方式. 示例:

  1. const domStrategy = DOM_STRATEGY.PrependToHead();
  2. const crossOriginStrategy = CROSS_ORIGIN_STRATEGY.Anonymous(
  3. 'sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh',
  4. );
  5. const loadingStrategy = new StyleLoadingStrategy(
  6. 'https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css',
  7. domStrategy,
  8. crossOriginStrategy,
  9. );
  10. this.lazyLoad.load(loadingStrategy, 1, 2000);

此代码将创建具有给定URL和完整性哈希的 <link> 元素,将其插入到 <head> 元素的顶部,如果第一次尝试失败,则在2秒后重试一次.

一个常见的用例是在使用功能之前加载多个脚本/样式:

  1. import { LazyLoadService, LOADING_STRATEGY } from '@abp/ng.core';
  2. import { frokJoin } from 'rxjs';
  3. @Component({
  4. template: `
  5. <some-component *ngIf="scriptsAndStylesLoaded$ | async"></some-component>
  6. `
  7. })
  8. class DemoComponent {
  9. private stylesLoaded$ = forkJoin(
  10. this.lazyLoad.load(
  11. LOADING_STRATEGY.PrependAnonymousStyleToHead('/assets/library-dark-theme.css'),
  12. ),
  13. this.lazyLoad.load(
  14. LOADING_STRATEGY.PrependAnonymousStyleToHead('/assets/library.css'),
  15. ),
  16. );
  17. private scriptsLoaded$ = forkJoin(
  18. this.lazyLoad.load(
  19. LOADING_STRATEGY.AppendAnonymousScriptToHead('/assets/library.js'),
  20. ),
  21. this.lazyLoad.load(
  22. LOADING_STRATEGY.AppendAnonymousScriptToHead('/assets/other-library.css'),
  23. ),
  24. );
  25. scriptsAndStylesLoaded$ = forkJoin(this.scriptsLoaded$, this.stylesLoaded$);
  26. constructor(private lazyLoadService: LazyLoadService) {}
  27. }

RxJS forkJoin 并行加载所有脚本和样式,并仅在加载所有脚本和样式时才放行. 因此放置 <some-component> 时,所有必需的依赖项都可用的.

注意到我们在文档头上添加了样式吗? 有时这是必需的因为你的应用程序样式可能会覆盖某些库样式. 在这种情况下你必须注意前置样式的顺序. 它们将一一放置,并且在放置前,最后一个放置在最上面.

另一个常见的用例是按顺序加载依赖脚本:

  1. import { LazyLoadService, LOADING_STRATEGY } from '@abp/ng.core';
  2. import { concat } from 'rxjs';
  3. @Component({
  4. template: `
  5. <some-component *ngIf="scriptsLoaded$ | async"></some-component>
  6. `
  7. })
  8. class DemoComponent {
  9. scriptsLoaded$ = concat(
  10. this.lazyLoad.load(
  11. LOADING_STRATEGY.PrependAnonymousScriptToHead('/assets/library.js'),
  12. ),
  13. this.lazyLoad.load(
  14. LOADING_STRATEGY.AppendAnonymousScriptToHead('/assets/script-that-requires-library.js'),
  15. ),
  16. );
  17. constructor(private lazyLoadService: LazyLoadService) {}
  18. }

在此示例中,第二个文件需要预先加载第一个文件, RxJS concat 函数将允许你以给定的顺序一个接一个地加载所有脚本,并且仅在加载所有脚本时放行.

API

loaded

  1. loaded: Set<string>

所有以前加载的路径都可以通过此属性访问. 它是一个简单的[JavaScript集]

load

  1. load(strategy: LoadingStrategy, retryTimes?: number, retryDelay?: number): Observable<Event>
  • strategy 是主要参数,上面已经介绍过.
  • retryTimes 定义加载失败前再次尝试多少次(默认值:2).
  • retryDelay 定义重试之间的延迟(默认为1000).

下一步是什么?