import { DOCUMENT } from '@angular/common' ; import { Inject , Injectable } from '@angular/core' ; import { BehaviorSubject , Observable } from 'rxjs' ; import { filter , share } from 'rxjs/operators' ; import type { NzSafeAny } from 'ng-zorro-antd/core/types' ; export interface LazyResult { path : string ; status : 'ok' | 'error' | 'loading' ; error ? : NzSafeAny ; } /**
 * 延迟加载资源（js 或 css）服务
 */ @ Injectable ( { providedIn : 'root' } ) export class LazyService { private list : { [ key ] : boolean ; } = { } ; private cached : { [ key ] : LazyResult ; } = { } ; private _notify : BehaviorSubject < LazyResult [ ] > = new BehaviorSubject < LazyResult [ ] > ( [ ] ) ; constructor ( @ Inject ( DOCUMENT ) private doc ) { } get change ( ) : Observable < LazyResult [ ] > { return this . _notify . asObservable ( ) . pipe ( share ( ) , filter ( ls => ls . length !== 0 ) ) ; } clear ( ) { this . list = { } ; this . cached = { } ; } load ( paths ) { if ( ! Array . isArray ( paths ) ) { paths = [ paths ] ; } const promises = [ ] ; paths . forEach ( path => { if ( path . endsWith ( '.js' ) ) { promises . push ( this . loadScript ( path ) ) ; } else { promises . push ( this . loadStyle ( path ) ) ; } } ) ; return Promise . all ( promises ) . then ( res => { this . _notify . next ( res ) ; return Promise . resolve ( res ) ; } ) ; } loadScript ( path , innerContent ? ) { return new Promise ( resolve => { if ( this . list [ path ] === true ) { resolve ( { ... this . cached [ path ] , status : 'loading' } ) ; return ; } this . list [ path ] = true ; const onSuccess = ( item ) : void => { this . cached [ path ] = item ; resolve ( item ) ; this . _notify . next ( [ item ] ) ; } ; const node = this . doc . createElement ( 'script' ) as HTMLScriptElement ; node . type = 'text/javascript' ; node . src = path ; if ( innerContent ) { node . innerHTML = innerContent ; } node . onload = ( ) => onSuccess ( { path , status : 'ok' } ) ; node . onerror = error => onSuccess ( { path , status : 'error' , error } ) ; this . doc . getElementsByTagName ( 'head' ) [ 0 ] . appendChild ( node ) ; } ) ; } loadStyle ( path , rel = 'stylesheet' , innerContent ? ) { return new Promise ( resolve => { if ( this . list [ path ] === true ) { resolve ( this . cached [ path ] ) ; return ; } this . list [ path ] = true ; const node = this . doc . createElement ( 'link' ) as HTMLLinkElement ; node . rel = rel ; node . type = 'text/css' ; node . href = path ; if ( innerContent ) { node . innerHTML = innerContent ; } this . doc . getElementsByTagName ( 'head' ) [ 0 ] . appendChild ( node ) ; const item = { path , status : 'ok' } ; this . cached [ path ] = item ; resolve ( item ) ; } ) ; } }