[{"data":1,"prerenderedAt":466},["ShallowReactive",2],{"msfe-doc:\u002Fextensions\u002Fresources":3},{"id":4,"title":5,"body":6,"description":451,"extension":452,"keywords":453,"locale":458,"meta":459,"navigation":460,"order":289,"path":461,"section":462,"seo":463,"stem":464,"__hash__":465},"docs\u002Fextensions\u002Fresources.zh-CN.md","异步资源与清理",{"type":7,"value":8,"toc":443},"minimark",[9,13,90,101,104,339,349,359,363,381,393,397,404,412,415,426,433,436,439],[10,11,12],"h2",{"id":12},"选择最短的正确生命周期",[14,15,16,32],"table",{},[17,18,19],"thead",{},[20,21,22,26,29],"tr",{},[23,24,25],"th",{},"作用域",[23,27,28],{},"何时结束",[23,30,31],{},"适合的资源",[33,34,35,50,63,77],"tbody",{},[20,36,37,44,47],{},[38,39,40],"td",{},[41,42,43],"code",{},"editor.scope",[38,45,46],{},"实例销毁",[38,48,49],{},"实例服务、全局监听",[20,51,52,57,60],{},[38,53,54],{},[41,55,56],{},"editor.documentScope",[38,58,59],{},"替换文档或实例销毁",[38,61,62],{},"当前文件的保存、上传、查询",[20,64,65,71,74],{},[38,66,67,68],{},"ComponentView ",[41,69,70],{},"mount.scope",[38,72,73],{},"视图卸载或文档替换",[38,75,76],{},"图表、预览、尺寸观察",[20,78,79,84,87],{},[38,80,81],{},[41,82,83],{},"scope.fork()",[38,85,86],{},"显式取消或父作用域结束",[38,88,89],{},"一次输入对应的请求",[91,92,93,96,97,100],"p",{},[41,94,95],{},"documentId"," 相同不等于同一次文档生命周期。",[41,98,99],{},"replaceDocument()"," 会结束旧文档作用域，业务请求应该跟随这个信号，而不是只比较文件名。",[10,102,103],{"id":103},"资源租约",[105,106,111],"pre",{"className":107,"code":108,"language":109,"meta":110,"style":110},"language-ts shiki shiki-themes github-light github-light github-dark","const request = editor.documentScope.acquire(\n  async signal => {\n    const response = await fetch('\u002Fapi\u002Fpreview', { signal })\n    if (!response.ok) throw new Error(`HTTP ${response.status}`)\n    const url = URL.createObjectURL(await response.blob())\n    return { value: url, release: () => URL.revokeObjectURL(url) }\n  },\n  url => { console.log('Ready preview URL', url) },\n)\n\u002F\u002F The host may cancel this request before replacing the document.\nrequest.dispose()\n","ts","",[41,112,113,140,156,183,227,259,287,293,315,320,327],{"__ignoreMap":110},[114,115,118,122,126,129,133,137],"span",{"class":116,"line":117},"line",1,[114,119,121],{"class":120},"sDD9d","const",[114,123,125],{"class":124},"sUNQB"," request",[114,127,128],{"class":120}," =",[114,130,132],{"class":131},"sVPnS"," editor.documentScope.",[114,134,136],{"class":135},"sgQzM","acquire",[114,138,139],{"class":131},"(\n",[114,141,143,146,150,153],{"class":116,"line":142},2,[114,144,145],{"class":120},"  async",[114,147,149],{"class":148},"sde8i"," signal",[114,151,152],{"class":120}," =>",[114,154,155],{"class":131}," {\n",[114,157,159,162,165,167,170,173,176,180],{"class":116,"line":158},3,[114,160,161],{"class":120},"    const",[114,163,164],{"class":124}," response",[114,166,128],{"class":120},[114,168,169],{"class":120}," await",[114,171,172],{"class":135}," fetch",[114,174,175],{"class":131},"(",[114,177,179],{"class":178},"s-HEn","'\u002Fapi\u002Fpreview'",[114,181,182],{"class":131},", { signal })\n",[114,184,186,189,192,195,198,201,204,207,209,212,215,218,221,224],{"class":116,"line":185},4,[114,187,188],{"class":120},"    if",[114,190,191],{"class":131}," (",[114,193,194],{"class":120},"!",[114,196,197],{"class":131},"response.ok) ",[114,199,200],{"class":120},"throw",[114,202,203],{"class":120}," new",[114,205,206],{"class":135}," Error",[114,208,175],{"class":131},[114,210,211],{"class":178},"`HTTP ${",[114,213,214],{"class":131},"response",[114,216,217],{"class":178},".",[114,219,220],{"class":131},"status",[114,222,223],{"class":178},"}`",[114,225,226],{"class":131},")\n",[114,228,230,232,235,237,240,242,245,247,250,253,256],{"class":116,"line":229},5,[114,231,161],{"class":120},[114,233,234],{"class":124}," url",[114,236,128],{"class":120},[114,238,239],{"class":124}," URL",[114,241,217],{"class":131},[114,243,244],{"class":135},"createObjectURL",[114,246,175],{"class":131},[114,248,249],{"class":120},"await",[114,251,252],{"class":131}," response.",[114,254,255],{"class":135},"blob",[114,257,258],{"class":131},"())\n",[114,260,262,265,268,271,274,277,279,281,284],{"class":116,"line":261},6,[114,263,264],{"class":120},"    return",[114,266,267],{"class":131}," { value: url, ",[114,269,270],{"class":135},"release",[114,272,273],{"class":131},": () ",[114,275,276],{"class":120},"=>",[114,278,239],{"class":124},[114,280,217],{"class":131},[114,282,283],{"class":135},"revokeObjectURL",[114,285,286],{"class":131},"(url) }\n",[114,288,290],{"class":116,"line":289},7,[114,291,292],{"class":131},"  },\n",[114,294,296,299,301,304,307,309,312],{"class":116,"line":295},8,[114,297,298],{"class":148},"  url",[114,300,152],{"class":120},[114,302,303],{"class":131}," { console.",[114,305,306],{"class":135},"log",[114,308,175],{"class":131},[114,310,311],{"class":178},"'Ready preview URL'",[114,313,314],{"class":131},", url) },\n",[114,316,318],{"class":116,"line":317},9,[114,319,226],{"class":131},[114,321,323],{"class":116,"line":322},10,[114,324,326],{"class":325},"sqmzS","\u002F\u002F The host may cancel this request before replacing the document.\n",[114,328,330,333,336],{"class":116,"line":329},11,[114,331,332],{"class":131},"request.",[114,334,335],{"class":135},"dispose",[114,337,338],{"class":131},"()\n",[91,340,341,342,345,346,348],{},"这是宿主自行实现 ",[41,343,344],{},"\u002Fapi\u002Fpreview"," 的接入形状，不是 MSFE 提供的服务。",[41,347,136],{}," 只向仍有效的请求提交结果；取消后才到达的租约也会释放。每次获取都要返回自己拥有的 lease，不能把一个共享对象的 dispose 随意交给多个视图。",[91,350,351,354,355,358],{},[41,352,353],{},"scope.own(cleanup)"," 注册幂等清理并返回提前释放函数；",[41,356,357],{},"dispose()"," 先 abort，再逆序释放。第三方库若不能取消计算，仍要在使用结果前检查信号，并释放迟到资源。",[10,360,362],{"id":361},"sourcedefinitionload","SourceDefinition.load",[91,364,365,366,369,370,373,374,376,377,380],{},"字段绑定资源可通过 ",[41,367,368],{},"defineSource\u003CAttrs, Resource>({ load(attrs, signal) })"," 返回 ",[41,371,372],{},"{ value, release? }","。NodeView 从 ",[41,375,220],{}," 和 ",[41,378,379],{},"resource"," 读取加载状态，不要自己再发一次相同请求。",[91,382,383,386,387,392],{},[41,384,385],{},"attrs"," 应保存可持久的业务标识，资源值可以是解析后的 URL、只读展示数据或布局结果。密钥、登录令牌和临时 object URL 不写入 Markdown。图片完整接入见 ",[388,389,391],"a",{"href":390},"\u002Fcustomization\u002Fimages","图片与外部资源","。",[10,394,396],{"id":395},"错误与重试是-ui-协议","错误与重试是 UI 协议",[91,398,399,400,403],{},"作用域报告错误并不等于自动显示错误界面。组件负责 ",[41,401,402],{},"loading \u002F ready \u002F error"," 状态、可读源码回退和重试动作。每次重试先结束旧子作用域；不能同时保留多张图表或多个网络响应写入口。",[91,405,406,407,411],{},"分配资源后、返回租约之前抛错也必须清理。可以在创建实例后立即注册清理，或用 try\u002Ffinally 管理部分初始化。图表例子的 ",[388,408,410],{"href":409},"\u002Fextensions\u002Fdiagrams","完整 renderer"," 包含观察器与实例释放。",[10,413,414],{"id":414},"资源完成后写回源码",[91,416,417,418,421,422,392],{},"“请求仍有效”与“原来的源码位置仍有效”是两回事。上传完成后不要用缓存的数字偏移直接插入。创建文档子作用域，并用 ",[41,419,420],{},"captureAnchor()"," 保留原意图；结果到达后重新取得 target，再通过命令写回。详见 ",[388,423,425],{"href":424},"\u002Fguide\u002Fselection","完整异步目标函数",[91,427,428,429,392],{},"保存不需要用服务端响应替换全文。顺序写入、失败重试和离开前 flush 的完整接入见 ",[388,430,432],{"href":431},"\u002Fguide\u002Fsaving","读取与保存",[10,434,435],{"id":435},"清理测试",[91,437,438],{},"为加载器提供可手动完成的 Promise：先发 A 再发 B，让 A 最后到达；销毁编辑器后完成请求；使用同一 documentId 换文档；让渲染在初始化中途抛错。验证没有旧 UI、旧源码修改、重复 finalizer 或遗留观察器。",[440,441,442],"style",{},"html pre.shiki code .sDD9d, html code.shiki .sDD9d{--shiki-default:#D73A49;--shiki-light:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sUNQB, html code.shiki .sUNQB{--shiki-default:#005CC5;--shiki-light:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sVPnS, html code.shiki .sVPnS{--shiki-default:#24292E;--shiki-light:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sgQzM, html code.shiki .sgQzM{--shiki-default:#6F42C1;--shiki-light:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sde8i, html code.shiki .sde8i{--shiki-default:#E36209;--shiki-light:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .s-HEn, html code.shiki .s-HEn{--shiki-default:#032F62;--shiki-light:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sqmzS, html code.shiki .sqmzS{--shiki-default:#6A737D;--shiki-light:#6A737D;--shiki-dark:#6A737D}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 .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 .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);}",{"title":110,"searchDepth":142,"depth":142,"links":444},[445,446,447,448,449,450],{"id":12,"depth":142,"text":12},{"id":103,"depth":142,"text":103},{"id":361,"depth":142,"text":362},{"id":395,"depth":142,"text":396},{"id":414,"depth":142,"text":414},{"id":435,"depth":142,"text":435},"组织加载、取消、重试、锚点和释放，避免旧结果写入新文档。","md",[454,455,456,457,335],"TaskScope","ResourceLease","AbortSignal","load","zh-CN",{},true,"\u002Fextensions\u002Fresources","扩展开发",{"title":5,"description":451},"extensions\u002Fresources.zh-CN","1AVvnLhgcMOmGVLV4XZzl7EYomwz3iigAb4KMo5oJ1U",1793046311420]