Browse Source

feat: 云教练加载时长优化

TIANYONG 2 weeks ago
parent
commit
ac5a0d1aaa

+ 20 - 5
src/helpers/formateMusic.ts

@@ -15,6 +15,7 @@ import {
 } from "/osmd-extended/src";
 import { GradualChange, speedInfo } from "./calcSpeed";
 import { beatUnitTo, speedBeatTo } from "/src/helpers/beatConfig"
+import { xmlDocRef } from "/src/view/music-score"
 
 const browserInfo = browser();
 dayjs.extend(duration);
@@ -251,12 +252,15 @@ export type CustomInfo = {
 };
 
 /** 从xml中获取自定义信息,并删除多余的字符串 */
-export const getCustomInfo = (xml: string): CustomInfo => {
+export const getCustomInfo = (xml: string, resourceType?: string): CustomInfo => {
 	const data = {
 		showSpeed: true,
 		parsedXML: xml,
 	};
-	const xmlParse = new DOMParser().parseFromString(xml, "text/xml");
+	//console.time('解析xml 耗时3')
+	// const xmlParse = new DOMParser().parseFromString(xml, "text/xml");
+	const xmlParse = xmlDocRef.value && resourceType === 'init' ? xmlDocRef.value : new DOMParser().parseFromString(xml, "text/xml");
+	//console.timeEnd('解析xml 耗时3')
 	const words: any = xmlParse.getElementsByTagName("words");
 	for (const word of words) {
 		if (word && word.textContent?.trim() === "隐藏速度") {
@@ -366,7 +370,10 @@ export const onlyVisible = (xml: string, partIndex: number, resourceType?: strin
 	if (!xml) return "";
 	// console.log('原始xml')
 	const detailId = state.examSongId + "";
-	const xmlParse = new DOMParser().parseFromString(xml, "text/xml");
+	//console.time('解析xml 耗时4')
+	// const xmlParse = new DOMParser().parseFromString(xml, "text/xml");
+	const xmlParse = xmlDocRef.value && !resourceType ? xmlDocRef.value : new DOMParser().parseFromString(xml, "text/xml");
+	//console.timeEnd('解析xml 耗时4')
 	const partList = xmlParse.getElementsByTagName("part-list")?.[0]?.getElementsByTagName("score-part") || [];
 	const partListNames = Array.from(partList).map((item) => item.getElementsByTagName("part-name")?.[0]?.textContent?.trim() || "");
 	const parts: any = xmlParse.getElementsByTagName("part");
@@ -538,7 +545,9 @@ export const onlyVisible2 = (xml: string): string => {
 	if (!xml) return "";
 	// console.log('原始xml')
 	//const detailId = state.examSongId + "";
+	console.time('解析xml 耗时5')
 	const xmlParse = new DOMParser().parseFromString(xml, "text/xml");
+	console.timeEnd('解析xml 耗时5')
 	const partList = xmlParse.getElementsByTagName("part-list")?.[0]?.getElementsByTagName("score-part") || [];
 	//const partListNames = Array.from(partList).map((item) => item.getElementsByTagName("part-name")?.[0]?.textContent?.trim() || "");
 	//state.partListNames = partListNames;
@@ -631,7 +640,9 @@ export const formatZoom = (num = 1) => {
 /** 妙极客多分轨的曲子,可能没有part-name标签,需要手动加上该标签 */
 export const xmlAddPartName = (xml: string) => {
 	if (!xml) return "";
+	console.time('解析xml 耗时')
 	const xmlParse = new DOMParser().parseFromString(xml, "text/xml");
+	console.timeEnd('解析xml 耗时')
 	const scoreParts = Array.from(xmlParse.getElementsByTagName("score-part"));
 	// 曲子:1795013313499971585,有两条声轨,声轨1有name,声轨2没有name,需要判断是否都没有name,才把evxmlAddPartName赋值为true
 	state.evxmlAddPartName = scoreParts.every(item => item.getElementsByTagName("part-name").length === 0);
@@ -647,15 +658,19 @@ export const xmlAddPartName = (xml: string) => {
 			scorePart.getElementsByTagName("part-name")[0].textContent = scorePart.getAttribute("id") || "";
 		}
 	}
+	xmlDocRef.value = xmlParse;
 	return new XMLSerializer().serializeToString(xmlParse);
 }
 
 /** 格式化曲谱
  * 1.全休止符的小节,没有音符默认加个全休止符
  */
-export const formatXML = (xml: string, xmlUrl?: string): string => {
+export const formatXML = (xml: string, xmlUrl?: string, resourceType?: string): string => {
 	if (!xml) return "";
-	const xmlParse = new DOMParser().parseFromString(xml, "text/xml");
+	//console.time('解析xml 耗时7')
+	// const xmlParse = new DOMParser().parseFromString(xml, "text/xml");
+	const xmlParse = xmlDocRef.value && resourceType === 'init' ? xmlDocRef.value : new DOMParser().parseFromString(xml, "text/xml");
+	//console.timeEnd('解析xml 耗时7')
 
 	// 声调
 	const fifths = xmlParse.getElementsByTagName("fifths");

+ 51 - 0
src/page-instrument/App.tsx

@@ -124,6 +124,57 @@ export default defineComponent({
       //     event.preventDefault();
       //   }
       // });
+
+      /**
+       * DNS 解析耗时: domainLookupEnd - domainLookupStart
+       * TCP 连接耗时: connectEnd - connectStart
+       * SSL 安全连接耗时: connectEnd - secureConnectionStart
+       * 网络请求耗时 (TTFB): responseStart - requestStart
+       *数据传输耗时: responseEnd - responseStart
+        *DOM 解析耗时: domInteractive - responseEnd
+        *资源加载耗时: loadEventStart - domContentLoadedEventEnd
+        *First Byte时间: responseStart - domainLookupStart
+        *白屏时间: responseEnd - fetchStart
+        *首次可交互时间: domInteractive - fetchStart
+        *DOM Ready 时间: domContentLoadEventEnd - fetchStart
+        *页面完全加载时间: loadEventStart - fetchStart
+        *http 头部大小: transferSize - encodedBodySize
+        *重定向次数:performance.navigation.redirectCount
+        *重定向耗时: redirectEnd - redirectStart
+       */
+      window.onload = function() {
+        console.log('加载完成')
+        let timing: any = performance.getEntriesByType('navigation')[0] || {};
+        const { domainLookupEnd, domainLookupStart, connectEnd, connectStart, responseStart,
+          requestStart, responseEnd, domInteractive, loadEventStart, domContentLoadedEventEnd,
+          fetchStart, secureConnectionStart, transferSize, encodedBodySize,
+          redirectEnd, redirectStart
+        } = timing
+        //console.log(timing.domInteractive);
+        //console.log(timing.fetchStart);
+        let diff = timing.domInteractive - timing.fetchStart;
+        //console.log("TTI: " + diff);
+        const timeInfo = [
+          { '类型': 'DNS 解析耗时', '耗时': domainLookupEnd - domainLookupStart},
+          { '类型': 'TCP 连接耗时', '耗时': connectEnd - connectStart},
+          { '类型': 'SSL 安全连接耗时', '耗时': connectEnd - secureConnectionStart},
+          { '类型': '网络请求耗时', '耗时': responseStart - requestStart},
+          { '类型': '数据传输耗时', '耗时': responseEnd - responseStart},
+          { '类型': 'DOM 解析耗时', '耗时': domInteractive - responseEnd},
+          { '类型': '资源加载耗时', '耗时': loadEventStart - domContentLoadedEventEnd},
+          { '类型': 'First Byte时间', '耗时': responseStart - domainLookupStart},
+          { '类型': '白屏时间', '耗时': responseEnd - fetchStart},
+          { '类型': '首次可交互时间', '耗时': domInteractive - fetchStart},
+          { '类型': 'DOM Ready 时间', '耗时': domContentLoadedEventEnd - fetchStart},
+          { '类型': '页面完全加载时间', '耗时': loadEventStart - fetchStart},
+          { '类型': 'http 头部大小', '耗时': transferSize - encodedBodySize},
+          { '类型': '重定向次数', '耗时': performance.navigation.redirectCount},
+          { '类型': '重定向耗时', '耗时': redirectEnd - redirectStart},
+        ];
+        console.table(timeInfo);
+        
+      };       
+
     });
 
     onUnmounted(() => {

+ 6 - 2
src/page-instrument/view-detail/index.tsx

@@ -118,6 +118,7 @@ export default defineComponent({
         }
       }
     };
+    console.time('加载过程')
     onBeforeMount(async () => {
       // console.time("渲染加载耗时");
       api_keepScreenLongLight();
@@ -210,8 +211,10 @@ export default defineComponent({
 
     /** 渲染完成 */
     const handleRendered = (osmd: any) => {
+      state.isLoading = false
       api_cloudLoading();
       console.timeEnd("渲染加载耗时");
+      console.timeLog('加载过程','谱面渲染完成')
       detailData.skeletonLoading = false;
       state.osmd = osmd;
       // 预览模式不需要往下执行
@@ -689,8 +692,9 @@ export default defineComponent({
             </div>
           }
         </div>
-        {/* 曲目渲染完成,再去下载mp3资源 */}
-        {!detailData.isLoading && !detailData.skeletonLoading && <AudioList />}
+        {/* 曲目渲染时,同步下载mp3资源 */}
+        {!detailData.isLoading && <AudioList />}
+        {/* {!detailData.isLoading && !detailData.skeletonLoading && <AudioList />} */}
 
         {/* {!detailData.isLoading && <TheAudio src={tickWav} />} */}
 

+ 35 - 7
src/state.ts

@@ -17,12 +17,13 @@ import { followData, skipNotePractice } from "/src/view/follow-practice/index"
 import { changeSongSourceByBeat } from "/src/view/audio-list"
 import { moveSmoothAnimation, smoothAnimationState, moveSmoothAnimationByPlayTime, moveTranslateXNum, destroySmoothAnimation, calcClientWidth } from "/src/page-instrument/view-detail/smoothAnimation"
 import { storeData } from "/src/store";
-import { downloadXmlStr } from "./view/music-score"
+import { downloadXmlStr, xmlDocRef } from "./view/music-score"
 import { musicScoreRef, headerColumnHide } from "/src/page-instrument/view-detail/index"
 import { headTopData } from "/src/page-instrument/header-top/index";
 import { api_lessonTrainingTrainingStudentDetail } from "/src/page-instrument/api"
 import { undoData, moveData } from "/src/view/plugins/move-music-score"
 import { speedBeatTo, unitImgs } from "/src/helpers/beatConfig"
+import IndexedDBService from "/src/utils/indexedDB";
 import { musicalInstrumentCodeInfo, instruments, fixInstrumentNameCode } from "/src/constant/instruments";
 
 const query: any = getQuery();
@@ -339,7 +340,7 @@ const state = reactive({
   // 加载条
   isLoading: true,
   /** 加载中的文案 */
-  loadingText: '音频资源加载中,请稍后…',
+  loadingText: '资源加载中,请稍后…',
   /** 是否是简单的单行谱模式页面 */
   isSimplePage: false, 
   /** xml的速度和后台设置的速度,计算出的基础音频播放倍率 */
@@ -377,6 +378,8 @@ const state = reactive({
   showWorkDonePop: false, // 显示需要提交练习作业弹窗
   /** 顶部栏高度 */
   headTopHeight: 0,
+  /** 是否是来源于缓存的xml */
+  xmlFromStore: false,
 });
 const browserInfo = browser();
 let offset_duration = 0;
@@ -624,7 +627,7 @@ export const skipNotePlay = async (itemIndex: number, isStart = false) => {
 export const togglePlay = async (playState: "play" | "paused", isForceCLoseToast?:boolean) => {
   // 如果mp3资源还在加载中,给出提示
   if (!state.isAppPlay && !state.audioDone) {
-    if (!isForceCLoseToast) showToast('音频资源加载中,请稍后')
+    if (!isForceCLoseToast) showToast('资源加载中,请稍后')
     return
   }
   // 播放之前  当为评测模式和不为MIDI时候按  是否禁用节拍器  切换音源
@@ -1216,6 +1219,25 @@ const initInstrumentCode = async () => {
   // console.log('声轨codes',instruments)
 }
 
+// 判断有没有xml缓存,有则直接使用
+const queryMusicXml = async (id: string, xmlUr: string) => {
+  let xmlString = ''
+  const dbService = new IndexedDBService("MyDatabase", "MyStore");
+  console.time('缓存获取xml')
+  const storeXmlData: any = await dbService.get(id).then((data) => data );
+  if (storeXmlData && storeXmlData.xmlString) {
+    xmlString = storeXmlData && storeXmlData.xmlString
+    state.xmlFromStore = true;
+    console.timeEnd('缓存获取xml')
+    // 使用完后删除数据
+    dbService.delete(id)
+  } else {
+    state.xmlFromStore = false;
+    xmlString = await fetch(xmlUr).then((response) => response.text());
+  }
+  return xmlString;
+}
+
 const getMusicInfo = async (res: any) => {
   try {
     await initInstrumentCode()
@@ -1227,7 +1249,8 @@ const getMusicInfo = async (res: any) => {
   // 是否默认显示总谱
   state.defaultScoreRender = res.data?.defaultScoreRender
   /* 获取声轨列表 */
-  let xmlString = await fetch(res.data.xmlFileUrl).then((response) => response.text());
+  // let xmlString = await fetch(res.data.xmlFileUrl).then((response) => response.text());
+  let xmlString: string = await queryMusicXml(res.data.bizId + "", res.data.xmlFileUrl);
   xmlString = xmlAddPartName(xmlString);
   downloadXmlStr.value = xmlString //给musice-score 赋值xmlString 以免加载2次
   const tracks = xmlToTracks(xmlString) //获取声轨列表
@@ -1287,9 +1310,14 @@ const getMusicInfo = async (res: any) => {
 };
 //获取xml中的音轨数据
 function xmlToTracks(xmlString: string) {
-  const xmlParse = new DOMParser().parseFromString(xmlString, "text/xml");
-  const partNames = Array.from(xmlParse.getElementsByTagName('part-name'));
-  return partNames.reduce((arr: string[], item) => {
+  //console.time('domparse')
+  // console.time('解析xml 耗时1')
+  // const xmlParse = new DOMParser().parseFromString(xmlString, "text/xml");
+  const xmlParse = xmlDocRef.value;
+  // console.timeEnd('解析xml 耗时1')
+  //console.timeEnd('domparse')
+  const partNames = xmlParse ? Array.from(xmlParse.getElementsByTagName('part-name')) : [];
+  return partNames.reduce((arr: string[], item: any) => {
     const textContent = item?.textContent?.trim()
     if (textContent?.toLocaleLowerCase() === "common") {
       (window as any).HasCommonTrack = true;

+ 117 - 0
src/utils/indexedDB.ts

@@ -0,0 +1,117 @@
+interface DataInfo {
+    id: number | string; // 主键
+    xmlString: string;
+    xmlDoc: Document;
+}
+
+export default class IndexedDBService<T> {
+    private dbName: string;
+    private storeName: string;
+    private version: number;
+  
+    constructor(dbName: string, storeName: string, version: number = 1) {
+      this.dbName = dbName;
+      this.storeName = storeName;
+      this.version = version;
+    }
+  
+    // 初始化数据库
+    private async init(): Promise<IDBDatabase> {
+      return new Promise((resolve, reject) => {
+        const request = indexedDB.open(this.dbName, this.version);
+  
+        // 数据库第一次创建或版本号升级时触发
+        request.onupgradeneeded = () => {
+          const db = request.result;
+          // 创建对象存储(类似于表)
+          if (!db.objectStoreNames.contains(this.storeName)) {
+            db.createObjectStore(this.storeName, { keyPath: "id" });
+          }
+        };
+  
+        // 返回数据库实例
+        request.onsuccess = () => resolve(request.result);
+        // 捕获错误
+        request.onerror = () => reject(request.error);
+      });
+    }
+  
+    // 添加或者更新数据
+    async save(data: T): Promise<void> {
+      const db = await this.init();
+      return new Promise((resolve, reject) => {
+        const transaction = db.transaction(this.storeName, "readwrite");
+        const store = transaction.objectStore(this.storeName);
+        const request = store.put(data);
+  
+        request.onsuccess = () => resolve();
+        request.onerror = () => reject(request.error);
+      });
+    }
+  
+    // 获取某条数据
+    async get(id: number | string): Promise<T | undefined> {
+      const db = await this.init();
+      return new Promise((resolve, reject) => {
+        const transaction = db.transaction(this.storeName, "readonly");
+        const store = transaction.objectStore(this.storeName);
+        const request = store.get(id);
+  
+        request.onsuccess = () => resolve(request.result as T);
+        request.onerror = () => reject(request.error);
+      });
+    }
+  
+    // 删除数据
+    async delete(id: number | string): Promise<void> {
+      const db = await this.init();
+      return new Promise((resolve, reject) => {
+        const transaction = db.transaction(this.storeName, "readwrite");
+        const store = transaction.objectStore(this.storeName);
+        const request = store.delete(id);
+  
+        request.onsuccess = () => resolve();
+        request.onerror = () => reject(request.error);
+      });
+    }
+  
+    // 查询所有数据
+    async getAll(): Promise<T[]> {
+      const db = await this.init();
+      return new Promise((resolve, reject) => {
+        const transaction = db.transaction(this.storeName, "readonly");
+        const store = transaction.objectStore(this.storeName);
+        const request = store.getAll();
+  
+        request.onsuccess = () => resolve(request.result as T[]);
+        request.onerror = () => reject(request.error);
+      });
+    }
+
+    // 清空某个 Object Store 中的所有数据
+    async clearAllData(): Promise<void> {
+      const db = await this.init();
+    
+      return new Promise((resolve, reject) => {
+        const transaction = db.transaction(this.storeName, "readwrite");
+        const store = transaction.objectStore(this.storeName);
+    
+        const request = store.clear(); // 清空数据
+    
+        request.onsuccess = () => resolve();
+        request.onerror = () => reject(request.error);
+      });
+    }
+
+    // 删除整个数据库
+    async deleteDatabase(): Promise<void> {
+      return new Promise((resolve, reject) => {
+        const request = indexedDB.deleteDatabase(this.dbName); // 删除整个数据库
+    
+        request.onsuccess = () => resolve();
+        request.onerror = () => reject(request.error);
+      });
+    }
+}
+  
+  

+ 8 - 5
src/view/audio-list/index.tsx

@@ -219,7 +219,8 @@ const createAudio = (src?: string): Promise<HTMLAudioElement | null> => {
 		return Promise.resolve(null)
 	}
 	return new Promise((resolve) => {
-		const a = new Audio(src + '?v=' + Date.now());
+		// const a = new Audio(src + '?v=' + Date.now());
+		const a = new Audio(src);
 		a.onloadedmetadata = () => {
 			resolve(a);
 		};
@@ -326,7 +327,7 @@ export const handleLoadBeatMusic = async () => {
 	if(isBeatMusic || !currentMusic){
 		return
 	}
-	state.loadingText = "音频资源加载中,请稍后…"
+	state.loadingText = "资源加载中,请稍后…"
 	state.isLoading = true
 	const musicAudio = await mergeBeatAudio(currentMusic) as any
 	// 给音频赋值
@@ -573,7 +574,7 @@ export default defineComponent({
 				return
 			}			
 			if (state.playMode !== "MIDI") {
-				console.time("音频加载时")
+				console.time("音频加载时")
 				// 处理音源
 				const [music, accompany, fanSong, banSong, mingSong, mingSongGirl] = await loadAudio()
 				audioData.backgroundEle = accompany;
@@ -666,8 +667,9 @@ export default defineComponent({
 				changeMingSongType()
 
 				state.audioDone = true;
-				state.isLoading = false
-				console.timeEnd("音频加载时间")
+				// state.isLoading = false
+				console.timeEnd("音频加载耗时")
+				console.timeLog('加载过程','音频加载完成')
 				console.log("音频数据:",audioData)
 				api_playProgress(progress);
 			} else {
@@ -680,6 +682,7 @@ export default defineComponent({
 				// 监听midi播放结束
 				api_cloudplayed(midiPlayEnd);
 			}
+			console.timeEnd('加载过程')
 		});
 		onUnmounted(() => {
 			api_remove_cloudplayed(midiPlayEnd);

+ 9 - 4
src/view/music-score/index.tsx

@@ -40,6 +40,9 @@ export const resetRenderMusicScore = (type?: string) => {
 // 下载后的xml
 export const downloadXmlStr = ref("")
 
+// xml的document对象
+export const xmlDocRef = ref<Document | null>(null);
+
 export default defineComponent({
 	name: "music-score",
 	emits: ["rendered"],
@@ -78,18 +81,20 @@ export default defineComponent({
 				state.musicRenderType = musicRenderType;
 			}
 		};
-		const getXML = async () => {
+		const getXML = async (cbType?: string) => {
 			// 当有下载的xml的时候直接使用,否则需要下载
 			if(!downloadXmlStr.value){
 				downloadXmlStr.value = await fetch(state.xmlUrl).then((response) => response.text())
 			}
+			console.time('增删改查xml')
 			const xmlStr = downloadXmlStr.value;
-			const parseXmlInfo = getCustomInfo(xmlStr);
-			const xml = formatXML(parseXmlInfo.parsedXML);			
+			const parseXmlInfo = getCustomInfo(xmlStr, cbType);
+			const xml = formatXML(parseXmlInfo.parsedXML, '', cbType);
 			musicData.score = state.isCombineRender ? xml : onlyVisible(xml, state.partIndex);
 			if (state.gradualTimes) {
 				state.gradual = getGradualLengthByXml(xml);
 			}
+			console.timeEnd('增删改查xml')
 		};
 
 		const init = async () => {
@@ -204,7 +209,7 @@ export default defineComponent({
 		onMounted(async () => {
 			getContainerWidth();
 			//setRenderType();
-			await getXML();
+			await getXML('init');
 			await init();
 			musicData.isRenderLoading = false;
 			// pc 端支持 拖动滚动

+ 12 - 1
src/view/plugins/toggleMusicSheet/index.tsx

@@ -10,6 +10,8 @@ import useDrag from "/src/view/plugins/useDrag/index";
 import Dragbom from "/src/view/plugins/useDrag/dragbom";
 import { setGuidance } from "/src/page-instrument/custom-plugins/guide-page/api";
 import { storeData } from "/src/store";
+import { xmlDocRef, downloadXmlStr } from "/src/view/music-score"
+import IndexedDBService from "/src/utils/indexedDB";
 
 export const toggleMusicSheet = reactive({
   show: false,
@@ -51,7 +53,15 @@ export default defineComponent({
       }
     })
 
-    const switchMusic = (partIndexs: number[]) => {
+    // 切换的时候存储处理过后的xml
+    const storeXmlData = async () => {
+      const dbService = new IndexedDBService("MyDatabase", "MyStore");
+      await dbService.save({ id: state.examSongId, xmlString: downloadXmlStr.value })
+        .then(() => dbService.get(state.examSongId))
+        .then((data) => console.log("数据:", data));
+    }
+
+    const switchMusic = async (partIndexs: number[]) => {
       const index = partIndexs.join(",") as any
       // 暂停播放
       togglePlay("paused");
@@ -80,6 +90,7 @@ export default defineComponent({
           'part-name': ''
         })
       console.log(_url)
+      await storeXmlData()
       location.href = _url
     }
 

+ 4 - 0
src/view/selection/index.tsx

@@ -254,10 +254,14 @@ export default defineComponent({
 			}
 			return []
 		})
+		console.time('dom挂载')
 		onMounted(() => {
+			console.timeEnd('dom挂载')
 			selectData.notes = [];
 			selectData.staves = [];
+			console.time('添加dom时间')
 			calcNoteData();
+			console.timeEnd('添加dom时间')
 			// 初始化谱面可移动的元素位置
 			try {
 			moveData.partIndex = state.partIndex + ""

+ 31 - 2
vite.config.ts

@@ -14,6 +14,16 @@ export default defineConfig({
   // assetsInclude: ['**/*.html'],
   plugins: [
     // mkcert(), // 本地https
+    // viteCompression({
+    //   algorithm: "gzip", // 指定压缩算法为gzip,[ 'gzip' , 'brotliCompress' ,'deflate' , 'deflateRaw']
+    //   ext: ".gz", // 指定压缩后的文件扩展名为.gz
+    //   threshold: 10240, // 仅对文件大小大于threshold的文件进行压缩,默认为10KB
+    //   deleteOriginFile: false, // 是否删除原始文件,默认为false
+    //   filter: /\.(js|css|json|html|ico|svg)(\?.*)?$/i, // 匹配要压缩的文件的正则表达式,默认为匹配.js、.css、.json、.html、.ico和.svg文件
+    //   compressionOptions: { level: 9 }, // 指定gzip压缩级别,默认为9(最高级别)
+    //   verbose: true, //是否在控制台输出压缩结果
+    //   disable: false, //是否禁用插件
+    // }), 
     legacy({
       targets: ["Chrome 63"],
       additionalLegacyPolyfills: ["regenerator-runtime/runtime"],
@@ -35,6 +45,14 @@ export default defineConfig({
     },
   },
   build: {
+    minify: 'terser', // 启用 terser 压缩  
+    terserOptions: {  
+        compress: {  
+            // pure_funcs: ['console.log'], // 只删除 console.log 
+            //drop_console: true, // 删除所有 console
+            drop_debugger: true, // 删除 debugger  
+        }  
+    }, 
     rollupOptions: {
       input: {
         gym: resolve(__dirname, "index.html"),
@@ -44,6 +62,17 @@ export default defineConfig({
         instrument: resolve(__dirname, "instrument.html"),
       },
       output: {
+        // 最小化拆分包
+        manualChunks(id) {
+          if (id.includes("osmd-extended")) {
+            // 通过拆分包的方式将所有来自osmd-extended的模块打包到单独的chunk中
+            return id
+              .toString()
+              .split("osmd-extended/")[1]
+              .split("/")[0]
+              .toString();
+          }
+        },
         chunkFileNames: "js/[name]-[hash].js", // 引入文件名的名称
         entryFileNames: "js/[name]-[hash].js", // 包的入口文件名称
         assetFileNames: "[ext]/[name]-[hash].[ext]", // 资源文件像 字体,图片等
@@ -76,8 +105,8 @@ export default defineConfig({
         // target: "https://kt.colexiu.com",
         // target: "https://test.lexiaoya.cn",
         // target: "https://kt.colexiu.com",
-        // target: "https://test.resource.colexiu.com", // 内容平台开发环境,内容平台开发,需在url链接上加上isCbs=true
-        target: "https://test.kt.colexiu.com",
+        target: "https://test.resource.colexiu.com", // 内容平台开发环境,内容平台开发,需在url链接上加上isCbs=true
+        // target: "https://test.kt.colexiu.com",
         // target: "https://mec.colexiu.com",
         changeOrigin: true,
         rewrite: (path) => path.replace(/^\/instrument/, ""),