@reskin/core 0.0.17 → 0.0.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundles/reskin-core-directives.umd.js +48 -3
- package/bundles/reskin-core-directives.umd.js.map +1 -1
- package/bundles/reskin-core-services.umd.js +189 -0
- package/bundles/reskin-core-services.umd.js.map +1 -1
- package/directives/directives.module.d.ts +2 -1
- package/directives/load.styles.directive.d.ts +15 -0
- package/directives/public-api.d.ts +1 -0
- package/esm2015/directives/directives.module.js +5 -4
- package/esm2015/directives/load.styles.directive.js +43 -0
- package/esm2015/directives/public-api.js +2 -1
- package/esm2015/services/public-api.js +2 -1
- package/esm2015/services/style.loader.service.js +180 -0
- package/fesm2015/reskin-core-directives.js +45 -5
- package/fesm2015/reskin-core-directives.js.map +1 -1
- package/fesm2015/reskin-core-services.js +179 -3
- package/fesm2015/reskin-core-services.js.map +1 -1
- package/package.json +1 -1
- package/services/public-api.d.ts +1 -0
- package/services/style.loader.service.d.ts +121 -0
|
@@ -4,9 +4,9 @@ import { InjectionToken, Injectable, Inject, APP_INITIALIZER } from '@angular/co
|
|
|
4
4
|
import * as i1 from '@angular/platform-browser';
|
|
5
5
|
import * as i1$1 from '@angular/common/http';
|
|
6
6
|
import { HttpHeaders, HttpClient } from '@angular/common/http';
|
|
7
|
-
import { tap, map, filter } from 'rxjs/operators';
|
|
7
|
+
import { tap, map, filter, catchError, shareReplay } from 'rxjs/operators';
|
|
8
8
|
import { Store, deepClone } from '@reskin/core/utils';
|
|
9
|
-
import { Subject } from 'rxjs';
|
|
9
|
+
import { Subject, of, forkJoin, Observable } from 'rxjs';
|
|
10
10
|
import { AnnexCatalogTypeEnum } from '@reskin/core/entity/annexCatalog';
|
|
11
11
|
|
|
12
12
|
/**
|
|
@@ -1132,9 +1132,185 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.17", ngImpo
|
|
|
1132
1132
|
}]
|
|
1133
1133
|
}], ctorParameters: function () { return [{ type: i0.Injector }]; } });
|
|
1134
1134
|
|
|
1135
|
+
/**
|
|
1136
|
+
* 样式加载服务
|
|
1137
|
+
* 提供动态加载和卸载CSS样式文件的功能,支持引用计数和缓存机制
|
|
1138
|
+
*
|
|
1139
|
+
* @description
|
|
1140
|
+
* 该服务允许在运行时动态加载CSS文件,并通过引用计数机制管理样式文件的生命周期。
|
|
1141
|
+
* 当多个组件需要同一个样式文件时,只会加载一次,但会增加引用计数。
|
|
1142
|
+
* 当所有组件都卸载样式后,才会真正从DOM中移除该样式文件。
|
|
1143
|
+
*
|
|
1144
|
+
* 该服务使用预加载策略优化性能,首先使用`rel="preload"`加载样式文件,
|
|
1145
|
+
* 加载完成后再将其设置为`rel="stylesheet"`以应用样式,避免页面闪烁问题。
|
|
1146
|
+
*
|
|
1147
|
+
* 服务特性:
|
|
1148
|
+
* 1. 引用计数管理 - 避免重复加载和过早卸载
|
|
1149
|
+
* 2. 缓存机制 - 使用shareReplay确保多次订阅复用同一结果
|
|
1150
|
+
* 3. 容错处理 - 单个文件加载失败不影响其他文件
|
|
1151
|
+
* 4. 内存优化 - 及时清理不再使用的样式文件
|
|
1152
|
+
*/
|
|
1153
|
+
class RkStyleLoaderService {
|
|
1154
|
+
/**
|
|
1155
|
+
* 构造函数
|
|
1156
|
+
* @param rendererFactory Angular渲染器工厂,用于创建Renderer2实例
|
|
1157
|
+
*/
|
|
1158
|
+
constructor(rendererFactory) {
|
|
1159
|
+
/** 存储已加载样式的状态映射表,key为样式URL,value为样式状态 */
|
|
1160
|
+
this.styleStates = new Map();
|
|
1161
|
+
this.renderer = rendererFactory.createRenderer(null, null);
|
|
1162
|
+
}
|
|
1163
|
+
/**
|
|
1164
|
+
* 加载一个或多个 CSS 文件,并返回每个文件的加载结果。
|
|
1165
|
+
* 此方法具有容错性,即使部分文件失败,也会返回所有结果。
|
|
1166
|
+
*
|
|
1167
|
+
* @param hrefs CSS 文件 URL列表
|
|
1168
|
+
* @returns Observable<ILoadResult[]> 每个CSS文件的加载结果数组
|
|
1169
|
+
*
|
|
1170
|
+
* @example
|
|
1171
|
+
* ```typescript
|
|
1172
|
+
* // 加载单个样式文件
|
|
1173
|
+
* styleLoaderService.load('/assets/styles/theme.css')
|
|
1174
|
+
* .subscribe(results => {
|
|
1175
|
+
* if (results[0].success) {
|
|
1176
|
+
* console.log('样式加载成功');
|
|
1177
|
+
* } else {
|
|
1178
|
+
* console.error('样式加载失败:', results[0].error);
|
|
1179
|
+
* }
|
|
1180
|
+
* });
|
|
1181
|
+
*
|
|
1182
|
+
* // 加载多个样式文件
|
|
1183
|
+
* styleLoaderService.load('/assets/styles/theme.css', '/assets/styles/components.css')
|
|
1184
|
+
* .subscribe(results => {
|
|
1185
|
+
* results.forEach(result => {
|
|
1186
|
+
* if (result.success) {
|
|
1187
|
+
* console.log(`样式 ${result.href} 加载成功`);
|
|
1188
|
+
* } else {
|
|
1189
|
+
* console.error(`样式 ${result.href} 加载失败:`, result.error);
|
|
1190
|
+
* }
|
|
1191
|
+
* });
|
|
1192
|
+
* });
|
|
1193
|
+
* ```
|
|
1194
|
+
*
|
|
1195
|
+
* @description
|
|
1196
|
+
* 该方法使用forkJoin并行加载所有CSS文件,即使其中某些文件加载失败,
|
|
1197
|
+
* 也会等待所有文件加载完成后再返回结果,确保调用方能获取完整的加载状态信息。
|
|
1198
|
+
*/
|
|
1199
|
+
load(...hrefs) {
|
|
1200
|
+
if (hrefs.length === 0) {
|
|
1201
|
+
return of([]);
|
|
1202
|
+
}
|
|
1203
|
+
const loadObservables = hrefs.map((href) => this.loadSingle(href).pipe(map(() => ({ href, success: true })),
|
|
1204
|
+
// 容错机制 - 捕获单个加载流的错误,并将其转换为成功发出的值
|
|
1205
|
+
catchError((error) => of({ href, success: false, error }))));
|
|
1206
|
+
return forkJoin(loadObservables);
|
|
1207
|
+
}
|
|
1208
|
+
/**
|
|
1209
|
+
* 卸载一个或多个 CSS 文件。
|
|
1210
|
+
* 使用引用计数,只有当没有任何组件需要此样式时,才会真正从 DOM 中移除。
|
|
1211
|
+
*
|
|
1212
|
+
* @param hrefs 要卸载的 CSS 文件 URL列表
|
|
1213
|
+
*
|
|
1214
|
+
* @example
|
|
1215
|
+
* ```typescript
|
|
1216
|
+
* // 卸载单个样式文件
|
|
1217
|
+
* styleLoaderService.unload('/assets/styles/theme.css');
|
|
1218
|
+
*
|
|
1219
|
+
* // 卸载多个样式文件
|
|
1220
|
+
* styleLoaderService.unload('/assets/styles/theme.css', '/assets/styles/components.css');
|
|
1221
|
+
* ```
|
|
1222
|
+
*
|
|
1223
|
+
* @description
|
|
1224
|
+
* 该方法通过减少引用计数来管理样式文件的生命周期。当引用计数降到0时,
|
|
1225
|
+
* 才会真正从DOM中移除对应的<link>元素,从而避免样式文件被提前移除导致页面样式异常。
|
|
1226
|
+
*/
|
|
1227
|
+
unload(...hrefs) {
|
|
1228
|
+
hrefs.forEach((href) => {
|
|
1229
|
+
const state = this.styleStates.get(href);
|
|
1230
|
+
if (state) {
|
|
1231
|
+
state.refCount--;
|
|
1232
|
+
// 当引用计数为0时,从DOM中移除样式文件
|
|
1233
|
+
if (state.refCount === 0) {
|
|
1234
|
+
const linkElement = document.head.querySelector(`link[href="${href}"]`);
|
|
1235
|
+
if (linkElement) {
|
|
1236
|
+
this.renderer.removeChild(document.head, linkElement);
|
|
1237
|
+
}
|
|
1238
|
+
// 清理状态缓存
|
|
1239
|
+
this.styleStates.delete(href);
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
});
|
|
1243
|
+
}
|
|
1244
|
+
/**
|
|
1245
|
+
* 加载单个CSS文件
|
|
1246
|
+
* 使用预加载策略和引用计数机制优化性能
|
|
1247
|
+
*
|
|
1248
|
+
* @param href CSS文件URL
|
|
1249
|
+
* @returns 返回加载CSS文件的Observable
|
|
1250
|
+
*
|
|
1251
|
+
* @description
|
|
1252
|
+
* 该方法使用预加载技术优化样式加载过程:
|
|
1253
|
+
* 1. 首先创建<link>元素并设置为预加载(rel="preload")
|
|
1254
|
+
* 2. 监听加载完成事件
|
|
1255
|
+
* 3. 加载完成后将元素设置为样式表(rel="stylesheet")
|
|
1256
|
+
* 4. 使用shareReplay(1)确保多次订阅复用同一结果
|
|
1257
|
+
* 5. 通过引用计数避免重复加载
|
|
1258
|
+
*
|
|
1259
|
+
* 这种方式可以避免样式加载过程中的页面闪烁问题,同时提高性能。
|
|
1260
|
+
*/
|
|
1261
|
+
loadSingle(href) {
|
|
1262
|
+
// 检查是否已存在该样式文件的加载状态
|
|
1263
|
+
let state = this.styleStates.get(href);
|
|
1264
|
+
if (state) {
|
|
1265
|
+
// 增加引用计数
|
|
1266
|
+
state.refCount++;
|
|
1267
|
+
return state.observable;
|
|
1268
|
+
}
|
|
1269
|
+
// 创建新的加载Observable
|
|
1270
|
+
const load$ = new Observable((observer) => {
|
|
1271
|
+
// 创建link元素
|
|
1272
|
+
const link = this.renderer.createElement('link');
|
|
1273
|
+
// 设置预加载属性
|
|
1274
|
+
this.renderer.setAttribute(link, 'rel', 'preload');
|
|
1275
|
+
this.renderer.setAttribute(link, 'as', 'style');
|
|
1276
|
+
this.renderer.setAttribute(link, 'href', href);
|
|
1277
|
+
// 监听加载完成事件
|
|
1278
|
+
link.onload = () => {
|
|
1279
|
+
// 清除事件处理函数避免内存泄漏
|
|
1280
|
+
link.onload = null;
|
|
1281
|
+
// 将预加载改为样式表应用
|
|
1282
|
+
this.renderer.setAttribute(link, 'rel', 'stylesheet');
|
|
1283
|
+
// 通知观察者加载完成
|
|
1284
|
+
observer.next(link);
|
|
1285
|
+
observer.complete();
|
|
1286
|
+
};
|
|
1287
|
+
// 监听加载错误事件
|
|
1288
|
+
link.onerror = (error) => {
|
|
1289
|
+
observer.error(error);
|
|
1290
|
+
};
|
|
1291
|
+
// 将link元素添加到head中开始加载
|
|
1292
|
+
this.renderer.appendChild(document.head, link);
|
|
1293
|
+
}).pipe(
|
|
1294
|
+
// 使用shareReplay确保多次订阅复用同一结果
|
|
1295
|
+
shareReplay(1));
|
|
1296
|
+
// 创建并存储新的状态
|
|
1297
|
+
state = { observable: load$, refCount: 1 };
|
|
1298
|
+
this.styleStates.set(href, state);
|
|
1299
|
+
return state.observable;
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
RkStyleLoaderService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0, type: RkStyleLoaderService, deps: [{ token: i0.RendererFactory2 }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1303
|
+
RkStyleLoaderService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0, type: RkStyleLoaderService, providedIn: 'root' });
|
|
1304
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0, type: RkStyleLoaderService, decorators: [{
|
|
1305
|
+
type: Injectable,
|
|
1306
|
+
args: [{
|
|
1307
|
+
providedIn: 'root',
|
|
1308
|
+
}]
|
|
1309
|
+
}], ctorParameters: function () { return [{ type: i0.RendererFactory2 }]; } });
|
|
1310
|
+
|
|
1135
1311
|
/**
|
|
1136
1312
|
* Generated bundle index. Do not edit.
|
|
1137
1313
|
*/
|
|
1138
1314
|
|
|
1139
|
-
export { AppConfig, CacheHttpTemplate, RK_ENVIRONMENT, RkAccountService, RkAnnexCatalogService, RkAuthService, RkCacheHttpService, RkDestroyService, RkDictionaryService, RkDownFileService, RkFieldSchemeService, RkFileService, RkMenuService, RkOrgService, RkPersonDatasetService, RkSchemeService, RkUnitDatasetService, providerAppConfig };
|
|
1315
|
+
export { AppConfig, CacheHttpTemplate, RK_ENVIRONMENT, RkAccountService, RkAnnexCatalogService, RkAuthService, RkCacheHttpService, RkDestroyService, RkDictionaryService, RkDownFileService, RkFieldSchemeService, RkFileService, RkMenuService, RkOrgService, RkPersonDatasetService, RkSchemeService, RkStyleLoaderService, RkUnitDatasetService, providerAppConfig };
|
|
1140
1316
|
//# sourceMappingURL=reskin-core-services.js.map
|