Angular自定义预加载策略

这篇文章发布于

DeathGhost 编辑, 归类于 angular » 当前评论 0 条。

预加载是介于直接加载与懒加载的一种方式。懒加载是为了降低初始化所需时间,预加载是为了提升即将进入路由模块时间。

Angular实际应用中我们会根据业务或角色权限拆分出各个模块,然后在路由中除初始化模块外设置懒加载。但是为了能够快速导航到即将进入的模块,我们会对路由模块设置预加载。Angular路由提供了preloadingStrategy配置属性,这个属性定义了用于预加载和处理延迟加载的Angular模块的逻辑,将PreloadAllModules指定给preloadingStrategy

import { RouterModule, PreloadAllModules } from '@angular/router';
// …

RouterModule.forRoot([
  …
], {
  preloadingStrategy: PreloadAllModules
})
// …

详情可阅读上篇文章关于“Angular路由预加载”。

Angular自定义预加载策略
Angular自定义预加载策略

今天谈谈另一种需求场景,就是对各个模块可配置性预加载;上述方法默认是预加载所有模块,但随着业务模块增多,未必是个好方法,对服务器资源也是一种消耗。我们设置一个自定义预加载策略,然后对每个模块进行选择性配置是否对其预加载。

创建preloading-strategy.service.ts预加载策略服务

import { Injectable } from '@angular/core';
import { PreloadingStrategy, Route } from '@angular/router';
import { Observable, of } from 'rxjs';

@Injectable({
  providedIn: 'root',
})

export class SelectivePreloadingStrategyService implements PreloadingStrategy {
  preloadedModules: string[] = [];
  preload(route: Route, load: () => Observable<any>): Observable<any> {
    if (route.data && route.data['preload']) {
      this.preloadedModules.push(route.path);
      return load();
    } else {
      return of(null);
    }
  }
}

在路由模块中导入并配置

import { PreloadingStrategyService } from '../preloading-strategy.service';
// ...
{ path: '', loadChildren: () => import('./user/user.module').then(m => m.UserModule), data: {preload: false} },
// ...
{ preloadingStrategy: PreloadingStrategyService, relativeLinkResolution: 'legacy' }
// ...

其中data: {preload: true}就是设置是否预加载。

那么,问题来了,未设置预加载的模块,还是原来的懒加载。我们都知道,懒加载情况下,点击跳转路由时,若该模块未加载完成,当前触发犹如未操作一般,用户体验非常糟糕。这时,我们可以在路由插座上设置CSS过渡效果以缓解这个尴尬的场面。

路由插座RouterOutlet中模块加载设置CSS过渡样式

RouterOutlet是一个占位符,Angular 会根据当前的路由器状态动态填充它。我们在页面整体布局结构中会使用到它。

<router-outlet>
  <div class="pageLoad" *ngIf="loadModule">
    <div class="loading_module">
      <div class="dot white"></div>
      <div class="dot"></div>
      <div class="dot"></div>
      <div class="dot"></div>
      <div class="dot"></div>
    </div>
    <p class="text">数据模块加载中,请耐心等待...</p>
  </div>
</router-outlet>
import { Router, NavigationEnd, RouterEvent, NavigationStart, GuardsCheckEnd } from '@angular/router';
// ...
loadModule: boolean;
constructor(
  private router: Router,
) { 
  this.loadModule = true;
  this.router.events.subscribe(
    (event: RouterEvent): void => {
      if (event instanceof NavigationStart) {
        this.loadModule = true;
      } else if (event instanceof NavigationEnd) {
        this.loadModule = false;
      } else if (event instanceof GuardsCheckEnd) {
        this.loadModule = false;
      } else if () {}
    }
  );
}
// ...

NavigationStart 导航开始时,显示定义的那段“pageLoad”,当NavigationEnd导航成功结束时将插座中的那段HTML文本移除掉。

过渡样式,示例中用SCSS文件编写。

.pageLoad{
    position: absolute;
    left:0;
    right:0;
    top:0;
    bottom:0;
    background:white;
    display:flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    z-index: 1986;
    .text{
        margin-top: 10em;
        letter-spacing: 1px;
        color:#999;
        font-size: 12px;
        text-align: center;
    }
}
.loading_module { 
    position: absolute;
    margin: auto;
    top: 0; bottom: 0; left: 0; right: 0;
    width: 6.250em; height: 6.250em;
    animation: rotate 2.4s linear infinite;
    .white { 
      top: 0; bottom: 0; left: 0; right: 0; 
      background: white; 
      animation: flash 2.4s linear infinite;
      opacity: 0;
    }
    .dot {
      position: absolute;
      margin: auto;
      width: 2.4em; height: 2.4em;
      border-radius: 100%;
      transition: all 1s ease;
    }
    .dot:nth-child(2) { top: 0; bottom: 0; left: 0; background: #FF4444; animation: dotsY 2.4s linear infinite; }
    .dot:nth-child(3) { left: 0; right: 0; top: 0; background: #FFBB33; animation: dotsX 2.4s linear infinite; }
    .dot:nth-child(4) { top: 0; bottom: 0; right: 0; background: #99CC00; animation: dotsY 2.4s linear infinite; }
    .dot:nth-child(5) { left: 0; right: 0; bottom: 0; background: #33B5E5; animation: dotsX 2.4s linear infinite; }
  }
  @keyframes rotate {
    0% { transform: rotate( 0 ); }
    10% { width: 6.250em; height: 6.250em; }
    66% { width: 2.4em; height: 2.4em; }
    100%{ transform: rotate(360deg); width: 6.250em; height: 6.250em; }
  }
  
  @keyframes dotsY {
    66% { opacity: .1; width: 2.4em; }
    77%{ opacity: 1; width: 0; }
  }
  @keyframes dotsX {
    66% { opacity: .1; height: 2.4em;}
    77%{ opacity: 1; height: 0; }
  }
  
  @keyframes flash {
    33% { opacity: 0; border-radius: 0%; }
    55%{ opacity: .6; border-radius: 100%; }
    66%{ opacity: 0; }
  }
CSS加载过渡效果
CSS加载过渡效果

选择性预加载,可以针对存在不同管理角色,加载相对应的所涉及的模块。避免加载一些非本角色的模块,消耗占用服务端及其用户资源。


Angular 路由 预加载 懒加载 延迟加载 按需加载 自定义预加载 模块加载 动画 过渡

上一篇: