[{"data":1,"prerenderedAt":618},["ShallowReactive",2],{"msfe-doc:\u002Fguide\u002Fvue":3},{"id":4,"title":5,"body":6,"description":602,"extension":603,"keywords":604,"locale":610,"meta":611,"navigation":123,"order":612,"path":613,"section":614,"seo":615,"stem":616,"__hash__":617},"docs\u002Fguide\u002Fvue.zh-CN.md","Vue 接入",{"type":7,"value":8,"toc":593},"minimark",[9,13,37,309,312,334,337,359,363,367,382,460,476,499,510,514,521,524,540,544,551,558,562,578,589],[10,11,12],"h2",{"id":12},"创建与挂载",[14,15,16,20,21,24,25,28,29,32,33,36],"p",{},[17,18,19],"code",{},"useMsfeEditor()"," 返回浅引用 ",[17,22,23],{},"editor"," 和组件宿主 ",[17,26,27],{},"host","。在模板中渲染 ",[17,30,31],{},"VueNodeViewHost","，组件视图便能保留当前应用的 ",[17,34,35],{},"provide\u002Finject"," 上下文。",[38,39,44],"pre",{"className":40,"code":41,"language":42,"meta":43,"style":43},"language-vue shiki shiki-themes github-light github-light github-dark","\u003Cscript setup lang=\"ts\">\nimport { shallowRef } from 'vue'\nimport { useMsfeEditor, VueNodeViewHost } from '@quirekit\u002Fmsfe-vue'\nimport { StarterKit } from '@quirekit\u002Fmsfe-starter-kit'\n\nconst parent = shallowRef\u003CHTMLElement | null>(null)\nconst { editor, host } = useMsfeEditor(parent, () => ({\n  markdown: '## 写作开始',\n  mode: 'live',\n  extensions: [StarterKit],\n}))\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cdiv ref=\"parent\" \u002F>\n  \u003CVueNodeViewHost :host=\"host\" \u002F>\n\u003C\u002Ftemplate>\n","vue","",[17,45,46,76,92,105,118,125,161,193,205,216,222,228,238,243,253,276,300],{"__ignoreMap":43},[47,48,51,55,59,63,66,69,73],"span",{"class":49,"line":50},"line",1,[47,52,54],{"class":53},"sVPnS","\u003C",[47,56,58],{"class":57},"su0ou","script",[47,60,62],{"class":61},"sgQzM"," setup",[47,64,65],{"class":61}," lang",[47,67,68],{"class":53},"=",[47,70,72],{"class":71},"s-HEn","\"ts\"",[47,74,75],{"class":53},">\n",[47,77,79,83,86,89],{"class":49,"line":78},2,[47,80,82],{"class":81},"sDD9d","import",[47,84,85],{"class":53}," { shallowRef } ",[47,87,88],{"class":81},"from",[47,90,91],{"class":71}," 'vue'\n",[47,93,95,97,100,102],{"class":49,"line":94},3,[47,96,82],{"class":81},[47,98,99],{"class":53}," { useMsfeEditor, VueNodeViewHost } ",[47,101,88],{"class":81},[47,103,104],{"class":71}," '@quirekit\u002Fmsfe-vue'\n",[47,106,108,110,113,115],{"class":49,"line":107},4,[47,109,82],{"class":81},[47,111,112],{"class":53}," { StarterKit } ",[47,114,88],{"class":81},[47,116,117],{"class":71}," '@quirekit\u002Fmsfe-starter-kit'\n",[47,119,121],{"class":49,"line":120},5,[47,122,124],{"emptyLinePlaceholder":123},true,"\n",[47,126,128,131,135,138,141,143,146,149,152,155,158],{"class":49,"line":127},6,[47,129,130],{"class":81},"const",[47,132,134],{"class":133},"sUNQB"," parent",[47,136,137],{"class":81}," =",[47,139,140],{"class":61}," shallowRef",[47,142,54],{"class":53},[47,144,145],{"class":61},"HTMLElement",[47,147,148],{"class":81}," |",[47,150,151],{"class":133}," null",[47,153,154],{"class":53},">(",[47,156,157],{"class":133},"null",[47,159,160],{"class":53},")\n",[47,162,164,166,169,171,174,176,179,181,184,187,190],{"class":49,"line":163},7,[47,165,130],{"class":81},[47,167,168],{"class":53}," { ",[47,170,23],{"class":133},[47,172,173],{"class":53},", ",[47,175,27],{"class":133},[47,177,178],{"class":53}," } ",[47,180,68],{"class":81},[47,182,183],{"class":61}," useMsfeEditor",[47,185,186],{"class":53},"(parent, () ",[47,188,189],{"class":81},"=>",[47,191,192],{"class":53}," ({\n",[47,194,196,199,202],{"class":49,"line":195},8,[47,197,198],{"class":53},"  markdown: ",[47,200,201],{"class":71},"'## 写作开始'",[47,203,204],{"class":53},",\n",[47,206,208,211,214],{"class":49,"line":207},9,[47,209,210],{"class":53},"  mode: ",[47,212,213],{"class":71},"'live'",[47,215,204],{"class":53},[47,217,219],{"class":49,"line":218},10,[47,220,221],{"class":53},"  extensions: [StarterKit],\n",[47,223,225],{"class":49,"line":224},11,[47,226,227],{"class":53},"}))\n",[47,229,231,234,236],{"class":49,"line":230},12,[47,232,233],{"class":53},"\u003C\u002F",[47,235,58],{"class":57},[47,237,75],{"class":53},[47,239,241],{"class":49,"line":240},13,[47,242,124],{"emptyLinePlaceholder":123},[47,244,246,248,251],{"class":49,"line":245},14,[47,247,54],{"class":53},[47,249,250],{"class":57},"template",[47,252,75],{"class":53},[47,254,256,259,262,265,267,270,274],{"class":49,"line":255},15,[47,257,258],{"class":53},"  \u003C",[47,260,261],{"class":57},"div",[47,263,264],{"class":61}," ref",[47,266,68],{"class":53},[47,268,269],{"class":71},"\"parent\"",[47,271,273],{"class":272},"shEpO"," \u002F",[47,275,75],{"class":53},[47,277,279,281,283,286,288,290,293,295,297],{"class":49,"line":278},16,[47,280,258],{"class":53},[47,282,31],{"class":57},[47,284,285],{"class":53}," :",[47,287,27],{"class":61},[47,289,68],{"class":53},[47,291,292],{"class":71},"\"",[47,294,27],{"class":53},[47,296,292],{"class":71},[47,298,299],{"class":53}," \u002F>\n",[47,301,303,305,307],{"class":49,"line":302},17,[47,304,233],{"class":53},[47,306,250],{"class":57},[47,308,75],{"class":53},[14,310,311],{},"hook 在挂载后创建实例，在卸载时清理编辑器与组件宿主。不要另外创建独立 Vue app 来渲染节点。",[14,313,314,317,318,321,322,325,326,329,330,333],{},[17,315,316],{},"options.onError"," 既接收运行时错误，也接收 ",[17,319,320],{},"createMsfeEditor()"," 创建失败的原始异常。\n创建失败时 ",[17,323,324],{},"editor.value"," 保持 ",[17,327,328],{},"undefined","，组件宿主会释放；宿主可展示错误提示和原文，\n修正配置后再明确重新挂载。未提供 ",[17,331,332],{},"onError"," 时，创建异常继续交给 Vue 的错误处理机制。\n配置 factory 自身抛出的异常仍由 Vue 处理。hook 不自动重试，也不在运行错误后重建文档。",[10,335,336],{"id":336},"更新内容和模式",[14,338,339,340,343,344,343,347,350,351,354,355,358],{},"初始化 options 不会被深度监听。挂载后的变化使用 ",[17,341,342],{},"setMode()","、",[17,345,346],{},"setReadOnly()",[17,348,349],{},"applyChanges()"," 或 ",[17,352,353],{},"replaceDocument()","。不要以全文 ",[17,356,357],{},"v-model"," 的方式在每次输入后回写编辑器。",[360,361],"msfe-demo",{"name":362},"basic\u002Feditor",[10,364,366],{"id":365},"将编辑器状态接入-vue","将编辑器状态接入 Vue",[14,368,369,370,373,374,377,378,381],{},"在 ",[17,371,372],{},"setup()"," 中使用 ",[17,375,376],{},"useMsfeState()","，无需自己编写 ",[17,379,380],{},"watch(editor)","、初始化快照或取消订阅：",[38,383,387],{"className":384,"code":385,"language":386,"meta":43,"style":43},"language-ts shiki shiki-themes github-light github-light github-dark","import { useMsfeState } from '@quirekit\u002Fmsfe-vue'\n\nconst state = useMsfeState(editor) \u002F\u002F 可直接传给 MdProse 的 state\nconst cursor = useMsfeState(editor, {\n  select: state => state.selection.main.head,\n})\n","ts",[17,388,389,400,404,423,437,455],{"__ignoreMap":43},[47,390,391,393,396,398],{"class":49,"line":50},[47,392,82],{"class":81},[47,394,395],{"class":53}," { useMsfeState } ",[47,397,88],{"class":81},[47,399,104],{"class":71},[47,401,402],{"class":49,"line":78},[47,403,124],{"emptyLinePlaceholder":123},[47,405,406,408,411,413,416,419],{"class":49,"line":94},[47,407,130],{"class":81},[47,409,410],{"class":133}," state",[47,412,137],{"class":81},[47,414,415],{"class":61}," useMsfeState",[47,417,418],{"class":53},"(editor) ",[47,420,422],{"class":421},"sqmzS","\u002F\u002F 可直接传给 MdProse 的 state\n",[47,424,425,427,430,432,434],{"class":49,"line":107},[47,426,130],{"class":81},[47,428,429],{"class":133}," cursor",[47,431,137],{"class":81},[47,433,415],{"class":61},[47,435,436],{"class":53},"(editor, {\n",[47,438,439,442,445,449,452],{"class":49,"line":120},[47,440,441],{"class":61},"  select",[47,443,444],{"class":53},": ",[47,446,448],{"class":447},"sde8i","state",[47,450,451],{"class":81}," =>",[47,453,454],{"class":53}," state.selection.main.head,\n",[47,456,457],{"class":49,"line":127},[47,458,459],{"class":53},"})\n",[14,461,462,464,465,468,469,471,472,475],{},[17,463,23],{}," 可为实例、ref 或 getter（例如 ",[17,466,467],{},"() => props.editor","）。返回只读的浅引用；创建前、\n创建失败、实例被清空或销毁时为 ",[17,470,328],{},"，模板可用 ",[17,473,474],{},"v-if=\"state\""," 等待可用状态。\nVue 作用域结束或切换实例会自动清理订阅；借用实例时不会替调用方销毁编辑器。",[14,477,478,479,482,483,486,487,490,491,494,495,498],{},"内部连接 MSFE 的 ",[17,480,481],{},"subscribeState()","，只观察已接受的 CodeMirror 状态。快照引用同步更新，\n不复制状态、重新解析或序列化全文，也不会向编辑器回写。未调用这个 hook 的组件不增加订阅。\n",[17,484,485],{},"select"," 和可选的 ",[17,488,489],{},"equal"," 沿用核心订阅合同：选择器必须纯且有界，每次已接受的更新求值一次，\n仅当结果变化时通知 Vue；默认用 ",[17,492,493],{},"Object.is"," 比较。返回新对象时可提供比较函数，例如\n",[17,496,497],{},"equal: (a, b) => a.from === b.from && a.to === b.to","。",[14,500,501,502,505,506,509],{},"selector 在创建 hook 时确定；它只依赖传入的不可变状态，不自动追踪其中读取的其他 Vue ref。\n需要与语言等响应式信息组合时，在返回值之外使用 ",[17,503,504],{},"computed()","。完整状态供阅读器使用，\n工具栏等小型控件优先选择需要的字段。纯选区变化是否能复用阅读内容仍由 ",[17,507,508],{},"MdProse"," 判断。",[10,511,513],{"id":512},"nuxt-与服务端渲染","Nuxt 与服务端渲染",[14,515,516,517,520],{},"编辑器依赖浏览器 DOM。在 Nuxt 中使用客户端组件或 ",[17,518,519],{},"ClientOnly"," 隔离编辑器挂载；文档说明和导航仍可服务端渲染。占位区域预留合理高度，避免页面在加载时跳动。",[10,522,523],{"id":523},"自定义组件",[14,525,526,527,530,531,534,535,498],{},"使用 ",[17,528,529],{},"VueNodeViewRenderer()"," 替换源码节点的 UI，或使用 ",[17,532,533],{},"VueComponentViewRenderer()"," 渲染搜索面板等通用控件。完整字段修改流程见",[536,537,539],"a",{"href":538},"\u002Fextensions\u002Fcustom-node","自定义节点",[10,541,543],{"id":542},"业务-provider-与-nodeview","业务 Provider 与 NodeView",[14,545,546,547,550],{},"应用可以通过 provide 注入语言、权限或业务服务，NodeView 和 ComponentView 通过同一 Host 继续读取。示例 ",[17,548,549],{},"shared\u002Flocale.ts"," 就演示了本地化注入。不要把服务实例或响应式 UI 对象写到 Markdown 字段中。",[14,552,553,554,557],{},"需要重新配置整个扩展集合时，先保存源码和需要保留的宿主状态，再明确重建实例。",[17,555,556],{},"options"," factory 是创建入口，不是深度响应式配置器；修改 props 并不等于扩展热插拔。",[10,559,561],{"id":560},"nuxt-项目检查","Nuxt 项目检查",[563,564,565,569,572,575],"ol",{},[566,567,568],"li",{},"客户端组件中创建 DOM 编辑器，服务端只渲染占位和说明。",[566,570,571],{},"避免同时打包多份 Vue、CodeMirror state\u002Fview；链接工作区源码时在打包器中 dedupe 对应包。",[566,573,574],{},"自定义组件的依赖必须存在于消费应用，不能依赖文档站私有 alias。",[566,576,577],{},"路由离开前处理未保存状态；卸载本身只能负责资源清理，不能代替可靠保存。",[14,579,580,581,584,585,498],{},"本文的可运行示例使用 QuireKit；更复杂的组件见 ",[536,582,583],{"href":538},"节点"," 和 ",[536,586,588],{"href":587},"\u002Fextensions\u002Fdiagrams","图表",[590,591,592],"style",{},"html pre.shiki code .sVPnS, html code.shiki .sVPnS{--shiki-default:#24292E;--shiki-light:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .su0ou, html code.shiki .su0ou{--shiki-default:#22863A;--shiki-light:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sgQzM, html code.shiki .sgQzM{--shiki-default:#6F42C1;--shiki-light:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s-HEn, html code.shiki .s-HEn{--shiki-default:#032F62;--shiki-light:#032F62;--shiki-dark:#9ECBFF}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 .shEpO, html code.shiki .shEpO{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-light:#B31D28;--shiki-light-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic}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);}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}",{"title":43,"searchDepth":78,"depth":78,"links":594},[595,596,597,598,599,600,601],{"id":12,"depth":78,"text":12},{"id":336,"depth":78,"text":336},{"id":365,"depth":78,"text":366},{"id":512,"depth":78,"text":513},{"id":523,"depth":78,"text":523},{"id":542,"depth":78,"text":543},{"id":560,"depth":78,"text":561},"在现有组件树内挂载编辑器，保留主题、依赖注入和视图生命周期。","md",[605,606,607,608,609],"Vue","Nuxt","Teleport","useMsfeEditor","useMsfeState","zh-CN",{},0,"\u002Fguide\u002Fvue","接入指南",{"title":5,"description":602},"guide\u002Fvue.zh-CN","uRgXt5ZhmxXUKl1a27Rx7kRsBVbQ9K2HfbTh8TmNwX0",1793046311417]