[{"data":1,"prerenderedAt":833},["ShallowReactive",2],{"msfe-doc:\u002Fconcepts\u002Fextensions":3},{"id":4,"title":5,"body":6,"description":822,"extension":823,"keywords":824,"locale":826,"meta":827,"navigation":196,"order":175,"path":828,"section":829,"seo":830,"stem":831,"__hash__":832},"docs\u002Fconcepts\u002Fextensions.zh-CN.md","扩展模型与组合",{"type":7,"value":8,"toc":813},"minimark",[9,13,29,158,162,386,404,408,438,445,449,522,544,549,552,648,662,665,771,783,786,809],[10,11,12],"h2",{"id":12},"先选择功能归属",[14,15,16,20,21,24,25,28],"p",{},[17,18,19],"code",{},"Extension"," 提供通用行为和预设；",[17,22,23],{},"Node"," 描述源码节点；",[17,26,27],{},"Mark"," 描述行内标记。三者共用声明式钩子，不会创建 ProseMirror schema 或另一份 JSON 文档。",[30,31,32,48],"table",{},[33,34,35],"thead",{},[36,37,38,42,45],"tr",{},[39,40,41],"th",{},"需求",[39,43,44],{},"推荐入口",[39,46,47],{},"完整教程",[49,50,51,69,85,105,121,137],"tbody",{},[36,52,53,57,62],{},[54,55,56],"td",{},"修改已有功能默认值",[54,58,59],{},[17,60,61],{},".configure()",[54,63,64],{},[65,66,68],"a",{"href":67},"\u002Fextensions\u002Fcontainers","容器与嵌套块",[36,70,71,74,79],{},[54,72,73],{},"保留语法，换按钮和组件",[54,75,76],{},[17,77,78],{},".extend({ addNodeView() {} })",[54,80,81],{},[65,82,84],{"href":83},"\u002Fextensions\u002Fcode-blocks","代码块定制",[36,86,87,90,99],{},[54,88,89],{},"新语法和可写字段",[54,91,92,95,96],{},[17,93,94],{},"Node.create()"," + ",[17,97,98],{},"defineSource()",[54,100,101],{},[65,102,104],{"href":103},"\u002Fextensions\u002Fcustom-node","自定义节点",[36,106,107,110,115],{},[54,108,109],{},"新增 Markdown 解析规则",[54,111,112],{},[17,113,114],{},"addMarkdownExtensions()",[54,116,117],{},[65,118,120],{"href":119},"\u002Fextensions\u002Fmarkdown-plugins","Markdown 插件",[36,122,123,126,131],{},[54,124,125],{},"多行图表或结构性预览",[54,127,128],{},[17,129,130],{},"addProjectionLayers()",[54,132,133],{},[65,134,136],{"href":135},"\u002Fextensions\u002Fdiagrams","图表与代码块预览",[36,138,139,142,147],{},[54,140,141],{},"临时状态、快捷键、输入规则",[54,143,144],{},[17,145,146],{},"Extension.create()",[54,148,149,153,154],{},[65,150,152],{"href":151},"\u002Fguide\u002Fstate","状态","、",[65,155,157],{"href":156},"\u002Fextensions\u002Finput-rules","输入规则",[10,159,161],{"id":160},"create声明可复用定义","create：声明可复用定义",[163,164,169],"pre",{"className":165,"code":166,"language":167,"meta":168,"style":168},"language-ts shiki shiki-themes github-light github-light github-dark","import { Extension } from '@quirekit\u002Fmsfe-core'\n\ninterface Options {\n  \u002F** Host application name used by this extension. *\u002F\n  label: string\n}\ninterface Storage {\n  \u002F** Accepted source changes observed in this installation. *\u002F\n  changes: number\n}\nexport const Audit = Extension.create\u003COptions, Storage>({\n  name: 'audit',\n  addOptions() { return { label: 'editor' } },\n  addStorage() { return { changes: 0 } },\n  onUpdate() { this.storage.changes += 1 },\n})\n","ts","",[17,170,171,191,198,211,218,232,238,248,254,265,270,306,318,339,357,380],{"__ignoreMap":168},[172,173,176,180,184,187],"span",{"class":174,"line":175},"line",1,[172,177,179],{"class":178},"sDD9d","import",[172,181,183],{"class":182},"sVPnS"," { Extension } ",[172,185,186],{"class":178},"from",[172,188,190],{"class":189},"s-HEn"," '@quirekit\u002Fmsfe-core'\n",[172,192,194],{"class":174,"line":193},2,[172,195,197],{"emptyLinePlaceholder":196},true,"\n",[172,199,201,204,208],{"class":174,"line":200},3,[172,202,203],{"class":178},"interface",[172,205,207],{"class":206},"sgQzM"," Options",[172,209,210],{"class":182}," {\n",[172,212,214],{"class":174,"line":213},4,[172,215,217],{"class":216},"sqmzS","  \u002F** Host application name used by this extension. *\u002F\n",[172,219,221,225,228],{"class":174,"line":220},5,[172,222,224],{"class":223},"sde8i","  label",[172,226,227],{"class":178},":",[172,229,231],{"class":230},"sUNQB"," string\n",[172,233,235],{"class":174,"line":234},6,[172,236,237],{"class":182},"}\n",[172,239,241,243,246],{"class":174,"line":240},7,[172,242,203],{"class":178},[172,244,245],{"class":206}," Storage",[172,247,210],{"class":182},[172,249,251],{"class":174,"line":250},8,[172,252,253],{"class":216},"  \u002F** Accepted source changes observed in this installation. *\u002F\n",[172,255,257,260,262],{"class":174,"line":256},9,[172,258,259],{"class":223},"  changes",[172,261,227],{"class":178},[172,263,264],{"class":230}," number\n",[172,266,268],{"class":174,"line":267},10,[172,269,237],{"class":182},[172,271,273,276,279,282,285,288,291,294,297,300,303],{"class":174,"line":272},11,[172,274,275],{"class":178},"export",[172,277,278],{"class":178}," const",[172,280,281],{"class":230}," Audit",[172,283,284],{"class":178}," =",[172,286,287],{"class":182}," Extension.",[172,289,290],{"class":206},"create",[172,292,293],{"class":182},"\u003C",[172,295,296],{"class":206},"Options",[172,298,299],{"class":182},", ",[172,301,302],{"class":206},"Storage",[172,304,305],{"class":182},">({\n",[172,307,309,312,315],{"class":174,"line":308},12,[172,310,311],{"class":182},"  name: ",[172,313,314],{"class":189},"'audit'",[172,316,317],{"class":182},",\n",[172,319,321,324,327,330,333,336],{"class":174,"line":320},13,[172,322,323],{"class":206},"  addOptions",[172,325,326],{"class":182},"() { ",[172,328,329],{"class":178},"return",[172,331,332],{"class":182}," { label: ",[172,334,335],{"class":189},"'editor'",[172,337,338],{"class":182}," } },\n",[172,340,342,345,347,349,352,355],{"class":174,"line":341},14,[172,343,344],{"class":206},"  addStorage",[172,346,326],{"class":182},[172,348,329],{"class":178},[172,350,351],{"class":182}," { changes: ",[172,353,354],{"class":230},"0",[172,356,338],{"class":182},[172,358,360,363,365,368,371,374,377],{"class":174,"line":359},15,[172,361,362],{"class":206},"  onUpdate",[172,364,326],{"class":182},[172,366,367],{"class":230},"this",[172,369,370],{"class":182},".storage.changes ",[172,372,373],{"class":178},"+=",[172,375,376],{"class":230}," 1",[172,378,379],{"class":182}," },\n",[172,381,383],{"class":174,"line":382},16,[172,384,385],{"class":182},"})\n",[14,387,388,389,392,393,396,397,399,400,403],{},"定义可在多个编辑器之间复用。",[17,390,391],{},"options"," 与 ",[17,394,395],{},"storage"," 在各次安装中独立解析，不能把实例状态放在模块级变量中。",[17,398,395],{}," 适合临时服务和计数；需要事务映射、撤销或投影依赖的状态使用 ",[17,401,402],{},"defineState()","。",[10,405,407],{"id":406},"configure修改配置","configure：修改配置",[163,409,411],{"className":165,"code":410,"language":167,"meta":168,"style":168},"const ReportAudit = Audit.configure({ label: 'report' })\n",[17,412,413],{"__ignoreMap":168},[172,414,415,418,421,423,426,429,432,435],{"class":174,"line":175},[172,416,417],{"class":178},"const",[172,419,420],{"class":230}," ReportAudit",[172,422,284],{"class":178},[172,424,425],{"class":182}," Audit.",[172,427,428],{"class":206},"configure",[172,430,431],{"class":182},"({ label: ",[172,433,434],{"class":189},"'report'",[172,436,437],{"class":182}," })\n",[14,439,440,441,444],{},"返回新定义，原定义保持不变。普通对象递归合并；数组整体替换，函数和非普通对象按值替换。配置容器 ",[17,442,443],{},"types"," 时，要显式保留仍需使用的默认类型。它不是已挂载实例的热更新方法；明暗、模式、只读使用实例对应方法。",[10,446,448],{"id":447},"extend继承和覆盖钩子","extend：继承和覆盖钩子",[163,450,452],{"className":165,"code":451,"language":167,"meta":168,"style":168},"const Audited = Audit.extend({\n  onUpdate(event) {\n    this.parent?.(event)\n    console.debug(this.options.label, event.revision)\n  },\n})\n",[17,453,454,471,484,498,513,518],{"__ignoreMap":168},[172,455,456,458,461,463,465,468],{"class":174,"line":175},[172,457,417],{"class":178},[172,459,460],{"class":230}," Audited",[172,462,284],{"class":178},[172,464,425],{"class":182},[172,466,467],{"class":206},"extend",[172,469,470],{"class":182},"({\n",[172,472,473,475,478,481],{"class":174,"line":193},[172,474,362],{"class":206},[172,476,477],{"class":182},"(",[172,479,480],{"class":223},"event",[172,482,483],{"class":182},") {\n",[172,485,486,489,492,495],{"class":174,"line":200},[172,487,488],{"class":230},"    this",[172,490,491],{"class":182},".",[172,493,494],{"class":206},"parent",[172,496,497],{"class":182},"?.(event)\n",[172,499,500,503,506,508,510],{"class":174,"line":213},[172,501,502],{"class":182},"    console.",[172,504,505],{"class":206},"debug",[172,507,477],{"class":182},[172,509,367],{"class":230},[172,511,512],{"class":182},".options.label, event.revision)\n",[172,514,515],{"class":174,"line":220},[172,516,517],{"class":182},"  },\n",[172,519,520],{"class":174,"line":234},[172,521,385],{"class":182},[14,523,524,525,528,529,153,532,153,535,153,538,541,542,403],{},"覆盖钩子不会自动合并父级的结果。需要保留父行为时调用当前钩子的 ",[17,526,527],{},"this.parent?.()","，数组结果由你显式拼接。需要 ",[17,530,531],{},"this.options",[17,533,534],{},"this.storage",[17,536,537],{},"this.editor",[17,539,540],{},"this.parent"," 的钩子使用普通方法，不能用箭头函数绑定外部 ",[17,543,367],{},[14,545,546,548],{},[17,547,467],{}," 默认继承名称。用新定义替换预设中对应项，不能把同名原定义和派生定义一起安装。改名也不意味着可以让两个扩展随意占有同一段替换投影。",[10,550,551],{"id":551},"安装一个预设",[163,553,555],{"className":165,"code":554,"language":167,"meta":168,"style":168},"import { StarterKit } from '@quirekit\u002Fmsfe-starter-kit'\nimport { CodeBlock } from '@quirekit\u002Fmsfe-code-block'\n\nconst CustomCodeBlock = CodeBlock.extend({\n  \u002F\u002F Add the renderer from the code-block tutorial here.\n})\nconst extensions = [\n  StarterKit.configure({ codeBlock: CustomCodeBlock, image: false }),\n  Audit,\n]\n",[17,556,557,569,581,585,601,606,610,622,638,643],{"__ignoreMap":168},[172,558,559,561,564,566],{"class":174,"line":175},[172,560,179],{"class":178},[172,562,563],{"class":182}," { StarterKit } ",[172,565,186],{"class":178},[172,567,568],{"class":189}," '@quirekit\u002Fmsfe-starter-kit'\n",[172,570,571,573,576,578],{"class":174,"line":193},[172,572,179],{"class":178},[172,574,575],{"class":182}," { CodeBlock } ",[172,577,186],{"class":178},[172,579,580],{"class":189}," '@quirekit\u002Fmsfe-code-block'\n",[172,582,583],{"class":174,"line":200},[172,584,197],{"emptyLinePlaceholder":196},[172,586,587,589,592,594,597,599],{"class":174,"line":213},[172,588,417],{"class":178},[172,590,591],{"class":230}," CustomCodeBlock",[172,593,284],{"class":178},[172,595,596],{"class":182}," CodeBlock.",[172,598,467],{"class":206},[172,600,470],{"class":182},[172,602,603],{"class":174,"line":220},[172,604,605],{"class":216},"  \u002F\u002F Add the renderer from the code-block tutorial here.\n",[172,607,608],{"class":174,"line":234},[172,609,385],{"class":182},[172,611,612,614,617,619],{"class":174,"line":240},[172,613,417],{"class":178},[172,615,616],{"class":230}," extensions",[172,618,284],{"class":178},[172,620,621],{"class":182}," [\n",[172,623,624,627,629,632,635],{"class":174,"line":250},[172,625,626],{"class":182},"  StarterKit.",[172,628,428],{"class":206},[172,630,631],{"class":182},"({ codeBlock: CustomCodeBlock, image: ",[172,633,634],{"class":230},"false",[172,636,637],{"class":182}," }),\n",[172,639,640],{"class":174,"line":256},[172,641,642],{"class":182},"  Audit,\n",[172,644,645],{"class":174,"line":267},[172,646,647],{"class":182},"]\n",[14,649,650,653,654,657,658,661],{},[17,651,652],{},"addExtensions()"," 用于组合业务预设；应用只安装该预设一次。",[17,655,656],{},"requires"," 声明依赖的扩展名称。满足依赖的扩展按 ",[17,659,660],{},"priority"," 从高到低安装，默认优先级为 100。名称冲突、缺失依赖等会在挂载前报告，不应靠调整数组顺序掩盖。",[10,663,664],{"id":664},"生命周期顺序与可做的事",[30,666,667,680],{},[33,668,669],{},[36,670,671,674,677],{},[39,672,673],{},"阶段",[39,675,676],{},"已有内容",[39,678,679],{},"应做的事",[49,681,682,693,706,719,732,745,758],{},[36,683,684,687,690],{},[54,685,686],{},"配置与状态构建",[54,688,689],{},"安装定义、配置、状态槽",[54,691,692],{},"保持可重复、无 DOM 副作用",[36,694,695,700,703],{},[54,696,697],{},[17,698,699],{},"onBeforeCreate",[54,701,702],{},"配置及状态，尚无编辑器 DOM",[54,704,705],{},"准备不依赖挂载的逻辑",[36,707,708,713,716],{},[54,709,710],{},[17,711,712],{},"onCreate",[54,714,715],{},"已挂载实例",[54,717,718],{},"建立宿主订阅，可返回清理函数",[36,720,721,726,729],{},[54,722,723],{},[17,724,725],{},"onUpdate",[54,727,728],{},"已接受源码变化",[54,730,731],{},"增量观察、标记未保存",[36,733,734,739,742],{},[54,735,736],{},[17,737,738],{},"onTransaction",[54,740,741],{},"包括选区和效果的已接受事务",[54,743,744],{},"有界读取，避免每次扫描全文",[36,746,747,752,755],{},[54,748,749],{},[17,750,751],{},"onSelectionUpdate",[54,753,754],{},"选区确实发生变化",[54,756,757],{},"更新相关工具栏状态",[36,759,760,765,768],{},[54,761,762],{},[17,763,764],{},"onDestroy",[54,766,767],{},"进入释放过程",[54,769,770],{},"逆序清理，允许重复释放资源的防御",[14,772,773,774,778,779,403],{},"组件自己的生命周期比编辑器短。滚动、源码显露、换模式可能导致组件卸载；",[65,775,777],{"href":776},"\u002Fextensions\u002Fresources","异步资源"," 应绑定对应作用域。全部钩子签名见 ",[65,780,782],{"href":781},"\u002Fapi\u002Fextensions","扩展 API",[10,784,785],{"id":785},"从早期描述符迁移",[14,787,788,791,792,795,796,798,799,153,802,153,805,808],{},[17,789,790],{},"defineExtension()"," 和 ",[17,793,794],{},"defineNodeView()"," 仍是公开的底层入口，但其旧描述符配置不能与现代 ",[17,797,61],{}," 的深合并语义混用。新增功能优先采用这里的作者 API。MSFE 的 Tiptap 风格命名不意味着支持 ",[17,800,801],{},"parseHTML",[17,803,804],{},"renderHTML",[17,806,807],{},"content"," schema 或 ProseMirror 插件直接安装。",[810,811,812],"style",{},"html pre.shiki code .sDD9d, html code.shiki .sDD9d{--shiki-default:#D73A49;--shiki-light:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVPnS, html code.shiki .sVPnS{--shiki-default:#24292E;--shiki-light:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s-HEn, html code.shiki .s-HEn{--shiki-default:#032F62;--shiki-light:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sgQzM, html code.shiki .sgQzM{--shiki-default:#6F42C1;--shiki-light:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sqmzS, html code.shiki .sqmzS{--shiki-default:#6A737D;--shiki-light:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sde8i, html code.shiki .sde8i{--shiki-default:#E36209;--shiki-light:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sUNQB, html code.shiki .sUNQB{--shiki-default:#005CC5;--shiki-light:#005CC5;--shiki-dark:#79B8FF}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":168,"searchDepth":193,"depth":193,"links":814},[815,816,817,818,819,820,821],{"id":12,"depth":193,"text":12},{"id":160,"depth":193,"text":161},{"id":406,"depth":193,"text":407},{"id":447,"depth":193,"text":448},{"id":551,"depth":193,"text":551},{"id":664,"depth":193,"text":664},{"id":785,"depth":193,"text":785},"理解 create、configure、extend、安装顺序和完整生命周期。","md",[19,428,467,660,656,825],"lifecycle","zh-CN",{},"\u002Fconcepts\u002Fextensions","核心概念",{"title":5,"description":822},"concepts\u002Fextensions.zh-CN","mTenMT1_zcMtAcMeMm5b0kT7IOD41rX7scXd36i-VeU",1793046311419]