customer-chat-sdk 1.0.46 → 1.0.47

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.
@@ -270,7 +270,7 @@ export declare class ScreenshotManager {
270
270
  */
271
271
  private calculateCompressedSize;
272
272
  /**
273
- * 创建 WebWorker
273
+ * 创建 WebWorker(实现类似 TinyPNG 的智能压缩)
274
274
  */
275
275
  private createWorker;
276
276
  /**
@@ -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":"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"}
@@ -16697,25 +16697,219 @@ class ScreenshotManager {
16697
16697
  return { width, height };
16698
16698
  }
16699
16699
  /**
16700
- * 创建 WebWorker
16700
+ * 创建 WebWorker(实现类似 TinyPNG 的智能压缩)
16701
16701
  */
16702
16702
  createWorker() {
16703
- if (typeof Worker === 'undefined' || typeof OffscreenCanvas === 'undefined') {
16703
+ if (typeof Worker === 'undefined') {
16704
16704
  return null;
16705
16705
  }
16706
16706
  try {
16707
- // 简化的 Worker 代码(实际使用时需要完整实现)
16707
+ // 完整的 Worker 压缩代码(类似 TinyPNG 的智能压缩)
16708
16708
  const workerCode = `
16709
- self.onmessage = function(e) {
16709
+ // 图片压缩 Worker(类似 TinyPNG 的智能压缩)
16710
+ self.onmessage = async function(e) {
16710
16711
  const { type, data } = e.data;
16712
+
16711
16713
  if (type === 'COMPRESS_IMAGE') {
16712
- // 压缩逻辑(简化版)
16713
- self.postMessage({
16714
- type: 'SCREENSHOT_RESULT',
16715
- data: { compressed: { dataUrl: data.dataUrl } }
16716
- });
16714
+ try {
16715
+ const { dataUrl, maxWidth, maxHeight, quality, outputFormat } = data;
16716
+
16717
+ // 1. 加载图片
16718
+ const img = await loadImage(dataUrl);
16719
+
16720
+ // 获取图片尺寸(ImageBitmap 或 Image 都支持)
16721
+ const imgWidth = img.width || img.naturalWidth || 0;
16722
+ const imgHeight = img.height || img.naturalHeight || 0;
16723
+
16724
+ if (imgWidth === 0 || imgHeight === 0) {
16725
+ throw new Error('无法获取图片尺寸');
16726
+ }
16727
+
16728
+ // 2. 计算压缩后的尺寸(保持宽高比)
16729
+ const { width, height } = calculateSize(
16730
+ imgWidth,
16731
+ imgHeight,
16732
+ maxWidth || 1600,
16733
+ maxHeight || 900
16734
+ );
16735
+
16736
+ // 3. 创建 OffscreenCanvas(Worker 中必须使用 OffscreenCanvas)
16737
+ if (typeof OffscreenCanvas === 'undefined') {
16738
+ throw new Error('浏览器不支持 OffscreenCanvas,无法在 Worker 中压缩');
16739
+ }
16740
+
16741
+ const canvas = new OffscreenCanvas(width, height);
16742
+ const ctx = canvas.getContext('2d');
16743
+
16744
+ if (!ctx) {
16745
+ throw new Error('无法获取 Canvas 上下文');
16746
+ }
16747
+
16748
+ // 4. 优化绘制设置(提升压缩质量)
16749
+ ctx.imageSmoothingEnabled = true;
16750
+ ctx.imageSmoothingQuality = 'high';
16751
+
16752
+ // 5. 使用白色背景(避免透明区域压缩问题)
16753
+ ctx.fillStyle = '#ffffff';
16754
+ ctx.fillRect(0, 0, width, height);
16755
+
16756
+ // 6. 绘制图片(高质量缩放)
16757
+ ctx.drawImage(img, 0, 0, width, height);
16758
+
16759
+ // 7. 转换为目标格式(智能质量调整)
16760
+ // OffscreenCanvas 使用 convertToBlob 而不是 toDataURL
16761
+ let compressedDataUrl;
16762
+ const finalQuality = Math.max(0.1, Math.min(0.95, quality || 0.15));
16763
+
16764
+ // 辅助函数:将 Blob 转换为 data URL
16765
+ async function blobToDataURL(blob) {
16766
+ return new Promise((resolve, reject) => {
16767
+ const reader = new FileReader();
16768
+ reader.onload = () => resolve(reader.result);
16769
+ reader.onerror = reject;
16770
+ reader.readAsDataURL(blob);
16771
+ });
16772
+ }
16773
+
16774
+ // 确定 MIME 类型
16775
+ let mimeType = 'image/png';
16776
+ if (outputFormat === 'webp') {
16777
+ mimeType = 'image/webp';
16778
+ } else if (outputFormat === 'jpeg') {
16779
+ mimeType = 'image/jpeg';
16780
+ }
16781
+
16782
+ // 第一次压缩
16783
+ let blob = await canvas.convertToBlob({
16784
+ type: mimeType,
16785
+ quality: finalQuality
16786
+ });
16787
+
16788
+ compressedDataUrl = await blobToDataURL(blob);
16789
+
16790
+ // 8. 如果压缩后文件仍然较大,尝试进一步降低质量
16791
+ const originalSize = dataUrl.length;
16792
+ let compressedSize = compressedDataUrl.length;
16793
+
16794
+ // 如果压缩后大小减少不足 20%,尝试更激进的压缩
16795
+ if (compressedSize > originalSize * 0.8 && finalQuality > 0.1 && (outputFormat === 'webp' || outputFormat === 'jpeg')) {
16796
+ const aggressiveQuality = Math.max(0.1, finalQuality * 0.7);
16797
+
16798
+ try {
16799
+ const moreCompressedBlob = await canvas.convertToBlob({
16800
+ type: mimeType,
16801
+ quality: aggressiveQuality
16802
+ });
16803
+
16804
+ const moreCompressedDataUrl = await blobToDataURL(moreCompressedBlob);
16805
+
16806
+ // 如果更激进的压缩效果更好,使用它
16807
+ if (moreCompressedDataUrl.length < compressedDataUrl.length) {
16808
+ compressedDataUrl = moreCompressedDataUrl;
16809
+ compressedSize = compressedDataUrl.length;
16810
+ }
16811
+ } catch (e) {
16812
+ // 忽略错误,使用之前的压缩结果
16813
+ }
16814
+ }
16815
+
16816
+ // 9. 返回压缩结果
16817
+ self.postMessage({
16818
+ type: 'SCREENSHOT_RESULT',
16819
+ data: {
16820
+ compressed: {
16821
+ dataUrl: compressedDataUrl,
16822
+ originalSize: originalSize,
16823
+ compressedSize: compressedDataUrl.length,
16824
+ compressionRatio: ((1 - compressedDataUrl.length / originalSize) * 100).toFixed(1)
16825
+ }
16826
+ }
16827
+ });
16828
+ } catch (error) {
16829
+ // 压缩失败,返回原始数据
16830
+ self.postMessage({
16831
+ type: 'SCREENSHOT_RESULT',
16832
+ data: {
16833
+ compressed: {
16834
+ dataUrl: data.dataUrl,
16835
+ error: error.message
16836
+ }
16837
+ }
16838
+ });
16839
+ }
16717
16840
  }
16718
16841
  };
16842
+
16843
+ // 加载图片的辅助函数(Worker 中使用 createImageBitmap)
16844
+ async function loadImage(dataUrl) {
16845
+ try {
16846
+ // 将 data URL 转换为 Blob
16847
+ let blob;
16848
+ if (typeof fetch !== 'undefined') {
16849
+ try {
16850
+ const response = await fetch(dataUrl);
16851
+ blob = await response.blob();
16852
+ } catch (e) {
16853
+ // fetch 失败,手动转换 data URL 到 Blob
16854
+ blob = dataURLToBlob(dataUrl);
16855
+ }
16856
+ } else {
16857
+ // 没有 fetch,手动转换
16858
+ blob = dataURLToBlob(dataUrl);
16859
+ }
16860
+
16861
+ // 使用 createImageBitmap 创建图片(Worker 中推荐方式)
16862
+ if (typeof createImageBitmap !== 'undefined') {
16863
+ return await createImageBitmap(blob);
16864
+ } else {
16865
+ // 回退方案:使用 Image(某些 Worker 环境可能支持)
16866
+ return new Promise((resolve, reject) => {
16867
+ const img = new Image();
16868
+ img.onload = () => resolve(img);
16869
+ img.onerror = reject;
16870
+ img.src = dataUrl;
16871
+ });
16872
+ }
16873
+ } catch (error) {
16874
+ // 如果 createImageBitmap 失败,尝试直接使用 Image
16875
+ return new Promise((resolve, reject) => {
16876
+ const img = new Image();
16877
+ img.onload = () => resolve(img);
16878
+ img.onerror = () => reject(error);
16879
+ img.src = dataUrl;
16880
+ });
16881
+ }
16882
+ }
16883
+
16884
+ // 将 data URL 转换为 Blob 的辅助函数
16885
+ function dataURLToBlob(dataUrl) {
16886
+ const arr = dataUrl.split(',');
16887
+ const mimeMatch = arr[0].match(/:(.*?);/);
16888
+ const mime = mimeMatch ? mimeMatch[1] : 'image/png';
16889
+ const bstr = atob(arr[1]);
16890
+ let n = bstr.length;
16891
+ const u8arr = new Uint8Array(n);
16892
+ while (n--) {
16893
+ u8arr[n] = bstr.charCodeAt(n);
16894
+ }
16895
+ return new Blob([u8arr], { type: mime });
16896
+ }
16897
+
16898
+ // 计算压缩后尺寸的辅助函数(保持宽高比)
16899
+ function calculateSize(originalWidth, originalHeight, maxWidth, maxHeight) {
16900
+ let width = originalWidth;
16901
+ let height = originalHeight;
16902
+
16903
+ if (width > maxWidth || height > maxHeight) {
16904
+ const widthRatio = maxWidth / width;
16905
+ const heightRatio = maxHeight / height;
16906
+ const ratio = Math.min(widthRatio, heightRatio);
16907
+ width = Math.round(width * ratio);
16908
+ height = Math.round(height * ratio);
16909
+ }
16910
+
16911
+ return { width, height };
16912
+ }
16719
16913
  `;
16720
16914
  const blob = new Blob([workerCode], { type: 'application/javascript' });
16721
16915
  const workerUrl = URL.createObjectURL(blob);
@@ -16723,17 +16917,34 @@ class ScreenshotManager {
16723
16917
  newWorker.onmessage = (e) => {
16724
16918
  const { type, data } = e.data;
16725
16919
  if (type === 'SCREENSHOT_RESULT' && data?.compressed) {
16920
+ const compressed = data.compressed;
16921
+ // 更新截图历史记录
16726
16922
  if (this.screenshotHistory.length > 0) {
16727
- this.screenshotHistory[this.screenshotHistory.length - 1] = data.compressed.dataUrl;
16923
+ this.screenshotHistory[this.screenshotHistory.length - 1] = compressed.dataUrl;
16924
+ // 打印压缩统计信息
16925
+ if (!this.options.silentMode && compressed.originalSize && compressed.compressedSize) {
16926
+ const originalKB = (compressed.originalSize * 0.75 / 1024).toFixed(2);
16927
+ const compressedKB = (compressed.compressedSize * 0.75 / 1024).toFixed(2);
16928
+ const ratio = compressed.compressionRatio || '0';
16929
+ console.log('📸 [Worker 压缩] ✅ 压缩完成');
16930
+ console.log(` 原始大小: ${originalKB} KB`);
16931
+ console.log(` 压缩后: ${compressedKB} KB`);
16932
+ console.log(` 压缩率: ${ratio}%`);
16933
+ if (compressed.error) {
16934
+ console.warn(` ⚠️ 压缩警告: ${compressed.error}`);
16935
+ }
16936
+ }
16728
16937
  }
16729
16938
  }
16730
16939
  };
16731
16940
  newWorker.onerror = (e) => {
16732
16941
  console.error('📸 WebWorker 错误:', e);
16942
+ if (!this.options.silentMode) {
16943
+ console.warn('📸 Worker 压缩失败,使用原始截图');
16944
+ }
16733
16945
  };
16734
16946
  // 注意:不要立即 revokeObjectURL,因为 Worker 需要这个 URL 保持有效
16735
16947
  // 在 destroy() 方法中清理 Worker 时再 revoke
16736
- // URL.revokeObjectURL(workerUrl) // 已移除,在 destroy 时清理
16737
16948
  return newWorker;
16738
16949
  }
16739
16950
  catch (err) {
@@ -16693,25 +16693,219 @@ class ScreenshotManager {
16693
16693
  return { width, height };
16694
16694
  }
16695
16695
  /**
16696
- * 创建 WebWorker
16696
+ * 创建 WebWorker(实现类似 TinyPNG 的智能压缩)
16697
16697
  */
16698
16698
  createWorker() {
16699
- if (typeof Worker === 'undefined' || typeof OffscreenCanvas === 'undefined') {
16699
+ if (typeof Worker === 'undefined') {
16700
16700
  return null;
16701
16701
  }
16702
16702
  try {
16703
- // 简化的 Worker 代码(实际使用时需要完整实现)
16703
+ // 完整的 Worker 压缩代码(类似 TinyPNG 的智能压缩)
16704
16704
  const workerCode = `
16705
- self.onmessage = function(e) {
16705
+ // 图片压缩 Worker(类似 TinyPNG 的智能压缩)
16706
+ self.onmessage = async function(e) {
16706
16707
  const { type, data } = e.data;
16708
+
16707
16709
  if (type === 'COMPRESS_IMAGE') {
16708
- // 压缩逻辑(简化版)
16709
- self.postMessage({
16710
- type: 'SCREENSHOT_RESULT',
16711
- data: { compressed: { dataUrl: data.dataUrl } }
16712
- });
16710
+ try {
16711
+ const { dataUrl, maxWidth, maxHeight, quality, outputFormat } = data;
16712
+
16713
+ // 1. 加载图片
16714
+ const img = await loadImage(dataUrl);
16715
+
16716
+ // 获取图片尺寸(ImageBitmap 或 Image 都支持)
16717
+ const imgWidth = img.width || img.naturalWidth || 0;
16718
+ const imgHeight = img.height || img.naturalHeight || 0;
16719
+
16720
+ if (imgWidth === 0 || imgHeight === 0) {
16721
+ throw new Error('无法获取图片尺寸');
16722
+ }
16723
+
16724
+ // 2. 计算压缩后的尺寸(保持宽高比)
16725
+ const { width, height } = calculateSize(
16726
+ imgWidth,
16727
+ imgHeight,
16728
+ maxWidth || 1600,
16729
+ maxHeight || 900
16730
+ );
16731
+
16732
+ // 3. 创建 OffscreenCanvas(Worker 中必须使用 OffscreenCanvas)
16733
+ if (typeof OffscreenCanvas === 'undefined') {
16734
+ throw new Error('浏览器不支持 OffscreenCanvas,无法在 Worker 中压缩');
16735
+ }
16736
+
16737
+ const canvas = new OffscreenCanvas(width, height);
16738
+ const ctx = canvas.getContext('2d');
16739
+
16740
+ if (!ctx) {
16741
+ throw new Error('无法获取 Canvas 上下文');
16742
+ }
16743
+
16744
+ // 4. 优化绘制设置(提升压缩质量)
16745
+ ctx.imageSmoothingEnabled = true;
16746
+ ctx.imageSmoothingQuality = 'high';
16747
+
16748
+ // 5. 使用白色背景(避免透明区域压缩问题)
16749
+ ctx.fillStyle = '#ffffff';
16750
+ ctx.fillRect(0, 0, width, height);
16751
+
16752
+ // 6. 绘制图片(高质量缩放)
16753
+ ctx.drawImage(img, 0, 0, width, height);
16754
+
16755
+ // 7. 转换为目标格式(智能质量调整)
16756
+ // OffscreenCanvas 使用 convertToBlob 而不是 toDataURL
16757
+ let compressedDataUrl;
16758
+ const finalQuality = Math.max(0.1, Math.min(0.95, quality || 0.15));
16759
+
16760
+ // 辅助函数:将 Blob 转换为 data URL
16761
+ async function blobToDataURL(blob) {
16762
+ return new Promise((resolve, reject) => {
16763
+ const reader = new FileReader();
16764
+ reader.onload = () => resolve(reader.result);
16765
+ reader.onerror = reject;
16766
+ reader.readAsDataURL(blob);
16767
+ });
16768
+ }
16769
+
16770
+ // 确定 MIME 类型
16771
+ let mimeType = 'image/png';
16772
+ if (outputFormat === 'webp') {
16773
+ mimeType = 'image/webp';
16774
+ } else if (outputFormat === 'jpeg') {
16775
+ mimeType = 'image/jpeg';
16776
+ }
16777
+
16778
+ // 第一次压缩
16779
+ let blob = await canvas.convertToBlob({
16780
+ type: mimeType,
16781
+ quality: finalQuality
16782
+ });
16783
+
16784
+ compressedDataUrl = await blobToDataURL(blob);
16785
+
16786
+ // 8. 如果压缩后文件仍然较大,尝试进一步降低质量
16787
+ const originalSize = dataUrl.length;
16788
+ let compressedSize = compressedDataUrl.length;
16789
+
16790
+ // 如果压缩后大小减少不足 20%,尝试更激进的压缩
16791
+ if (compressedSize > originalSize * 0.8 && finalQuality > 0.1 && (outputFormat === 'webp' || outputFormat === 'jpeg')) {
16792
+ const aggressiveQuality = Math.max(0.1, finalQuality * 0.7);
16793
+
16794
+ try {
16795
+ const moreCompressedBlob = await canvas.convertToBlob({
16796
+ type: mimeType,
16797
+ quality: aggressiveQuality
16798
+ });
16799
+
16800
+ const moreCompressedDataUrl = await blobToDataURL(moreCompressedBlob);
16801
+
16802
+ // 如果更激进的压缩效果更好,使用它
16803
+ if (moreCompressedDataUrl.length < compressedDataUrl.length) {
16804
+ compressedDataUrl = moreCompressedDataUrl;
16805
+ compressedSize = compressedDataUrl.length;
16806
+ }
16807
+ } catch (e) {
16808
+ // 忽略错误,使用之前的压缩结果
16809
+ }
16810
+ }
16811
+
16812
+ // 9. 返回压缩结果
16813
+ self.postMessage({
16814
+ type: 'SCREENSHOT_RESULT',
16815
+ data: {
16816
+ compressed: {
16817
+ dataUrl: compressedDataUrl,
16818
+ originalSize: originalSize,
16819
+ compressedSize: compressedDataUrl.length,
16820
+ compressionRatio: ((1 - compressedDataUrl.length / originalSize) * 100).toFixed(1)
16821
+ }
16822
+ }
16823
+ });
16824
+ } catch (error) {
16825
+ // 压缩失败,返回原始数据
16826
+ self.postMessage({
16827
+ type: 'SCREENSHOT_RESULT',
16828
+ data: {
16829
+ compressed: {
16830
+ dataUrl: data.dataUrl,
16831
+ error: error.message
16832
+ }
16833
+ }
16834
+ });
16835
+ }
16713
16836
  }
16714
16837
  };
16838
+
16839
+ // 加载图片的辅助函数(Worker 中使用 createImageBitmap)
16840
+ async function loadImage(dataUrl) {
16841
+ try {
16842
+ // 将 data URL 转换为 Blob
16843
+ let blob;
16844
+ if (typeof fetch !== 'undefined') {
16845
+ try {
16846
+ const response = await fetch(dataUrl);
16847
+ blob = await response.blob();
16848
+ } catch (e) {
16849
+ // fetch 失败,手动转换 data URL 到 Blob
16850
+ blob = dataURLToBlob(dataUrl);
16851
+ }
16852
+ } else {
16853
+ // 没有 fetch,手动转换
16854
+ blob = dataURLToBlob(dataUrl);
16855
+ }
16856
+
16857
+ // 使用 createImageBitmap 创建图片(Worker 中推荐方式)
16858
+ if (typeof createImageBitmap !== 'undefined') {
16859
+ return await createImageBitmap(blob);
16860
+ } else {
16861
+ // 回退方案:使用 Image(某些 Worker 环境可能支持)
16862
+ return new Promise((resolve, reject) => {
16863
+ const img = new Image();
16864
+ img.onload = () => resolve(img);
16865
+ img.onerror = reject;
16866
+ img.src = dataUrl;
16867
+ });
16868
+ }
16869
+ } catch (error) {
16870
+ // 如果 createImageBitmap 失败,尝试直接使用 Image
16871
+ return new Promise((resolve, reject) => {
16872
+ const img = new Image();
16873
+ img.onload = () => resolve(img);
16874
+ img.onerror = () => reject(error);
16875
+ img.src = dataUrl;
16876
+ });
16877
+ }
16878
+ }
16879
+
16880
+ // 将 data URL 转换为 Blob 的辅助函数
16881
+ function dataURLToBlob(dataUrl) {
16882
+ const arr = dataUrl.split(',');
16883
+ const mimeMatch = arr[0].match(/:(.*?);/);
16884
+ const mime = mimeMatch ? mimeMatch[1] : 'image/png';
16885
+ const bstr = atob(arr[1]);
16886
+ let n = bstr.length;
16887
+ const u8arr = new Uint8Array(n);
16888
+ while (n--) {
16889
+ u8arr[n] = bstr.charCodeAt(n);
16890
+ }
16891
+ return new Blob([u8arr], { type: mime });
16892
+ }
16893
+
16894
+ // 计算压缩后尺寸的辅助函数(保持宽高比)
16895
+ function calculateSize(originalWidth, originalHeight, maxWidth, maxHeight) {
16896
+ let width = originalWidth;
16897
+ let height = originalHeight;
16898
+
16899
+ if (width > maxWidth || height > maxHeight) {
16900
+ const widthRatio = maxWidth / width;
16901
+ const heightRatio = maxHeight / height;
16902
+ const ratio = Math.min(widthRatio, heightRatio);
16903
+ width = Math.round(width * ratio);
16904
+ height = Math.round(height * ratio);
16905
+ }
16906
+
16907
+ return { width, height };
16908
+ }
16715
16909
  `;
16716
16910
  const blob = new Blob([workerCode], { type: 'application/javascript' });
16717
16911
  const workerUrl = URL.createObjectURL(blob);
@@ -16719,17 +16913,34 @@ class ScreenshotManager {
16719
16913
  newWorker.onmessage = (e) => {
16720
16914
  const { type, data } = e.data;
16721
16915
  if (type === 'SCREENSHOT_RESULT' && data?.compressed) {
16916
+ const compressed = data.compressed;
16917
+ // 更新截图历史记录
16722
16918
  if (this.screenshotHistory.length > 0) {
16723
- this.screenshotHistory[this.screenshotHistory.length - 1] = data.compressed.dataUrl;
16919
+ this.screenshotHistory[this.screenshotHistory.length - 1] = compressed.dataUrl;
16920
+ // 打印压缩统计信息
16921
+ if (!this.options.silentMode && compressed.originalSize && compressed.compressedSize) {
16922
+ const originalKB = (compressed.originalSize * 0.75 / 1024).toFixed(2);
16923
+ const compressedKB = (compressed.compressedSize * 0.75 / 1024).toFixed(2);
16924
+ const ratio = compressed.compressionRatio || '0';
16925
+ console.log('📸 [Worker 压缩] ✅ 压缩完成');
16926
+ console.log(` 原始大小: ${originalKB} KB`);
16927
+ console.log(` 压缩后: ${compressedKB} KB`);
16928
+ console.log(` 压缩率: ${ratio}%`);
16929
+ if (compressed.error) {
16930
+ console.warn(` ⚠️ 压缩警告: ${compressed.error}`);
16931
+ }
16932
+ }
16724
16933
  }
16725
16934
  }
16726
16935
  };
16727
16936
  newWorker.onerror = (e) => {
16728
16937
  console.error('📸 WebWorker 错误:', e);
16938
+ if (!this.options.silentMode) {
16939
+ console.warn('📸 Worker 压缩失败,使用原始截图');
16940
+ }
16729
16941
  };
16730
16942
  // 注意:不要立即 revokeObjectURL,因为 Worker 需要这个 URL 保持有效
16731
16943
  // 在 destroy() 方法中清理 Worker 时再 revoke
16732
- // URL.revokeObjectURL(workerUrl) // 已移除,在 destroy 时清理
16733
16944
  return newWorker;
16734
16945
  }
16735
16946
  catch (err) {