[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"knowledge-\u002Fapi\u002Fknowledge\u002Fteam-documents\u002F04":3,"knowledge-related-\u002Fknowledge\u002Fteam-documents\u002F04":2511,"knowledge-series-\u002Fknowledge\u002Fteam-documents\u002F04":2537},{"source":4,"connector":5,"page":6},"sqlite","better-sqlite3",{"path":7,"title":8,"description":9,"category":10,"tags":11,"date":17,"pinned":18,"draft":18,"body":19,"seo":2507,"stem":2508,"id":2509,"extension":2510},"\u002Fknowledge\u002Fteam-documents\u002F04","老系统升级与兼容改造实践","公司原有业务系统采用 Node + jQuery 架构，长期运行后暴露出典型遗留系统问题：","团队文档",[12,13,14,15,16],"team","vue","performance","geo","upload","2024-12-12",false,{"type":20,"value":21,"toc":2473},"minimark",[22,25,30,39,55,71,74,88,91,95,100,128,131,135,138,159,161,165,169,188,192,195,198,242,246,278,282,296,298,302,312,315,363,366,434,437,456,458,462,470,480,1618,1622,1791,1793,1800,1804,1811,1822,1826,1850,1853,1962,1964,1968,1979,1983,2275,2279,2315,2317,2321,2325,2355,2359,2373,2375,2379,2382,2384,2387,2469],[23,24,8],"h1",{"id":8},[26,27,29],"h2",{"id":28},"_1-背景与目标","1. 背景与目标",[31,32,33,34,38],"p",{},"公司原有业务系统采用 ",[35,36,37],"code",{},"Node + jQuery"," 架构，长期运行后暴露出典型遗留系统问题：",[40,41,42,46,49,52],"ul",{},[43,44,45],"li",{},"技术栈老旧，工程化与类型体系薄弱；",[43,47,48],{},"前后端边界不清，页面脚本与业务逻辑耦合严重；",[43,50,51],{},"公共方法分散，重复代码多，变更成本高；",[43,53,54],{},"发布链路依赖人工流程，质量保障不足。",[31,56,57,58,62,63,66,67,70],{},"本次升级以“",[59,60,61],"strong",{},"稳定优先、渐进迁移、可回滚","”为原则，整体使用 ",[35,64,65],{},"Vue 3 + TypeScript + Vite + Pinia"," 搭建新中台外壳，在尽量不改动老业务逻辑的前提下，通过 ",[35,68,69],{},"iframe"," 承载历史页面，逐步完成能力替换与业务迁移。",[31,72,73],{},"核心目标如下：",[40,75,76,79,82,85],{},[43,77,78],{},"建立统一登录与权限体系；",[43,80,81],{},"统一路由、菜单、接口层和全局状态；",[43,83,84],{},"保持关键业务连续可用，避免一次性重写导致的风险；",[43,86,87],{},"为后续模块化迁移提供标准化底座。",[89,90],"hr",{},[26,92,94],{"id":93},"_2-总体方案与架构分层","2. 总体方案与架构分层",[96,97,99],"h3",{"id":98},"_21-双层架构","2.1 双层架构",[40,101,102,115],{},[43,103,104,107,108,110],{},[59,105,106],{},"外层（新中台）","：",[35,109,65],{},[40,111,112],{},[43,113,114],{},"承担登录鉴权、布局、菜单、动态路由、请求封装、全局异常处理等职责。",[43,116,117,107,120,122,123],{},[59,118,119],{},"内层（老系统）",[35,121,69],{}," 承载历史页面\n",[40,124,125],{},[43,126,127],{},"继续承载高耦合存量业务，减少对老代码入侵改造。",[31,129,130],{},"该方案的价值在于：先统一“系统能力层”，再按业务价值与复杂度分批迁移页面，降低全量替换风险。",[96,132,134],{"id":133},"_22-迁移阶段建议","2.2 迁移阶段建议",[31,136,137],{},"可按以下节奏推进（可根据团队规模调整）：",[139,140,141,147,153],"ol",{},[43,142,143,146],{},[59,144,145],{},"底座期（1-2 周）","：搭建脚手架、鉴权、路由、请求层、日志与监控。",[43,148,149,152],{},[59,150,151],{},"并行期（2-6 周）","：新老页面并存，优先迁移高频\u002F高收益页面。",[43,154,155,158],{},[59,156,157],{},"收敛期（持续）","：下线低价值旧页，沉淀组件与规范，建立迁移验收机制。",[89,160],{},[26,162,164],{"id":163},"_3-升级范围与关键改造点","3. 升级范围与关键改造点",[96,166,168],{"id":167},"_31-登录与账户体系重构","3.1 登录与账户体系重构",[40,170,171,174,177],{},[43,172,173],{},"以新框架重建登录、重置密码、会话续期流程；",[43,175,176],{},"统一错误码处理（如未登录、会话过期、权限不足）；",[43,178,179,180,183,184,187],{},"避免在 ",[35,181,182],{},"localStorage"," 明文存放敏感信息，优先采用 ",[35,185,186],{},"HttpOnly"," Cookie（若后端条件允许）。",[96,189,191],{"id":190},"_32-路由与权限后端主导","3.2 路由与权限（后端主导）",[31,193,194],{},"推荐由后端维护路由元数据与权限点，前端根据返回的路由树动态渲染菜单与页面，并通过路由守卫控制访问。",[31,196,197],{},"建议路由元数据至少包含：",[40,199,200,217,232],{},[43,201,202,205,206,205,209,212,213,216],{},[35,203,204],{},"path","、",[35,207,208],{},"name",[35,210,211],{},"component","（或 ",[35,214,215],{},"iframeUrl","）；",[43,218,219,205,222,205,225,205,228,231],{},[35,220,221],{},"meta.title",[35,223,224],{},"meta.icon",[35,226,227],{},"meta.roles",[35,229,230],{},"meta.keepAlive","；",[43,233,234,235,205,238,241],{},"页面级权限点（如 ",[35,236,237],{},"user:create",[35,239,240],{},"order:refund","）。",[96,243,245],{"id":244},"_33-基础能力统一封装","3.3 基础能力统一封装",[40,247,248,254,260,266,272],{},[43,249,250,253],{},[35,251,252],{},"utils","：日期、数值、字符串、安全转义、节流防抖等；",[43,255,256,259],{},[35,257,258],{},"hooks","：加载态、弹窗、消息、分页筛选等可复用交互逻辑；",[43,261,262,265],{},[35,263,264],{},"config","：系统常量、品牌配置、环境差异配置；",[43,267,268,271],{},[35,269,270],{},"store","：用户信息、权限状态、全局 UI 状态；",[43,273,274,277],{},[35,275,276],{},"http","：请求拦截、响应归一化、错误上报。",[96,279,281],{"id":280},"_34-类型与接口契约治理","3.4 类型与接口契约治理",[40,283,284,287,290],{},[43,285,286],{},"使用 TypeScript 描述接口响应结构与业务实体；",[43,288,289],{},"通过枚举\u002F常量集中维护 API 路径，减少“魔法字符串”；",[43,291,292,293,241],{},"建议与后端约定统一响应结构（如 ",[35,294,295],{},"code\u002Fmessage\u002Fdata",[89,297],{},[26,299,301],{"id":300},"_4-环境配置与多环境支持","4. 环境配置与多环境支持",[31,303,304,305,307,308,311],{},"企业项目通常存在开发、测试、预发布、生产等多环境。接口域名、静态资源域名、",[35,306,69],{}," 宿主域名可能不同，建议通过 Vite ",[35,309,310],{},".env"," 文件管理。",[31,313,314],{},"示例：",[316,317,322],"pre",{"className":318,"code":319,"language":320,"meta":321,"style":321},"language-bash shiki shiki-themes material-theme-lighter github-light github-dark","VITE_APP_API_URL=https:\u002F\u002Fapi.example.com\nVITE_APP_APP_ID=123456\nVITE_APP_IFRAME_HOST=https:\u002F\u002Flegacy.example.com\n","bash","",[35,323,324,341,352],{"__ignoreMap":321},[325,326,329,333,337],"span",{"class":327,"line":328},"line",1,[325,330,332],{"class":331},"su5hD","VITE_APP_API_URL",[325,334,336],{"class":335},"smGrS","=",[325,338,340],{"class":339},"s_sjI","https:\u002F\u002Fapi.example.com\n",[325,342,344,347,349],{"class":327,"line":343},2,[325,345,346],{"class":331},"VITE_APP_APP_ID",[325,348,336],{"class":335},[325,350,351],{"class":339},"123456\n",[325,353,355,358,360],{"class":327,"line":354},3,[325,356,357],{"class":331},"VITE_APP_IFRAME_HOST",[325,359,336],{"class":335},[325,361,362],{"class":339},"https:\u002F\u002Flegacy.example.com\n",[31,364,365],{},"代码中读取：",[316,367,371],{"className":368,"code":369,"language":370,"meta":321,"style":321},"language-ts shiki shiki-themes material-theme-lighter github-light github-dark","const apiUrl = import.meta.env.VITE_APP_API_URL;\nconst iframeHost = import.meta.env.VITE_APP_IFRAME_HOST;\n","ts",[35,372,373,409],{"__ignoreMap":321},[325,374,375,379,383,386,390,394,397,399,402,404,406],{"class":327,"line":328},[325,376,378],{"class":377},"sbsja","const",[325,380,382],{"class":381},"s_hVV"," apiUrl",[325,384,385],{"class":335}," =",[325,387,389],{"class":388},"sVHd0"," import",[325,391,393],{"class":392},"sP7_E",".",[325,395,396],{"class":381},"meta",[325,398,393],{"class":392},[325,400,401],{"class":331},"env",[325,403,393],{"class":392},[325,405,332],{"class":381},[325,407,408],{"class":392},";\n",[325,410,411,413,416,418,420,422,424,426,428,430,432],{"class":327,"line":343},[325,412,378],{"class":377},[325,414,415],{"class":381}," iframeHost",[325,417,385],{"class":335},[325,419,389],{"class":388},[325,421,393],{"class":392},[325,423,396],{"class":381},[325,425,393],{"class":392},[325,427,401],{"class":331},[325,429,393],{"class":392},[325,431,357],{"class":381},[325,433,408],{"class":392},[31,435,436],{},"注意事项：",[40,438,439,446,449],{},[43,440,441,442,445],{},"仅以 ",[35,443,444],{},"VITE_"," 前缀声明的变量会暴露到前端；",[43,447,448],{},"不要将密钥、数据库密码等敏感信息写入前端环境变量；",[43,450,451,452,455],{},"为每个环境配置独立域名白名单与 ",[35,453,454],{},"CORS"," 策略。",[89,457],{},[26,459,461],{"id":460},"_5-基础能力封装实践","5. 基础能力封装实践",[96,463,465,466,469],{"id":464},"_51-axios-请求层示例","5.1 ",[35,467,468],{},"axios"," 请求层（示例）",[31,471,472,473,205,476,479],{},"以下示例强调两个实践：",[59,474,475],{},"请求阶段统一注入上下文",[59,477,478],{},"响应阶段统一归一化与错误处理","。",[316,481,485],{"className":482,"code":483,"language":484,"meta":321,"style":321},"language-typescript shiki shiki-themes material-theme-lighter github-light github-dark","import axios, {\n  type AxiosInstance,\n  type InternalAxiosRequestConfig,\n  type AxiosResponse,\n} from \"axios\";\nimport qs from \"qs\";\n\nconst instance: AxiosInstance = axios.create({\n  baseURL: import.meta.env.VITE_APP_API_URL as string,\n  timeout: 60_000,\n});\n\ninstance.interceptors.request.use(\n  (config: InternalAxiosRequestConfig) => {\n    const token = localStorage.getItem(\"token\");\n    if (token) {\n      config.headers.Authorization = `Bearer ${token}`;\n    }\n\n    if (config.method === \"get\" && config.params) {\n      config.paramsSerializer = (params) =>\n        qs.stringify(params, { arrayFormat: \"repeat\" });\n    }\n    return config;\n  },\n  (error) => Promise.reject(error),\n);\n\ninstance.interceptors.response.use(\n  (response: AxiosResponse) => response.data,\n  (error) => {\n    const status = error?.response?.status;\n    if (status === 401) {\n      \u002F\u002F 可在此做统一登出与重定向\n    }\n    return Promise.reject(error?.response ?? error);\n  },\n);\n\nexport interface IHttpResult\u003CT> {\n  code: number;\n  message: string;\n  data: T;\n  success: boolean;\n}\n\nexport const httpServer = {\n  get\u003CT = unknown, U = unknown>(url: string, params?: U) {\n    return instance.request\u003CIHttpResult\u003CT>>({ url, method: \"get\", params });\n  },\n  post\u003CT = unknown, U = unknown>(url: string, data?: U) {\n    return instance.request\u003CIHttpResult\u003CT>>({ url, method: \"post\", data });\n  },\n  postForm\u003CT = unknown, U = unknown>(url: string, params?: U) {\n    return instance.request\u003CIHttpResult\u003CT>>({\n      url,\n      method: \"post\",\n      data: qs.stringify(params, { arrayFormat: \"repeat\" }),\n      headers: {\n        \"Content-Type\": \"application\u002Fx-www-form-urlencoded;charset=UTF-8\",\n      },\n    });\n  },\n};\n","typescript",[35,486,487,501,512,521,531,551,571,578,608,640,654,664,669,693,713,745,761,795,801,806,845,866,905,910,920,926,951,958,963,983,1008,1021,1046,1064,1071,1076,1104,1109,1116,1121,1144,1158,1170,1183,1196,1202,1207,1222,1271,1325,1330,1373,1423,1428,1470,1495,1503,1519,1558,1568,1591,1597,1607,1612],{"__ignoreMap":321},[325,488,489,492,495,498],{"class":327,"line":328},[325,490,491],{"class":388},"import",[325,493,494],{"class":331}," axios",[325,496,497],{"class":392},",",[325,499,500],{"class":392}," {\n",[325,502,503,506,509],{"class":327,"line":343},[325,504,505],{"class":388},"  type",[325,507,508],{"class":331}," AxiosInstance",[325,510,511],{"class":392},",\n",[325,513,514,516,519],{"class":327,"line":354},[325,515,505],{"class":388},[325,517,518],{"class":331}," InternalAxiosRequestConfig",[325,520,511],{"class":392},[325,522,524,526,529],{"class":327,"line":523},4,[325,525,505],{"class":388},[325,527,528],{"class":331}," AxiosResponse",[325,530,511],{"class":392},[325,532,534,537,540,544,546,549],{"class":327,"line":533},5,[325,535,536],{"class":392},"}",[325,538,539],{"class":388}," from",[325,541,543],{"class":542},"sjJ54"," \"",[325,545,468],{"class":339},[325,547,548],{"class":542},"\"",[325,550,408],{"class":392},[325,552,554,556,559,562,564,567,569],{"class":327,"line":553},6,[325,555,491],{"class":388},[325,557,558],{"class":331}," qs ",[325,560,561],{"class":388},"from",[325,563,543],{"class":542},[325,565,566],{"class":339},"qs",[325,568,548],{"class":542},[325,570,408],{"class":392},[325,572,574],{"class":327,"line":573},7,[325,575,577],{"emptyLinePlaceholder":576},true,"\n",[325,579,581,583,586,589,592,594,596,598,602,605],{"class":327,"line":580},8,[325,582,378],{"class":377},[325,584,585],{"class":381}," instance",[325,587,588],{"class":335},":",[325,590,508],{"class":591},"sbgvK",[325,593,385],{"class":335},[325,595,494],{"class":331},[325,597,393],{"class":392},[325,599,601],{"class":600},"sGLFI","create",[325,603,604],{"class":331},"(",[325,606,607],{"class":392},"{\n",[325,609,611,615,617,619,621,623,625,627,629,631,634,638],{"class":327,"line":610},9,[325,612,614],{"class":613},"skxfh","  baseURL",[325,616,588],{"class":392},[325,618,389],{"class":388},[325,620,393],{"class":392},[325,622,396],{"class":381},[325,624,393],{"class":392},[325,626,401],{"class":331},[325,628,393],{"class":392},[325,630,332],{"class":381},[325,632,633],{"class":388}," as",[325,635,637],{"class":636},"sZMiF"," string",[325,639,511],{"class":392},[325,641,643,646,648,652],{"class":327,"line":642},10,[325,644,645],{"class":613},"  timeout",[325,647,588],{"class":392},[325,649,651],{"class":650},"srdBf"," 60_000",[325,653,511],{"class":392},[325,655,657,659,662],{"class":327,"line":656},11,[325,658,536],{"class":392},[325,660,661],{"class":331},")",[325,663,408],{"class":392},[325,665,667],{"class":327,"line":666},12,[325,668,577],{"emptyLinePlaceholder":576},[325,670,672,675,677,680,682,685,687,690],{"class":327,"line":671},13,[325,673,674],{"class":331},"instance",[325,676,393],{"class":392},[325,678,679],{"class":331},"interceptors",[325,681,393],{"class":392},[325,683,684],{"class":331},"request",[325,686,393],{"class":392},[325,688,689],{"class":600},"use",[325,691,692],{"class":331},"(\n",[325,694,696,699,702,704,706,708,711],{"class":327,"line":695},14,[325,697,698],{"class":392},"  (",[325,700,264],{"class":701},"s99_P",[325,703,588],{"class":335},[325,705,518],{"class":591},[325,707,661],{"class":392},[325,709,710],{"class":377}," =>",[325,712,500],{"class":392},[325,714,716,719,722,724,727,729,732,734,736,739,741,743],{"class":327,"line":715},15,[325,717,718],{"class":377},"    const",[325,720,721],{"class":381}," token",[325,723,385],{"class":335},[325,725,726],{"class":331}," localStorage",[325,728,393],{"class":392},[325,730,731],{"class":600},"getItem",[325,733,604],{"class":613},[325,735,548],{"class":542},[325,737,738],{"class":339},"token",[325,740,548],{"class":542},[325,742,661],{"class":613},[325,744,408],{"class":392},[325,746,748,751,754,756,759],{"class":327,"line":747},16,[325,749,750],{"class":388},"    if",[325,752,753],{"class":613}," (",[325,755,738],{"class":331},[325,757,758],{"class":613},") ",[325,760,607],{"class":392},[325,762,764,767,769,772,774,777,779,782,785,788,790,793],{"class":327,"line":763},17,[325,765,766],{"class":331},"      config",[325,768,393],{"class":392},[325,770,771],{"class":331},"headers",[325,773,393],{"class":392},[325,775,776],{"class":331},"Authorization",[325,778,385],{"class":335},[325,780,781],{"class":542}," `",[325,783,784],{"class":339},"Bearer ",[325,786,787],{"class":542},"${",[325,789,738],{"class":331},[325,791,792],{"class":542},"}`",[325,794,408],{"class":392},[325,796,798],{"class":327,"line":797},18,[325,799,800],{"class":392},"    }\n",[325,802,804],{"class":327,"line":803},19,[325,805,577],{"emptyLinePlaceholder":576},[325,807,809,811,813,815,817,820,823,825,828,830,833,836,838,841,843],{"class":327,"line":808},20,[325,810,750],{"class":388},[325,812,753],{"class":613},[325,814,264],{"class":331},[325,816,393],{"class":392},[325,818,819],{"class":331},"method",[325,821,822],{"class":335}," ===",[325,824,543],{"class":542},[325,826,827],{"class":339},"get",[325,829,548],{"class":542},[325,831,832],{"class":335}," &&",[325,834,835],{"class":331}," config",[325,837,393],{"class":392},[325,839,840],{"class":331},"params",[325,842,758],{"class":613},[325,844,607],{"class":392},[325,846,848,850,852,855,857,859,861,863],{"class":327,"line":847},21,[325,849,766],{"class":331},[325,851,393],{"class":392},[325,853,854],{"class":600},"paramsSerializer",[325,856,385],{"class":335},[325,858,753],{"class":392},[325,860,840],{"class":701},[325,862,661],{"class":392},[325,864,865],{"class":377}," =>\n",[325,867,869,872,874,877,879,881,883,886,889,891,893,896,898,901,903],{"class":327,"line":868},22,[325,870,871],{"class":331},"        qs",[325,873,393],{"class":392},[325,875,876],{"class":600},"stringify",[325,878,604],{"class":613},[325,880,840],{"class":331},[325,882,497],{"class":392},[325,884,885],{"class":392}," {",[325,887,888],{"class":613}," arrayFormat",[325,890,588],{"class":392},[325,892,543],{"class":542},[325,894,895],{"class":339},"repeat",[325,897,548],{"class":542},[325,899,900],{"class":392}," }",[325,902,661],{"class":613},[325,904,408],{"class":392},[325,906,908],{"class":327,"line":907},23,[325,909,800],{"class":392},[325,911,913,916,918],{"class":327,"line":912},24,[325,914,915],{"class":388},"    return",[325,917,835],{"class":331},[325,919,408],{"class":392},[325,921,923],{"class":327,"line":922},25,[325,924,925],{"class":392},"  },\n",[325,927,929,931,934,936,938,941,943,946,949],{"class":327,"line":928},26,[325,930,698],{"class":392},[325,932,933],{"class":701},"error",[325,935,661],{"class":392},[325,937,710],{"class":377},[325,939,940],{"class":636}," Promise",[325,942,393],{"class":392},[325,944,945],{"class":600},"reject",[325,947,948],{"class":331},"(error)",[325,950,511],{"class":392},[325,952,954,956],{"class":327,"line":953},27,[325,955,661],{"class":331},[325,957,408],{"class":392},[325,959,961],{"class":327,"line":960},28,[325,962,577],{"emptyLinePlaceholder":576},[325,964,966,968,970,972,974,977,979,981],{"class":327,"line":965},29,[325,967,674],{"class":331},[325,969,393],{"class":392},[325,971,679],{"class":331},[325,973,393],{"class":392},[325,975,976],{"class":331},"response",[325,978,393],{"class":392},[325,980,689],{"class":600},[325,982,692],{"class":331},[325,984,986,988,990,992,994,996,998,1001,1003,1006],{"class":327,"line":985},30,[325,987,698],{"class":392},[325,989,976],{"class":701},[325,991,588],{"class":335},[325,993,528],{"class":591},[325,995,661],{"class":392},[325,997,710],{"class":377},[325,999,1000],{"class":331}," response",[325,1002,393],{"class":392},[325,1004,1005],{"class":331},"data",[325,1007,511],{"class":392},[325,1009,1011,1013,1015,1017,1019],{"class":327,"line":1010},31,[325,1012,698],{"class":392},[325,1014,933],{"class":701},[325,1016,661],{"class":392},[325,1018,710],{"class":377},[325,1020,500],{"class":392},[325,1022,1024,1026,1029,1031,1034,1037,1039,1041,1044],{"class":327,"line":1023},32,[325,1025,718],{"class":377},[325,1027,1028],{"class":381}," status",[325,1030,385],{"class":335},[325,1032,1033],{"class":331}," error",[325,1035,1036],{"class":392},"?.",[325,1038,976],{"class":331},[325,1040,1036],{"class":392},[325,1042,1043],{"class":331},"status",[325,1045,408],{"class":392},[325,1047,1049,1051,1053,1055,1057,1060,1062],{"class":327,"line":1048},33,[325,1050,750],{"class":388},[325,1052,753],{"class":613},[325,1054,1043],{"class":331},[325,1056,822],{"class":335},[325,1058,1059],{"class":650}," 401",[325,1061,758],{"class":613},[325,1063,607],{"class":392},[325,1065,1067],{"class":327,"line":1066},34,[325,1068,1070],{"class":1069},"sutJx","      \u002F\u002F 可在此做统一登出与重定向\n",[325,1072,1074],{"class":327,"line":1073},35,[325,1075,800],{"class":392},[325,1077,1079,1081,1083,1085,1087,1089,1091,1093,1095,1098,1100,1102],{"class":327,"line":1078},36,[325,1080,915],{"class":388},[325,1082,940],{"class":636},[325,1084,393],{"class":392},[325,1086,945],{"class":600},[325,1088,604],{"class":613},[325,1090,933],{"class":331},[325,1092,1036],{"class":392},[325,1094,976],{"class":331},[325,1096,1097],{"class":335}," ??",[325,1099,1033],{"class":331},[325,1101,661],{"class":613},[325,1103,408],{"class":392},[325,1105,1107],{"class":327,"line":1106},37,[325,1108,925],{"class":392},[325,1110,1112,1114],{"class":327,"line":1111},38,[325,1113,661],{"class":331},[325,1115,408],{"class":392},[325,1117,1119],{"class":327,"line":1118},39,[325,1120,577],{"emptyLinePlaceholder":576},[325,1122,1124,1127,1130,1133,1136,1139,1142],{"class":327,"line":1123},40,[325,1125,1126],{"class":388},"export",[325,1128,1129],{"class":377}," interface",[325,1131,1132],{"class":591}," IHttpResult",[325,1134,1135],{"class":392},"\u003C",[325,1137,1138],{"class":591},"T",[325,1140,1141],{"class":392},">",[325,1143,500],{"class":392},[325,1145,1147,1151,1153,1156],{"class":327,"line":1146},41,[325,1148,1150],{"class":1149},"sucvu","  code",[325,1152,588],{"class":335},[325,1154,1155],{"class":636}," number",[325,1157,408],{"class":392},[325,1159,1161,1164,1166,1168],{"class":327,"line":1160},42,[325,1162,1163],{"class":1149},"  message",[325,1165,588],{"class":335},[325,1167,637],{"class":636},[325,1169,408],{"class":392},[325,1171,1173,1176,1178,1181],{"class":327,"line":1172},43,[325,1174,1175],{"class":1149},"  data",[325,1177,588],{"class":335},[325,1179,1180],{"class":591}," T",[325,1182,408],{"class":392},[325,1184,1186,1189,1191,1194],{"class":327,"line":1185},44,[325,1187,1188],{"class":1149},"  success",[325,1190,588],{"class":335},[325,1192,1193],{"class":636}," boolean",[325,1195,408],{"class":392},[325,1197,1199],{"class":327,"line":1198},45,[325,1200,1201],{"class":392},"}\n",[325,1203,1205],{"class":327,"line":1204},46,[325,1206,577],{"emptyLinePlaceholder":576},[325,1208,1210,1212,1215,1218,1220],{"class":327,"line":1209},47,[325,1211,1126],{"class":388},[325,1213,1214],{"class":377}," const",[325,1216,1217],{"class":381}," httpServer",[325,1219,385],{"class":335},[325,1221,500],{"class":392},[325,1223,1225,1229,1231,1233,1235,1238,1240,1243,1245,1247,1250,1253,1255,1257,1259,1262,1265,1267,1269],{"class":327,"line":1224},48,[325,1226,1228],{"class":1227},"sVXei","  get",[325,1230,1135],{"class":392},[325,1232,1138],{"class":591},[325,1234,385],{"class":335},[325,1236,1237],{"class":636}," unknown",[325,1239,497],{"class":392},[325,1241,1242],{"class":591}," U",[325,1244,385],{"class":335},[325,1246,1237],{"class":636},[325,1248,1249],{"class":392},">(",[325,1251,1252],{"class":701},"url",[325,1254,588],{"class":335},[325,1256,637],{"class":636},[325,1258,497],{"class":392},[325,1260,1261],{"class":701}," params",[325,1263,1264],{"class":335},"?:",[325,1266,1242],{"class":591},[325,1268,661],{"class":392},[325,1270,500],{"class":392},[325,1272,1274,1276,1278,1280,1282,1284,1287,1289,1291,1294,1296,1299,1302,1304,1307,1309,1311,1313,1315,1317,1319,1321,1323],{"class":327,"line":1273},49,[325,1275,915],{"class":388},[325,1277,585],{"class":331},[325,1279,393],{"class":392},[325,1281,684],{"class":600},[325,1283,1135],{"class":392},[325,1285,1286],{"class":591},"IHttpResult",[325,1288,1135],{"class":392},[325,1290,1138],{"class":591},[325,1292,1293],{"class":392},">>",[325,1295,604],{"class":613},[325,1297,1298],{"class":392},"{",[325,1300,1301],{"class":331}," url",[325,1303,497],{"class":392},[325,1305,1306],{"class":613}," method",[325,1308,588],{"class":392},[325,1310,543],{"class":542},[325,1312,827],{"class":339},[325,1314,548],{"class":542},[325,1316,497],{"class":392},[325,1318,1261],{"class":331},[325,1320,900],{"class":392},[325,1322,661],{"class":613},[325,1324,408],{"class":392},[325,1326,1328],{"class":327,"line":1327},50,[325,1329,925],{"class":392},[325,1331,1333,1336,1338,1340,1342,1344,1346,1348,1350,1352,1354,1356,1358,1360,1362,1365,1367,1369,1371],{"class":327,"line":1332},51,[325,1334,1335],{"class":1227},"  post",[325,1337,1135],{"class":392},[325,1339,1138],{"class":591},[325,1341,385],{"class":335},[325,1343,1237],{"class":636},[325,1345,497],{"class":392},[325,1347,1242],{"class":591},[325,1349,385],{"class":335},[325,1351,1237],{"class":636},[325,1353,1249],{"class":392},[325,1355,1252],{"class":701},[325,1357,588],{"class":335},[325,1359,637],{"class":636},[325,1361,497],{"class":392},[325,1363,1364],{"class":701}," data",[325,1366,1264],{"class":335},[325,1368,1242],{"class":591},[325,1370,661],{"class":392},[325,1372,500],{"class":392},[325,1374,1376,1378,1380,1382,1384,1386,1388,1390,1392,1394,1396,1398,1400,1402,1404,1406,1408,1411,1413,1415,1417,1419,1421],{"class":327,"line":1375},52,[325,1377,915],{"class":388},[325,1379,585],{"class":331},[325,1381,393],{"class":392},[325,1383,684],{"class":600},[325,1385,1135],{"class":392},[325,1387,1286],{"class":591},[325,1389,1135],{"class":392},[325,1391,1138],{"class":591},[325,1393,1293],{"class":392},[325,1395,604],{"class":613},[325,1397,1298],{"class":392},[325,1399,1301],{"class":331},[325,1401,497],{"class":392},[325,1403,1306],{"class":613},[325,1405,588],{"class":392},[325,1407,543],{"class":542},[325,1409,1410],{"class":339},"post",[325,1412,548],{"class":542},[325,1414,497],{"class":392},[325,1416,1364],{"class":331},[325,1418,900],{"class":392},[325,1420,661],{"class":613},[325,1422,408],{"class":392},[325,1424,1426],{"class":327,"line":1425},53,[325,1427,925],{"class":392},[325,1429,1431,1434,1436,1438,1440,1442,1444,1446,1448,1450,1452,1454,1456,1458,1460,1462,1464,1466,1468],{"class":327,"line":1430},54,[325,1432,1433],{"class":1227},"  postForm",[325,1435,1135],{"class":392},[325,1437,1138],{"class":591},[325,1439,385],{"class":335},[325,1441,1237],{"class":636},[325,1443,497],{"class":392},[325,1445,1242],{"class":591},[325,1447,385],{"class":335},[325,1449,1237],{"class":636},[325,1451,1249],{"class":392},[325,1453,1252],{"class":701},[325,1455,588],{"class":335},[325,1457,637],{"class":636},[325,1459,497],{"class":392},[325,1461,1261],{"class":701},[325,1463,1264],{"class":335},[325,1465,1242],{"class":591},[325,1467,661],{"class":392},[325,1469,500],{"class":392},[325,1471,1473,1475,1477,1479,1481,1483,1485,1487,1489,1491,1493],{"class":327,"line":1472},55,[325,1474,915],{"class":388},[325,1476,585],{"class":331},[325,1478,393],{"class":392},[325,1480,684],{"class":600},[325,1482,1135],{"class":392},[325,1484,1286],{"class":591},[325,1486,1135],{"class":392},[325,1488,1138],{"class":591},[325,1490,1293],{"class":392},[325,1492,604],{"class":613},[325,1494,607],{"class":392},[325,1496,1498,1501],{"class":327,"line":1497},56,[325,1499,1500],{"class":331},"      url",[325,1502,511],{"class":392},[325,1504,1506,1509,1511,1513,1515,1517],{"class":327,"line":1505},57,[325,1507,1508],{"class":613},"      method",[325,1510,588],{"class":392},[325,1512,543],{"class":542},[325,1514,1410],{"class":339},[325,1516,548],{"class":542},[325,1518,511],{"class":392},[325,1520,1522,1525,1527,1530,1532,1534,1536,1538,1540,1542,1544,1546,1548,1550,1552,1554,1556],{"class":327,"line":1521},58,[325,1523,1524],{"class":613},"      data",[325,1526,588],{"class":392},[325,1528,1529],{"class":331}," qs",[325,1531,393],{"class":392},[325,1533,876],{"class":600},[325,1535,604],{"class":613},[325,1537,840],{"class":331},[325,1539,497],{"class":392},[325,1541,885],{"class":392},[325,1543,888],{"class":613},[325,1545,588],{"class":392},[325,1547,543],{"class":542},[325,1549,895],{"class":339},[325,1551,548],{"class":542},[325,1553,900],{"class":392},[325,1555,661],{"class":613},[325,1557,511],{"class":392},[325,1559,1561,1564,1566],{"class":327,"line":1560},59,[325,1562,1563],{"class":613},"      headers",[325,1565,588],{"class":392},[325,1567,500],{"class":392},[325,1569,1571,1574,1578,1580,1582,1584,1587,1589],{"class":327,"line":1570},60,[325,1572,1573],{"class":542},"        \"",[325,1575,1577],{"class":1576},"sZUrc","Content-Type",[325,1579,548],{"class":542},[325,1581,588],{"class":392},[325,1583,543],{"class":542},[325,1585,1586],{"class":339},"application\u002Fx-www-form-urlencoded;charset=UTF-8",[325,1588,548],{"class":542},[325,1590,511],{"class":392},[325,1592,1594],{"class":327,"line":1593},61,[325,1595,1596],{"class":392},"      },\n",[325,1598,1600,1603,1605],{"class":327,"line":1599},62,[325,1601,1602],{"class":392},"    }",[325,1604,661],{"class":613},[325,1606,408],{"class":392},[325,1608,1610],{"class":327,"line":1609},63,[325,1611,925],{"class":392},[325,1613,1615],{"class":327,"line":1614},64,[325,1616,1617],{"class":392},"};\n",[96,1619,1621],{"id":1620},"_52-api-枚举与函数封装示例","5.2 API 枚举与函数封装（示例）",[316,1623,1625],{"className":482,"code":1624,"language":484,"meta":321,"style":321},"enum UserApi {\n  UploadImage = \"\u002Fapi\u002Fupload\u002Fimage\",\n  DeleteImage = \"\u002Fapi\u002Fupload\u002Fdelete\",\n  ImageList = \"\u002Fapi\u002Fupload\u002Flist\",\n}\n\nimport { httpServer } from \"@\u002Fhttp\";\n\nexport function getImageList\u003CT, U>(params: U) {\n  return httpServer.get\u003CT, U>(UserApi.ImageList, params);\n}\n",[35,1626,1627,1637,1653,1669,1685,1689,1693,1714,1718,1748,1787],{"__ignoreMap":321},[325,1628,1629,1632,1635],{"class":327,"line":328},[325,1630,1631],{"class":377},"enum",[325,1633,1634],{"class":591}," UserApi",[325,1636,500],{"class":392},[325,1638,1639,1642,1644,1646,1649,1651],{"class":327,"line":343},[325,1640,1641],{"class":381},"  UploadImage",[325,1643,385],{"class":335},[325,1645,543],{"class":542},[325,1647,1648],{"class":339},"\u002Fapi\u002Fupload\u002Fimage",[325,1650,548],{"class":542},[325,1652,511],{"class":392},[325,1654,1655,1658,1660,1662,1665,1667],{"class":327,"line":354},[325,1656,1657],{"class":381},"  DeleteImage",[325,1659,385],{"class":335},[325,1661,543],{"class":542},[325,1663,1664],{"class":339},"\u002Fapi\u002Fupload\u002Fdelete",[325,1666,548],{"class":542},[325,1668,511],{"class":392},[325,1670,1671,1674,1676,1678,1681,1683],{"class":327,"line":523},[325,1672,1673],{"class":381},"  ImageList",[325,1675,385],{"class":335},[325,1677,543],{"class":542},[325,1679,1680],{"class":339},"\u002Fapi\u002Fupload\u002Flist",[325,1682,548],{"class":542},[325,1684,511],{"class":392},[325,1686,1687],{"class":327,"line":533},[325,1688,1201],{"class":392},[325,1690,1691],{"class":327,"line":553},[325,1692,577],{"emptyLinePlaceholder":576},[325,1694,1695,1697,1699,1701,1703,1705,1707,1710,1712],{"class":327,"line":573},[325,1696,491],{"class":388},[325,1698,885],{"class":392},[325,1700,1217],{"class":331},[325,1702,900],{"class":392},[325,1704,539],{"class":388},[325,1706,543],{"class":542},[325,1708,1709],{"class":339},"@\u002Fhttp",[325,1711,548],{"class":542},[325,1713,408],{"class":392},[325,1715,1716],{"class":327,"line":580},[325,1717,577],{"emptyLinePlaceholder":576},[325,1719,1720,1722,1725,1728,1730,1732,1734,1736,1738,1740,1742,1744,1746],{"class":327,"line":610},[325,1721,1126],{"class":388},[325,1723,1724],{"class":377}," function",[325,1726,1727],{"class":600}," getImageList",[325,1729,1135],{"class":392},[325,1731,1138],{"class":591},[325,1733,497],{"class":392},[325,1735,1242],{"class":591},[325,1737,1249],{"class":392},[325,1739,840],{"class":701},[325,1741,588],{"class":335},[325,1743,1242],{"class":591},[325,1745,661],{"class":392},[325,1747,500],{"class":392},[325,1749,1750,1753,1755,1757,1759,1761,1763,1765,1767,1769,1771,1774,1776,1779,1781,1783,1785],{"class":327,"line":642},[325,1751,1752],{"class":388},"  return",[325,1754,1217],{"class":331},[325,1756,393],{"class":392},[325,1758,827],{"class":600},[325,1760,1135],{"class":392},[325,1762,1138],{"class":591},[325,1764,497],{"class":392},[325,1766,1242],{"class":591},[325,1768,1141],{"class":392},[325,1770,604],{"class":613},[325,1772,1773],{"class":331},"UserApi",[325,1775,393],{"class":392},[325,1777,1778],{"class":331},"ImageList",[325,1780,497],{"class":392},[325,1782,1261],{"class":331},[325,1784,661],{"class":613},[325,1786,408],{"class":392},[325,1788,1789],{"class":327,"line":656},[325,1790,1201],{"class":392},[89,1792],{},[26,1794,1796,1797,1799],{"id":1795},"_6-iframe-并行阶段的体验优化","6. ",[35,1798,69],{}," 并行阶段的体验优化",[96,1801,1803],{"id":1802},"_61-常见问题","6.1 常见问题",[31,1805,1806,1807,1810],{},"简单使用 ",[35,1808,1809],{},"\u003Ciframe :src=\"url\">"," 在路由切换时会触发整页重载，导致：",[40,1812,1813,1816,1819],{},[43,1814,1815],{},"重复请求静态资源与接口；",[43,1817,1818],{},"表单临时输入丢失；",[43,1820,1821],{},"页面滚动位置与交互上下文丢失。",[96,1823,1825],{"id":1824},"_62-优化策略","6.2 优化策略",[40,1827,1828,1835,1844,1847],{},[43,1829,1830,1831,1834],{},"使用 ",[35,1832,1833],{},"v-show"," 控制显隐，尽量避免卸载重建；",[43,1836,1837,1838,1840,1841,1843],{},"维护 ",[35,1839,69],{}," 实例缓存（如按路由 ",[35,1842,204],{}," 建立 Map）；",[43,1845,1846],{},"在切换前后同步滚动位置；",[43,1848,1849],{},"对高频页面可增加预加载策略（注意并发与内存上限）。",[31,1851,1852],{},"示例（简化）：",[316,1854,1857],{"className":1855,"code":1856,"language":13,"meta":321,"style":321},"language-vue shiki shiki-themes material-theme-lighter github-light github-dark","\u003Ciframe\n  v-for=\"item in routes\"\n  :key=\"item.path\"\n  v-show=\"item.path === route.path\"\n  :src=\"item.iframeUrl\"\n\u002F>\n",[35,1858,1859,1867,1888,1910,1938,1957],{"__ignoreMap":321},[325,1860,1861,1863],{"class":327,"line":328},[325,1862,1135],{"class":392},[325,1864,1866],{"class":1865},"sQzsp","iframe\n",[325,1868,1869,1872,1874,1876,1879,1882,1885],{"class":327,"line":343},[325,1870,1871],{"class":388},"  v-for",[325,1873,336],{"class":392},[325,1875,548],{"class":542},[325,1877,1878],{"class":331},"item ",[325,1880,1881],{"class":335},"in",[325,1883,1884],{"class":331}," routes",[325,1886,1887],{"class":542},"\"\n",[325,1889,1890,1893,1897,1899,1901,1904,1906,1908],{"class":327,"line":354},[325,1891,1892],{"class":392},"  :",[325,1894,1896],{"class":1895},"s9AJx","key",[325,1898,336],{"class":392},[325,1900,548],{"class":542},[325,1902,1903],{"class":331},"item",[325,1905,393],{"class":392},[325,1907,204],{"class":331},[325,1909,1887],{"class":542},[325,1911,1912,1915,1917,1919,1921,1923,1926,1929,1932,1934,1936],{"class":327,"line":523},[325,1913,1914],{"class":1895},"  v-show",[325,1916,336],{"class":392},[325,1918,548],{"class":542},[325,1920,1903],{"class":331},[325,1922,393],{"class":392},[325,1924,1925],{"class":331},"path ",[325,1927,1928],{"class":335},"===",[325,1930,1931],{"class":331}," route",[325,1933,393],{"class":392},[325,1935,204],{"class":331},[325,1937,1887],{"class":542},[325,1939,1940,1942,1945,1947,1949,1951,1953,1955],{"class":327,"line":533},[325,1941,1892],{"class":392},[325,1943,1944],{"class":1895},"src",[325,1946,336],{"class":392},[325,1948,548],{"class":542},[325,1950,1903],{"class":331},[325,1952,393],{"class":392},[325,1954,215],{"class":331},[325,1956,1887],{"class":542},[325,1958,1959],{"class":327,"line":553},[325,1960,1961],{"class":331},"\u002F>\n",[89,1963],{},[26,1965,1967],{"id":1966},"_7-新老系统通信与图片预览兼容","7. 新老系统通信与图片预览兼容",[31,1969,1970,1971,1974,1975,1978],{},"老系统常以“新标签页打开图片”为默认行为；新系统可能希望统一为 ",[35,1972,1973],{},"Element Plus"," 预览组件。可通过 ",[35,1976,1977],{},"postMessage"," 做父子通信，但必须关注安全边界。",[96,1980,1982],{"id":1981},"_71-通信示例","7.1 通信示例",[316,1984,1988],{"className":1985,"code":1986,"language":1987,"meta":321,"style":321},"language-javascript shiki shiki-themes material-theme-lighter github-light github-dark","\u002F\u002F iframe 内部（老系统）\nwindow.parent.postMessage(\n  {\n    command: \"open-image\",\n    data: { url },\n  },\n  \"https:\u002F\u002Fadmin.example.com\",\n);\n\n\u002F\u002F 外层新系统\nfunction handleMessage(event) {\n  const trustedOrigin = \"https:\u002F\u002Flegacy.example.com\";\n  if (event.origin !== trustedOrigin) return;\n\n  if (event.data?.command === \"open-image\") {\n    const imageUrl = event.data?.data?.url;\n    \u002F\u002F 1) 调用图片预览组件，或\n    \u002F\u002F 2) 兜底 window.open(imageUrl, \"_blank\", \"noopener,noreferrer\")\n  }\n}\n\nwindow.addEventListener(\"message\", handleMessage);\n\u002F\u002F 卸载时移除监听\nwindow.removeEventListener(\"message\", handleMessage);\n","javascript",[35,1989,1990,1995,2011,2016,2032,2047,2051,2063,2069,2073,2078,2095,2114,2140,2144,2173,2199,2204,2209,2214,2218,2222,2247,2252],{"__ignoreMap":321},[325,1991,1992],{"class":327,"line":328},[325,1993,1994],{"class":1069},"\u002F\u002F iframe 内部（老系统）\n",[325,1996,1997,2000,2002,2005,2007,2009],{"class":327,"line":343},[325,1998,1999],{"class":331},"window",[325,2001,393],{"class":392},[325,2003,2004],{"class":331},"parent",[325,2006,393],{"class":392},[325,2008,1977],{"class":600},[325,2010,692],{"class":331},[325,2012,2013],{"class":327,"line":354},[325,2014,2015],{"class":392},"  {\n",[325,2017,2018,2021,2023,2025,2028,2030],{"class":327,"line":523},[325,2019,2020],{"class":613},"    command",[325,2022,588],{"class":392},[325,2024,543],{"class":542},[325,2026,2027],{"class":339},"open-image",[325,2029,548],{"class":542},[325,2031,511],{"class":392},[325,2033,2034,2037,2039,2041,2044],{"class":327,"line":533},[325,2035,2036],{"class":613},"    data",[325,2038,588],{"class":392},[325,2040,885],{"class":392},[325,2042,2043],{"class":331}," url ",[325,2045,2046],{"class":392},"},\n",[325,2048,2049],{"class":327,"line":553},[325,2050,925],{"class":392},[325,2052,2053,2056,2059,2061],{"class":327,"line":573},[325,2054,2055],{"class":542},"  \"",[325,2057,2058],{"class":339},"https:\u002F\u002Fadmin.example.com",[325,2060,548],{"class":542},[325,2062,511],{"class":392},[325,2064,2065,2067],{"class":327,"line":580},[325,2066,661],{"class":331},[325,2068,408],{"class":392},[325,2070,2071],{"class":327,"line":610},[325,2072,577],{"emptyLinePlaceholder":576},[325,2074,2075],{"class":327,"line":642},[325,2076,2077],{"class":1069},"\u002F\u002F 外层新系统\n",[325,2079,2080,2083,2086,2088,2091,2093],{"class":327,"line":656},[325,2081,2082],{"class":377},"function",[325,2084,2085],{"class":600}," handleMessage",[325,2087,604],{"class":392},[325,2089,2090],{"class":701},"event",[325,2092,661],{"class":392},[325,2094,500],{"class":392},[325,2096,2097,2100,2103,2105,2107,2110,2112],{"class":327,"line":666},[325,2098,2099],{"class":377},"  const",[325,2101,2102],{"class":381}," trustedOrigin",[325,2104,385],{"class":335},[325,2106,543],{"class":542},[325,2108,2109],{"class":339},"https:\u002F\u002Flegacy.example.com",[325,2111,548],{"class":542},[325,2113,408],{"class":392},[325,2115,2116,2119,2121,2123,2125,2128,2131,2133,2135,2138],{"class":327,"line":671},[325,2117,2118],{"class":388},"  if",[325,2120,753],{"class":613},[325,2122,2090],{"class":331},[325,2124,393],{"class":392},[325,2126,2127],{"class":331},"origin",[325,2129,2130],{"class":335}," !==",[325,2132,2102],{"class":331},[325,2134,758],{"class":613},[325,2136,2137],{"class":388},"return",[325,2139,408],{"class":392},[325,2141,2142],{"class":327,"line":695},[325,2143,577],{"emptyLinePlaceholder":576},[325,2145,2146,2148,2150,2152,2154,2156,2158,2161,2163,2165,2167,2169,2171],{"class":327,"line":715},[325,2147,2118],{"class":388},[325,2149,753],{"class":613},[325,2151,2090],{"class":331},[325,2153,393],{"class":392},[325,2155,1005],{"class":331},[325,2157,1036],{"class":392},[325,2159,2160],{"class":331},"command",[325,2162,822],{"class":335},[325,2164,543],{"class":542},[325,2166,2027],{"class":339},[325,2168,548],{"class":542},[325,2170,758],{"class":613},[325,2172,607],{"class":392},[325,2174,2175,2177,2180,2182,2185,2187,2189,2191,2193,2195,2197],{"class":327,"line":747},[325,2176,718],{"class":377},[325,2178,2179],{"class":381}," imageUrl",[325,2181,385],{"class":335},[325,2183,2184],{"class":331}," event",[325,2186,393],{"class":392},[325,2188,1005],{"class":331},[325,2190,1036],{"class":392},[325,2192,1005],{"class":331},[325,2194,1036],{"class":392},[325,2196,1252],{"class":331},[325,2198,408],{"class":392},[325,2200,2201],{"class":327,"line":763},[325,2202,2203],{"class":1069},"    \u002F\u002F 1) 调用图片预览组件，或\n",[325,2205,2206],{"class":327,"line":797},[325,2207,2208],{"class":1069},"    \u002F\u002F 2) 兜底 window.open(imageUrl, \"_blank\", \"noopener,noreferrer\")\n",[325,2210,2211],{"class":327,"line":803},[325,2212,2213],{"class":392},"  }\n",[325,2215,2216],{"class":327,"line":808},[325,2217,1201],{"class":392},[325,2219,2220],{"class":327,"line":847},[325,2221,577],{"emptyLinePlaceholder":576},[325,2223,2224,2226,2228,2231,2233,2235,2238,2240,2242,2245],{"class":327,"line":868},[325,2225,1999],{"class":331},[325,2227,393],{"class":392},[325,2229,2230],{"class":600},"addEventListener",[325,2232,604],{"class":331},[325,2234,548],{"class":542},[325,2236,2237],{"class":339},"message",[325,2239,548],{"class":542},[325,2241,497],{"class":392},[325,2243,2244],{"class":331}," handleMessage)",[325,2246,408],{"class":392},[325,2248,2249],{"class":327,"line":907},[325,2250,2251],{"class":1069},"\u002F\u002F 卸载时移除监听\n",[325,2253,2254,2256,2258,2261,2263,2265,2267,2269,2271,2273],{"class":327,"line":912},[325,2255,1999],{"class":331},[325,2257,393],{"class":392},[325,2259,2260],{"class":600},"removeEventListener",[325,2262,604],{"class":331},[325,2264,548],{"class":542},[325,2266,2237],{"class":339},[325,2268,548],{"class":542},[325,2270,497],{"class":392},[325,2272,2244],{"class":331},[325,2274,408],{"class":392},[96,2276,2278],{"id":2277},"_72-安全建议","7.2 安全建议",[40,2280,2281,2291,2298,2301],{},[43,2282,2283,2286,2287,2290],{},[35,2284,2285],{},"targetOrigin"," 不要使用 ",[35,2288,2289],{},"\"*\"","，应指定明确来源；",[43,2292,2293,2294,2297],{},"接收消息时必须校验 ",[35,2295,2296],{},"event.origin"," 和消息结构；",[43,2299,2300],{},"对 URL 做协议与域名白名单校验，防止注入恶意链接；",[43,2302,2303,2304,2307,2308,2307,2311,2314],{},"统一定义消息协议版本（如 ",[35,2305,2306],{},"type",", ",[35,2309,2310],{},"version",[35,2312,2313],{},"payload","），降低联调歧义。",[89,2316],{},[26,2318,2320],{"id":2319},"_8-风险控制与验收建议","8. 风险控制与验收建议",[96,2322,2324],{"id":2323},"_81-风险清单","8.1 风险清单",[40,2326,2327,2333,2339,2347],{},[43,2328,2329,2332],{},[59,2330,2331],{},"会话一致性风险","：新老系统 token 刷新机制不一致；",[43,2334,2335,2338],{},[59,2336,2337],{},"权限漂移风险","：菜单可见但接口无权，或反之；",[43,2340,2341,107,2344,2346],{},[59,2342,2343],{},"性能风险",[35,2345,69],{}," 数量过多导致内存上涨；",[43,2348,2349,107,2352,2354],{},[59,2350,2351],{},"通信风险",[35,2353,1977],{}," 校验不足带来安全隐患。",[96,2356,2358],{"id":2357},"_82-验收维度","8.2 验收维度",[40,2360,2361,2364,2367,2370],{},[43,2362,2363],{},"功能正确性：登录、登出、权限、关键业务流程；",[43,2365,2366],{},"体验一致性：切页速度、滚动位置恢复、图片预览行为；",[43,2368,2369],{},"稳定性：接口错误回退、白屏兜底、异常链路可观测；",[43,2371,2372],{},"可维护性：新增页面开发时间、重复代码减少比例、问题定位时长。",[89,2374],{},[26,2376,2378],{"id":2377},"_9-结语","9. 结语",[31,2380,2381],{},"对遗留系统升级而言，“推倒重来”通常不是最优解。通过“新外壳 + 老页面承载 + 渐进迁移”的策略，可以在业务连续运行的前提下，逐步获得现代工程能力，并为后续彻底去遗留化奠定基础。",[89,2383],{},[26,2385,2386],{"id":2386},"参考文献",[139,2388,2389,2401,2410,2419,2428,2439,2451,2460],{},[43,2390,2391,2392,2395],{},"Vite 文档：环境变量与模式",[2393,2394],"br",{},[2396,2397,2398],"a",{"href":2398,"rel":2399},"https:\u002F\u002Fvite.dev\u002Fguide\u002Fenv-and-mode.html",[2400],"nofollow",[43,2402,2403,2404,2406],{},"Vue 3 官方文档",[2393,2405],{},[2396,2407,2408],{"href":2408,"rel":2409},"https:\u002F\u002Fvuejs.org\u002F",[2400],[43,2411,2412,2413,2415],{},"Pinia 官方文档",[2393,2414],{},[2396,2416,2417],{"href":2417,"rel":2418},"https:\u002F\u002Fpinia.vuejs.org\u002F",[2400],[43,2420,2421,2422,2424],{},"Axios 官方文档",[2393,2423],{},[2396,2425,2426],{"href":2426,"rel":2427},"https:\u002F\u002Faxios-http.com\u002F",[2400],[43,2429,2430,2432,2433,2435],{},[35,2431,566],{}," 仓库",[2393,2434],{},[2396,2436,2437],{"href":2437,"rel":2438},"https:\u002F\u002Fgithub.com\u002Fljharb\u002Fqs",[2400],[43,2440,2441,2442,2445,2447],{},"MDN: ",[35,2443,2444],{},"window.postMessage",[2393,2446],{},[2396,2448,2449],{"href":2449,"rel":2450},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FAPI\u002FWindow\u002FpostMessage",[2400],[43,2452,2453,2454,2456],{},"Element Plus 官方文档",[2393,2455],{},[2396,2457,2458],{"href":2458,"rel":2459},"https:\u002F\u002Felement-plus.org\u002F",[2400],[43,2461,2462,2463,2465],{},"OWASP Top 10",[2393,2464],{},[2396,2466,2467],{"href":2467,"rel":2468},"https:\u002F\u002Fowasp.org\u002Fwww-project-top-ten\u002F",[2400],[2470,2471,2472],"style",{},"html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .skxfh, html code.shiki .skxfh{--shiki-light:#E53935;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZMiF, html code.shiki .sZMiF{--shiki-light:#E2931D;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s99_P, html code.shiki .s99_P{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#E36209;--shiki-default-font-style:inherit;--shiki-dark:#FFAB70;--shiki-dark-font-style:inherit}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .sucvu, html code.shiki .sucvu{--shiki-light:#E53935;--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sVXei, html code.shiki .sVXei{--shiki-light:#E53935;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZUrc, html code.shiki .sZUrc{--shiki-light:#E53935;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sQzsp, html code.shiki .sQzsp{--shiki-light:#E53935;--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .s9AJx, html code.shiki .s9AJx{--shiki-light:#9C3EDA;--shiki-default:#6F42C1;--shiki-dark:#B392F0}",{"title":321,"searchDepth":343,"depth":343,"links":2474},[2475,2476,2480,2486,2487,2492,2497,2501,2505,2506],{"id":28,"depth":343,"text":29},{"id":93,"depth":343,"text":94,"children":2477},[2478,2479],{"id":98,"depth":354,"text":99},{"id":133,"depth":354,"text":134},{"id":163,"depth":343,"text":164,"children":2481},[2482,2483,2484,2485],{"id":167,"depth":354,"text":168},{"id":190,"depth":354,"text":191},{"id":244,"depth":354,"text":245},{"id":280,"depth":354,"text":281},{"id":300,"depth":343,"text":301},{"id":460,"depth":343,"text":461,"children":2488},[2489,2491],{"id":464,"depth":354,"text":2490},"5.1 axios 请求层（示例）",{"id":1620,"depth":354,"text":1621},{"id":1795,"depth":343,"text":2493,"children":2494},"6. iframe 并行阶段的体验优化",[2495,2496],{"id":1802,"depth":354,"text":1803},{"id":1824,"depth":354,"text":1825},{"id":1966,"depth":343,"text":1967,"children":2498},[2499,2500],{"id":1981,"depth":354,"text":1982},{"id":2277,"depth":354,"text":2278},{"id":2319,"depth":343,"text":2320,"children":2502},[2503,2504],{"id":2323,"depth":354,"text":2324},{"id":2357,"depth":354,"text":2358},{"id":2377,"depth":343,"text":2378},{"id":2386,"depth":343,"text":2386},{"title":8,"description":9},"knowledge\u002Fteam-documents\u002F04","knowledge\u002Fknowledge\u002Fteam-documents\u002F04.md","md",{"items":2512},[2513,2517,2521,2525,2529,2533],{"path":2514,"title":2515,"description":2516,"category":10,"score":715},"\u002Fknowledge\u002Fteam-documents\u002F19","GridView 宫格加载渲染优化","系统首页 GridView 宫格模块接口耗时不高，但首次进入总耗时接近 22s。复盘耗时分层定位过程、代码层面的瓶颈（约 2000 行、100 个 tab 重复节点）、优化手段与最终指标对比。",{"path":2518,"title":2519,"description":2520,"category":10,"score":715},"\u002Fknowledge\u002Fteam-documents\u002F13","新商家系统性能优化实践","商家系统新版本上线后，团队持续针对构建效率与用户体验做了一轮系统性优化。本文记录核心方案与落地结果，供后续版本复用。",{"path":2522,"title":2523,"description":2524,"category":10,"score":715},"\u002Fknowledge\u002Fteam-documents\u002F02","防篡改水印","在各类管理后台、SaaS 平台或内部系统中，页面水印已经是非常常见的能力：一方面在截图时携带账号、姓名、时间等信息，降低截图外传的风险；另一方面在上传图片或文档预览时，通过水印标记上传者与时间，满足审计和合规需求。",{"path":2526,"title":2527,"description":2528,"category":10,"score":715},"\u002Fknowledge\u002Fteam-documents\u002F10","图片上传前的自定义水印实践","项目需要在图片上传前叠加动态水印。水印内容不是固定文案，而是由多个动态元素组成（如时间、地点、业务字段等）。",{"path":2530,"title":2531,"description":2532,"category":10,"score":666},"\u002Fknowledge\u002Fteam-documents\u002F03","键盘弹起导致底部被顶起问题（H5 适配）","在移动端 H5 页面中，当用户聚焦 input、textarea 等可编辑元素、软键盘弹出时，常见表现包括：",{"path":2534,"title":2535,"description":2536,"category":10,"score":666},"\u002Fknowledge\u002Fteam-documents\u002F22","企业微信 uni-app H5：OAuth 回退白屏与列表缓存","第三方 BI 报表项目，企业微信内嵌 H5，uni-app 编译，history 模式。主页面 BiLink 同时承担静默授权和列表展示，上线后碰到两个问题：授权完清掉 URL 参数，iOS 侧滑返回还是白屏；列表加了缓存以后，刷新行…",{"series":2538,"title":2539,"items":2540,"index":354,"prev":2614,"next":2615},"team-documents","team documents",[2541,2545,2547,2549,2550,2554,2558,2562,2566,2570,2572,2576,2580,2582,2586,2590,2594,2598,2602,2604,2608,2612],{"path":2542,"title":2543,"date":2544,"pinned":18},"\u002Fknowledge\u002Fteam-documents\u002F01","执行价格弹窗：表头 \u002F 表体合并与可配置表格","2024-07-27",{"path":2522,"title":2523,"date":2546,"pinned":18},"2024-01-09",{"path":2530,"title":2531,"date":2548,"pinned":18},"2024-09-17",{"path":7,"title":8,"date":17,"pinned":18},{"path":2551,"title":2552,"date":2553,"pinned":18},"\u002Fknowledge\u002Fteam-documents\u002F05","内部插件 \u002F 工具库开发实践","2021-08-19",{"path":2555,"title":2556,"date":2557,"pinned":18},"\u002Fknowledge\u002Fteam-documents\u002F06","企业微信与小程序工单问题","2025-10-01",{"path":2559,"title":2560,"date":2561,"pinned":18},"\u002Fknowledge\u002Fteam-documents\u002F07","企业微信群工具打开缓慢原因分析","2020-04-12",{"path":2563,"title":2564,"date":2565,"pinned":18},"\u002Fknowledge\u002Fteam-documents\u002F08","属性排序需求","2024-06-16",{"path":2567,"title":2568,"date":2569,"pinned":18},"\u002Fknowledge\u002Fteam-documents\u002F09","同业主重复单提醒规则","2020-06-07",{"path":2526,"title":2527,"date":2571,"pinned":18},"2021-06-10",{"path":2573,"title":2574,"date":2575,"pinned":18},"\u002Fknowledge\u002Fteam-documents\u002F11","系统下单（重复单提示框）异常复盘","2023-11-21",{"path":2577,"title":2578,"date":2579,"pinned":18},"\u002Fknowledge\u002Fteam-documents\u002F12","系统自动更新说明","2025-05-26",{"path":2518,"title":2519,"date":2581,"pinned":18},"2025-04-08",{"path":2583,"title":2584,"date":2585,"pinned":18},"\u002Fknowledge\u002Fteam-documents\u002F14","选项卡切换与 Loading 状态封装","2019-11-03",{"path":2587,"title":2588,"date":2589,"pinned":18},"\u002Fknowledge\u002Fteam-documents\u002F15","移动端返回事件触发两次问题复盘","2021-03-09",{"path":2591,"title":2592,"date":2593,"pinned":18},"\u002Fknowledge\u002Fteam-documents\u002F16","在线协作表格接入实践","2023-10-27",{"path":2595,"title":2596,"date":2597,"pinned":18},"\u002Fknowledge\u002Fteam-documents\u002F17","支付中转页面升级实践","2022-10-15",{"path":2599,"title":2600,"date":2601,"pinned":18},"\u002Fknowledge\u002Fteam-documents\u002F18","中英文输入法下搜索触发冲突复盘","2024-09-01",{"path":2514,"title":2515,"date":2603,"pinned":18},"2026-03-17",{"path":2605,"title":2606,"date":2607,"pinned":18},"\u002Fknowledge\u002Fteam-documents\u002F20","H5 GPS 定位问题说明","2023-08-31",{"path":2609,"title":2610,"date":2611,"pinned":18},"\u002Fknowledge\u002Fteam-documents\u002F21","vxe-table 行 Hover 联动高亮","2024-08-31",{"path":2534,"title":2535,"date":2613,"pinned":18},"2023-08-17",{"path":2530,"title":2531,"date":2548,"pinned":18},{"path":2551,"title":2552,"date":2553,"pinned":18}]