customer-chat-sdk 1.0.40 → 1.0.41

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.
@@ -30,6 +30,8 @@ export interface ScreenshotOptions {
30
30
  maxCacheAge?: number;
31
31
  maxImageSize?: number;
32
32
  skipLargeImages?: boolean;
33
+ workerNumber?: number;
34
+ workerUrl?: string;
33
35
  }
34
36
  /**
35
37
  * 上传配置接口
@@ -62,7 +64,11 @@ export declare class ScreenshotManager {
62
64
  private worker;
63
65
  private screenshotTimer;
64
66
  private screenshotContext;
67
+ private contextElement;
68
+ private contextOptionsHash;
65
69
  private isScreenshotInProgress;
70
+ private screenshotQueue;
71
+ private isProcessingQueue;
66
72
  private messageHandler;
67
73
  private dynamicInterval;
68
74
  private expirationTimer;
@@ -163,6 +169,10 @@ export declare class ScreenshotManager {
163
169
  * - 页面资源较少
164
170
  */
165
171
  private takeScreenshotWithModernScreenshot;
172
+ /**
173
+ * 处理截图队列
174
+ */
175
+ private processScreenshotQueue;
166
176
  /**
167
177
  * 预连接代理服务器(优化网络性能)
168
178
  */
@@ -1 +1 @@
1
- {"version":3,"file":"ScreenshotManager.d.ts","sourceRoot":"","sources":["../../src/core/ScreenshotManager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ScreenshotManager.d.ts","sourceRoot":"","sources":["../../src/core/ScreenshotManager.ts"],"names":[],"mappings":"AAWA;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,KAAK,CAAA;IACtC,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,MAAM,CAAC,EAAE,mBAAmB,GAAG,SAAS,GAAG,aAAa,CAAA;IACxD,QAAQ,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,cAAc,GAAG,QAAQ,GAAG,YAAY,CAAA;IAC3F,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,sBAAsB,CAAC,EAAE,MAAM,CAAA;IAC/B,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,uBAAuB,CAAC,EAAE,OAAO,CAAA;IACjC,aAAa,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,CAAA;IACvC,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,EAAE,MAAM,CAAA;IACnB,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,MAAM,CAAA;CACjB;AAoBD;;;GAGG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,OAAO,CAAyH;IACxI,OAAO,CAAC,aAAa,CAA2B;IAChD,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,eAAe,CAAI;IAC3B,OAAO,CAAC,iBAAiB,CAAe;IACxC,OAAO,CAAC,kBAAkB,CAAI;IAC9B,OAAO,CAAC,KAAK,CAAsB;IACnC,OAAO,CAAC,SAAS,CAAQ;IAGzB,OAAO,CAAC,WAAW,CAAQ;IAC3B,OAAO,CAAC,WAAW,CAAsB;IACzC,OAAO,CAAC,cAAc,CAA4B;IAClD,OAAO,CAAC,mBAAmB,CAA4B;IAGvD,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,eAAe,CAA8B;IAGrD,OAAO,CAAC,iBAAiB,CAAY;IACrC,OAAO,CAAC,cAAc,CAA2B;IACjD,OAAO,CAAC,kBAAkB,CAAa;IAGvC,OAAO,CAAC,sBAAsB,CAAQ;IAGtC,OAAO,CAAC,eAAe,CAAqE;IAC5F,OAAO,CAAC,iBAAiB,CAAQ;IAGjC,OAAO,CAAC,cAAc,CAA8D;IAGpF,OAAO,CAAC,eAAe,CAAsB;IAG7C,OAAO,CAAC,eAAe,CAA8B;IAGrD,OAAO,CAAC,eAAe,CAA4D;IAGnF,OAAO,CAAC,cAAc,CAA2B;IACjD,OAAO,CAAC,cAAc,CAAQ;IAG9B,OAAO,CAAC,oBAAoB,CAAoC;IAChE,OAAO,CAAC,oBAAoB,CAAyB;IAGrD,OAAO,CAAC,YAAY,CAAQ;IAG5B,OAAO,CAAC,kBAAkB,CAAqC;IAC/D,OAAO,CAAC,eAAe,CAAoB;IAC3C,OAAO,CAAC,2BAA2B,CAAI;IAGvC,OAAO,CAAC,kBAAkB,CAA6C;IACvE,OAAO,CAAC,sBAAsB,CAAwD;gBAE1E,aAAa,EAAE,WAAW,GAAG,IAAI,EAAE,OAAO,GAAE,iBAAsB;IA6D9E;;OAEG;IACH,gBAAgB,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,GAAG,IAAI;IAoBnD;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAY5B;;OAEG;IACH,OAAO,CAAC,6BAA6B;IAcrC;;OAEG;IACH,OAAO,CAAC,mBAAmB;IA2F3B;;OAEG;YACW,uBAAuB;IAsDrC;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAgCzB;;OAEG;IACH,eAAe,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI;IA8D9C;;OAEG;IACH,cAAc,IAAI,IAAI;IAiBtB;;OAEG;IACG,WAAW,CAAC,KAAK,GAAE,OAAe,GAAG,OAAO,CAAC,OAAO,CAAC;IAS3D;;OAEG;YACW,cAAc;IA2K5B;;;;;;OAMG;YACW,yBAAyB;IAoGvC;;;;;;;;;;;;;;;;OAgBG;YACW,6BAA6B;IAgW3C;;;;;;;;;;;;;;;;;OAiBG;YACW,kCAAkC;IA2hBhD;;OAEG;YACW,sBAAsB;IA0BpC;;OAEG;IACH,OAAO,CAAC,eAAe;IAsBvB;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAuBhC;;OAEG;YACW,aAAa;IA4B3B;;OAEG;YACW,iBAAiB;IAsC/B;;OAEG;YACW,iBAAiB;IAwB/B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAO3B;;OAEG;YACW,qBAAqB;IA0BnC;;OAEG;YACW,mBAAmB;IAiEjC;;OAEG;YACW,yBAAyB;IAuDvC;;OAEG;YACW,oBAAoB;IAclC;;OAEG;IACH,OAAO,CAAC,gBAAgB;IA+CxB;;OAEG;YACW,uBAAuB;IA2ErC;;OAEG;YACW,UAAU;IAmDxB;;OAEG;YACW,yBAAyB;IAsFvC;;OAEG;IACH,OAAO,CAAC,aAAa;IAqBrB;;OAEG;YACW,mBAAmB;IA8BjC;;;OAGG;YACW,qBAAqB;IAqCnC;;OAEG;YACW,YAAY;IAa1B;;;OAGG;YACW,sBAAsB;IAoEpC;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAe/B;;OAEG;IACH,OAAO,CAAC,YAAY;IAgDpB;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAkChC;;OAEG;IACH,OAAO,CAAC,yBAAyB;IAYjC;;OAEG;YACW,gBAAgB;IAwC9B;;OAEG;IACH,OAAO,CAAC,aAAa;IAerB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAI3B;;OAEG;IACH,MAAM,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAO9B;;OAEG;IACH,OAAO,IAAI,IAAI;IAkEf;;OAEG;IACH,OAAO,CAAC,cAAc;IAkBtB;;;OAGG;IACH,OAAO,CAAC,cAAc;IAqCtB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAoBzB;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAsBzB;;OAEG;IACH,QAAQ;;;;;;;;;;;;;;CAaT"}
@@ -14323,8 +14323,13 @@ class ScreenshotManager {
14323
14323
  this.screenshotTimer = null;
14324
14324
  // modern-screenshot Worker 上下文(用于复用,避免频繁创建和销毁)
14325
14325
  this.screenshotContext = null;
14326
+ this.contextElement = null; // 当前 context 对应的元素
14327
+ this.contextOptionsHash = ''; // context 配置的哈希值,用于判断是否需要重新创建
14326
14328
  // 截图锁,防止并发截图
14327
14329
  this.isScreenshotInProgress = false;
14330
+ // 截图队列(用于处理频繁的截图请求)
14331
+ this.screenshotQueue = [];
14332
+ this.isProcessingQueue = false;
14328
14333
  // PostMessage 监听器
14329
14334
  this.messageHandler = null;
14330
14335
  // 动态轮询间隔(由 iframe 消息控制)
@@ -14377,7 +14382,8 @@ class ScreenshotManager {
14377
14382
  maxCacheSize: options.maxCacheSize ?? 50, // 默认最大50MB
14378
14383
  maxCacheAge: options.maxCacheAge ?? 86400000, // 默认24小时(86400000ms)
14379
14384
  maxImageSize: options.maxImageSize ?? 5, // 不使用代理时,单个图片最大尺寸(MB),默认5MB
14380
- skipLargeImages: options.skipLargeImages ?? true // 不使用代理时,是否跳过过大的图片,默认true(跳过)
14385
+ skipLargeImages: options.skipLargeImages ?? true, // 不使用代理时,是否跳过过大的图片,默认true(跳过)
14386
+ workerNumber: options.workerNumber ?? undefined // modern-screenshot Worker 数量,默认自动计算(undefined 表示自动)
14381
14387
  };
14382
14388
  this.setupMessageListener();
14383
14389
  this.setupVisibilityChangeListener();
@@ -14405,15 +14411,22 @@ class ScreenshotManager {
14405
14411
  * 设置目标元素
14406
14412
  */
14407
14413
  setTargetElement(element) {
14408
- // 如果元素改变了,清理旧的 Worker 上下文
14409
- if (this.targetElement !== element && this.screenshotContext) {
14410
- try {
14411
- destroyContext(this.screenshotContext);
14412
- }
14413
- catch (e) {
14414
- // 忽略清理错误
14414
+ // 如果元素变化,需要清理 context(下次截图时会重新创建)
14415
+ if (this.targetElement !== element) {
14416
+ if (this.screenshotContext) {
14417
+ try {
14418
+ destroyContext(this.screenshotContext);
14419
+ if (!this.options.silentMode) {
14420
+ console.log('📸 目标元素变化,清理 context');
14421
+ }
14422
+ }
14423
+ catch (e) {
14424
+ // 忽略清理错误
14425
+ }
14426
+ this.screenshotContext = null;
14427
+ this.contextElement = null;
14428
+ this.contextOptionsHash = '';
14415
14429
  }
14416
- this.screenshotContext = null;
14417
14430
  }
14418
14431
  this.targetElement = element;
14419
14432
  }
@@ -14641,22 +14654,36 @@ class ScreenshotManager {
14641
14654
  if (!this.worker && this.options.compress) {
14642
14655
  this.worker = this.createWorker();
14643
14656
  }
14644
- // 设置定时器
14645
- this.screenshotTimer = setInterval(async () => {
14657
+ // 设置定时器(使用递归 setTimeout,确保等待前一个完成)
14658
+ // 这样可以避免 setInterval 不等待异步完成的问题
14659
+ const scheduleNext = async () => {
14646
14660
  if (this.isRunning && this.isEnabled && !document.hidden) {
14647
- await this.takeScreenshot();
14648
- // 如果配置了上传,且当前有上传配置,自动上传
14649
- if (this.currentUploadConfig) {
14650
- const latestScreenshot = this.getLatestScreenshot();
14651
- if (latestScreenshot && !this.isUploading) {
14652
- this.uploadScreenshot(latestScreenshot, this.currentUploadConfig)
14653
- .catch((error) => {
14654
- console.error('📸 [轮询] 自动上传失败:', error);
14655
- });
14661
+ try {
14662
+ await this.takeScreenshot();
14663
+ // 如果配置了上传,且当前有上传配置,自动上传
14664
+ if (this.currentUploadConfig) {
14665
+ const latestScreenshot = this.getLatestScreenshot();
14666
+ if (latestScreenshot && !this.isUploading) {
14667
+ this.uploadScreenshot(latestScreenshot, this.currentUploadConfig)
14668
+ .catch((error) => {
14669
+ console.error('📸 [轮询] 自动上传失败:', error);
14670
+ });
14671
+ }
14672
+ }
14673
+ }
14674
+ catch (error) {
14675
+ if (!this.options.silentMode) {
14676
+ console.error('📸 [轮询] 截图失败:', error);
14656
14677
  }
14657
14678
  }
14658
14679
  }
14659
- }, currentInterval);
14680
+ // 如果还在运行,安排下一次截图
14681
+ if (this.isRunning) {
14682
+ this.screenshotTimer = setTimeout(scheduleNext, currentInterval);
14683
+ }
14684
+ };
14685
+ // 立即开始第一次
14686
+ scheduleNext();
14660
14687
  // 注意:不再立即执行一次,因为已经在 takeScreenshotAndUpload 中执行了
14661
14688
  }
14662
14689
  /**
@@ -15276,8 +15303,37 @@ class ScreenshotManager {
15276
15303
  */
15277
15304
  async takeScreenshotWithModernScreenshot(element) {
15278
15305
  // 检查是否有截图正在进行(防止并发冲突)
15306
+ // 如果正在进行,将请求加入队列,而不是直接拒绝
15279
15307
  if (this.isScreenshotInProgress) {
15280
- throw new Error('截图正在进行中,请稍后再试');
15308
+ // 队列最多保留 1 个请求,避免积压
15309
+ if (this.screenshotQueue.length >= 1) {
15310
+ if (!this.options.silentMode) {
15311
+ console.log('📸 截图队列已满,跳过当前请求(等待队列处理)');
15312
+ }
15313
+ // 等待队列中的请求完成
15314
+ return new Promise((resolve, reject) => {
15315
+ const checkQueue = () => {
15316
+ if (!this.isScreenshotInProgress && this.screenshotQueue.length === 0) {
15317
+ // 队列已清空,重新尝试
15318
+ this.takeScreenshotWithModernScreenshot(element).then(resolve).catch(reject);
15319
+ }
15320
+ else {
15321
+ setTimeout(checkQueue, 100); // 100ms 后再次检查
15322
+ }
15323
+ };
15324
+ checkQueue();
15325
+ });
15326
+ }
15327
+ // 将请求加入队列
15328
+ return new Promise((resolve, reject) => {
15329
+ this.screenshotQueue.push({ resolve: () => {
15330
+ this.takeScreenshotWithModernScreenshot(element).then(resolve).catch(reject);
15331
+ }, reject });
15332
+ // 启动队列处理(如果还没启动)
15333
+ if (!this.isProcessingQueue) {
15334
+ this.processScreenshotQueue();
15335
+ }
15336
+ });
15281
15337
  }
15282
15338
  this.isScreenshotInProgress = true;
15283
15339
  if (!this.options.silentMode) {
@@ -15489,20 +15545,38 @@ class ScreenshotManager {
15489
15545
  if (rect.width === 0 || rect.height === 0) {
15490
15546
  throw new Error('元素尺寸为 0,无法截图');
15491
15547
  }
15492
- // 每次截图都重新创建 context,确保使用最新的元素状态
15493
- // 如果已有 context,先清理
15494
- if (this.screenshotContext) {
15495
- try {
15496
- destroyContext(this.screenshotContext);
15548
+ // Worker 数量配置:智能计算或使用用户配置
15549
+ // workerNumber > 0 会启用 Worker 模式,截图处理在后台线程执行,不会阻塞主线程 UI
15550
+ // 如果用户指定了 workerNumber,直接使用;否则根据设备性能自动计算
15551
+ let workerNumber;
15552
+ if (this.options.workerNumber !== undefined && this.options.workerNumber > 0) {
15553
+ // 用户明确指定了 workerNumber
15554
+ workerNumber = this.options.workerNumber;
15555
+ }
15556
+ else {
15557
+ // 自动计算 workerNumber
15558
+ const cpuCores = navigator.hardwareConcurrency || 4; // 默认假设 4 核
15559
+ if (isMobile || isLowEndDevice) {
15560
+ // 移动设备/低端设备:使用 1 个 Worker(避免内存压力)
15561
+ workerNumber = 1;
15497
15562
  }
15498
- catch (e) {
15499
- // 忽略清理错误
15563
+ else if (cpuCores >= 8) {
15564
+ // 高性能设备(8核及以上):使用 3-4 个 Worker(充分利用多核)
15565
+ // 但根据截图间隔调整:频繁截图(间隔 < 2秒)时使用更多 Worker
15566
+ const isFrequentScreenshot = this.options.interval < 2000;
15567
+ workerNumber = isFrequentScreenshot ? Math.min(4, Math.floor(cpuCores / 2)) : 3;
15568
+ }
15569
+ else if (cpuCores >= 4) {
15570
+ // 中等性能设备(4-7核):使用 2 个 Worker
15571
+ workerNumber = 2;
15572
+ }
15573
+ else {
15574
+ // 低性能设备(< 4核):使用 1 个 Worker
15575
+ workerNumber = 1;
15500
15576
  }
15501
- this.screenshotContext = null;
15502
15577
  }
15503
- // Worker 数量配置:移动设备/低端设备使用 1 Worker,桌面设备使用 2 个
15504
- // workerNumber > 0 会启用 Worker 模式,截图处理在后台线程执行,不会阻塞主线程 UI
15505
- const workerNumber = isMobile || isLowEndDevice ? 1 : 2;
15578
+ // 限制 workerNumber 范围:1-8(避免过多 Worker 导致资源竞争)
15579
+ workerNumber = Math.max(1, Math.min(8, workerNumber));
15506
15580
  // 构建 createContext 配置
15507
15581
  // 参考: https://github.com/qq15725/modern-screenshot/blob/main/src/options.ts
15508
15582
  const contextOptions = {
@@ -15519,7 +15593,28 @@ class ScreenshotManager {
15519
15593
  // 参考: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas#maximum_canvas_size
15520
15594
  // 大多数浏览器限制为 16,777,216 像素(4096x4096),这里设置为更保守的值
15521
15595
  maximumCanvasSize: 16777216, // 16M 像素(约 4096x4096)
15596
+ // 使用 modern-screenshot 内置的 timeout(更可靠)
15597
+ timeout: Math.max(this.options.interval * 6, 5000),
15522
15598
  };
15599
+ // 限制 timeout 最多 15 秒
15600
+ contextOptions.timeout = Math.min(contextOptions.timeout, 15000);
15601
+ // 如果用户指定了 workerUrl,使用指定的 URL
15602
+ // 否则让 modern-screenshot 自动处理(它会尝试从 node_modules 或 CDN 加载)
15603
+ // 注意:在某些构建工具(如 Rollup)中,可能需要手动指定 workerUrl
15604
+ if (this.options.workerUrl) {
15605
+ contextOptions.workerUrl = this.options.workerUrl;
15606
+ if (!this.options.silentMode) {
15607
+ console.log(`📸 使用指定的 Worker URL: ${this.options.workerUrl}`);
15608
+ }
15609
+ }
15610
+ else {
15611
+ // 未指定 workerUrl 时,modern-screenshot 会自动处理
15612
+ // 但在某些构建环境中可能需要手动指定,可以使用 CDN 作为后备
15613
+ // 这里不设置 workerUrl,让 modern-screenshot 自己处理
15614
+ if (!this.options.silentMode) {
15615
+ console.log('📸 Worker URL 未指定,modern-screenshot 将自动处理');
15616
+ }
15617
+ }
15523
15618
  // 对所有元素都设置尺寸限制(包括 document.body),避免截图过大
15524
15619
  // 这样可以减少 base64 大小,提高性能
15525
15620
  if (finalWidth && finalHeight) {
@@ -15548,69 +15643,99 @@ class ScreenshotManager {
15548
15643
  // 如果未指定 scale,移动设备默认使用 0.7
15549
15644
  contextOptions.scale = 0.7;
15550
15645
  }
15551
- // modern-screenshot 会自动处理 worker URL,不需要手动设置 workerUrl
15552
- // workerNumber > 0 时,截图处理会在 Worker 线程中执行,不会阻塞主线程 UI
15553
- // 创建 Worker 上下文(每次截图都创建新的,确保元素状态最新)
15554
- if (!this.options.silentMode) {
15555
- console.log(`📸 Worker 模式: ${workerNumber} 个 Worker,质量: ${finalQuality.toFixed(2)},缩放: ${contextOptions.scale || 1}`);
15556
- }
15557
- // 添加重试机制
15558
- let retries = 0;
15559
- const maxRetries = this.options.maxRetries || 2;
15560
- let screenshotContext = null;
15561
- while (retries <= maxRetries) {
15562
- try {
15563
- screenshotContext = await createContext$1(element, contextOptions);
15564
- this.screenshotContext = screenshotContext;
15565
- break;
15646
+ // 优化:复用 context,避免频繁创建和销毁(性能提升 20%+)
15647
+ // 只在元素变化或配置变化时重新创建 context
15648
+ const contextOptionsHash = JSON.stringify({
15649
+ workerNumber,
15650
+ quality: finalQuality,
15651
+ scale: contextOptions.scale,
15652
+ width: contextOptions.width,
15653
+ height: contextOptions.height,
15654
+ maximumCanvasSize: contextOptions.maximumCanvasSize,
15655
+ timeout: contextOptions.timeout
15656
+ });
15657
+ const needsRecreateContext = !this.screenshotContext ||
15658
+ this.contextElement !== element ||
15659
+ this.contextOptionsHash !== contextOptionsHash;
15660
+ if (needsRecreateContext) {
15661
+ if (!this.options.silentMode) {
15662
+ if (this.screenshotContext) {
15663
+ console.log('📸 检测到元素或配置变化,重新创建 context...');
15664
+ }
15665
+ else {
15666
+ console.log(`📸 Worker 模式: ${workerNumber} 个 Worker,质量: ${finalQuality.toFixed(2)},缩放: ${contextOptions.scale || 1}`);
15667
+ }
15566
15668
  }
15567
- catch (error) {
15568
- if (retries === maxRetries) {
15569
- throw new Error(`创建截图上下文失败(已重试 ${maxRetries} 次): ${error instanceof Error ? error.message : String(error)}`);
15669
+ // 销毁旧 context
15670
+ if (this.screenshotContext) {
15671
+ try {
15672
+ destroyContext(this.screenshotContext);
15570
15673
  }
15571
- retries++;
15572
- const delay = 1000 * retries; // 递增延迟:1秒、2秒...
15573
- if (!this.options.silentMode) {
15574
- console.warn(`📸 ⚠️ 创建截图上下文失败,${delay}ms 后重试 (${retries}/${maxRetries})...`);
15674
+ catch (e) {
15675
+ // 忽略清理错误
15676
+ }
15677
+ this.screenshotContext = null;
15678
+ }
15679
+ // 添加 progress 回调(可选,用于显示进度)
15680
+ if (!this.options.silentMode) {
15681
+ contextOptions.progress = (current, total) => {
15682
+ if (total > 0) {
15683
+ const percent = Math.round((current / total) * 100);
15684
+ if (percent % 25 === 0 || current === total) { // 每 25% 或完成时打印
15685
+ console.log(`📸 截图进度: ${current}/${total} (${percent}%)`);
15686
+ }
15687
+ }
15688
+ };
15689
+ }
15690
+ // 添加重试机制创建新 context
15691
+ let retries = 0;
15692
+ const maxRetries = this.options.maxRetries || 2;
15693
+ while (retries <= maxRetries) {
15694
+ try {
15695
+ this.screenshotContext = await createContext$1(element, contextOptions);
15696
+ this.contextElement = element;
15697
+ this.contextOptionsHash = contextOptionsHash;
15698
+ break;
15699
+ }
15700
+ catch (error) {
15701
+ if (retries === maxRetries) {
15702
+ throw new Error(`创建截图上下文失败(已重试 ${maxRetries} 次): ${error instanceof Error ? error.message : String(error)}`);
15703
+ }
15704
+ retries++;
15705
+ const delay = 1000 * retries; // 递增延迟:1秒、2秒...
15706
+ if (!this.options.silentMode) {
15707
+ console.warn(`📸 ⚠️ 创建截图上下文失败,${delay}ms 后重试 (${retries}/${maxRetries})...`);
15708
+ }
15709
+ await new Promise(resolve => setTimeout(resolve, delay));
15575
15710
  }
15576
- await new Promise(resolve => setTimeout(resolve, delay));
15711
+ }
15712
+ }
15713
+ else {
15714
+ if (!this.options.silentMode) {
15715
+ console.log('📸 复用现有 context(性能优化)');
15577
15716
  }
15578
15717
  }
15579
15718
  try {
15580
15719
  // 根据输出格式选择对应的 API,避免格式转换(性能优化)
15581
- // 添加超时机制,防止卡住(30秒超时)
15582
- const timeoutMs = 30000; // 30秒超时
15583
- const timeoutPromise = new Promise((_, reject) => {
15584
- setTimeout(() => {
15585
- reject(new Error(`截图超时(${timeoutMs}ms),可能页面过大或 Worker 处理时间过长`));
15586
- }, timeoutMs);
15587
- });
15720
+ // 注意:timeout 已经在 createContext 时设置,modern-screenshot 内部会处理超时
15588
15721
  let dataUrl;
15589
15722
  const outputFormat = this.options.outputFormat || 'webp';
15590
15723
  if (!this.options.silentMode) {
15591
15724
  console.log(`📸 使用 ${outputFormat.toUpperCase()} 格式截图(直接输出,无需转换)...`);
15592
15725
  }
15593
15726
  // 根据输出格式选择对应的 API
15727
+ // modern-screenshot 内部已经处理了超时,不需要额外的 Promise.race
15594
15728
  if (outputFormat === 'webp') {
15595
15729
  // 使用 domToWebp,直接输出 WebP 格式,无需转换
15596
- dataUrl = await Promise.race([
15597
- domToWebp(this.screenshotContext),
15598
- timeoutPromise
15599
- ]);
15730
+ dataUrl = await domToWebp(this.screenshotContext);
15600
15731
  }
15601
15732
  else if (outputFormat === 'jpeg') {
15602
15733
  // 使用 domToJpeg,直接输出 JPEG 格式,无需转换
15603
- dataUrl = await Promise.race([
15604
- domToJpeg(this.screenshotContext),
15605
- timeoutPromise
15606
- ]);
15734
+ dataUrl = await domToJpeg(this.screenshotContext);
15607
15735
  }
15608
15736
  else {
15609
15737
  // 默认使用 domToPng
15610
- dataUrl = await Promise.race([
15611
- domToPng(this.screenshotContext),
15612
- timeoutPromise
15613
- ]);
15738
+ dataUrl = await domToPng(this.screenshotContext);
15614
15739
  }
15615
15740
  // 验证截图结果
15616
15741
  if (!dataUrl || dataUrl.length < 100) {
@@ -15637,37 +15762,11 @@ class ScreenshotManager {
15637
15762
  throw error;
15638
15763
  }
15639
15764
  finally {
15640
- // 每次截图后立即清理 context,释放 Worker 和内存
15641
- // 这是防止内存泄漏的关键步骤
15642
- if (this.screenshotContext) {
15643
- try {
15644
- destroyContext(this.screenshotContext);
15645
- if (!this.options.silentMode) {
15646
- console.log('📸 ✅ modern-screenshot context 已清理');
15647
- }
15648
- }
15649
- catch (e) {
15650
- if (!this.options.silentMode) {
15651
- console.warn('📸 ⚠️ 清理 context 失败:', e);
15652
- }
15653
- }
15654
- finally {
15655
- // 确保 context 引用被清除
15656
- this.screenshotContext = null;
15657
- }
15658
- }
15765
+ // 优化:不复用 context 时才清理(性能优化)
15766
+ // 如果元素或配置没有变化,保留 context 以便下次复用
15767
+ // 这样可以避免频繁创建和销毁 Worker,提升性能 20%+
15659
15768
  // 释放截图锁
15660
15769
  this.isScreenshotInProgress = false;
15661
- // 强制触发垃圾回收(如果可能)
15662
- // 注意:这需要浏览器支持,不是所有浏览器都有效
15663
- if (typeof window !== 'undefined' && window.gc && typeof window.gc === 'function') {
15664
- try {
15665
- window.gc();
15666
- }
15667
- catch {
15668
- // 忽略 GC 错误
15669
- }
15670
- }
15671
15770
  }
15672
15771
  }
15673
15772
  catch (error) {
@@ -15683,7 +15782,34 @@ class ScreenshotManager {
15683
15782
  if (this.isScreenshotInProgress) {
15684
15783
  this.isScreenshotInProgress = false;
15685
15784
  }
15785
+ // 处理队列中的下一个请求
15786
+ this.processScreenshotQueue();
15787
+ }
15788
+ }
15789
+ /**
15790
+ * 处理截图队列
15791
+ */
15792
+ async processScreenshotQueue() {
15793
+ if (this.isProcessingQueue || this.screenshotQueue.length === 0) {
15794
+ return;
15795
+ }
15796
+ this.isProcessingQueue = true;
15797
+ while (this.screenshotQueue.length > 0 && !this.isScreenshotInProgress) {
15798
+ const task = this.screenshotQueue.shift();
15799
+ if (task) {
15800
+ try {
15801
+ task.resolve();
15802
+ // 等待当前截图完成
15803
+ while (this.isScreenshotInProgress) {
15804
+ await new Promise(resolve => setTimeout(resolve, 50));
15805
+ }
15806
+ }
15807
+ catch (error) {
15808
+ task.reject(error instanceof Error ? error : new Error(String(error)));
15809
+ }
15810
+ }
15686
15811
  }
15812
+ this.isProcessingQueue = false;
15687
15813
  }
15688
15814
  /**
15689
15815
  * 预连接代理服务器(优化网络性能)
@@ -16557,6 +16683,18 @@ class ScreenshotManager {
16557
16683
  * 清理资源
16558
16684
  */
16559
16685
  destroy() {
16686
+ // 清理 modern-screenshot context
16687
+ if (this.screenshotContext) {
16688
+ try {
16689
+ destroyContext(this.screenshotContext);
16690
+ }
16691
+ catch (e) {
16692
+ // 忽略清理错误
16693
+ }
16694
+ this.screenshotContext = null;
16695
+ this.contextElement = null;
16696
+ this.contextOptionsHash = '';
16697
+ }
16560
16698
  this.stopScreenshot();
16561
16699
  if (this.worker) {
16562
16700
  this.worker.terminate();