[{"data":1,"prerenderedAt":283},["ShallowReactive",2],{"docs-guides\u002Fbackend-nodejs-zh":3},{"id":4,"title":5,"body":6,"description":16,"extension":277,"meta":278,"navigation":84,"path":279,"seo":280,"stem":281,"__hash__":282},"docs\u002Fzh\u002Fguides\u002Fbackend-nodejs.md","Node.js 后端接入指南",{"type":7,"value":8,"toc":270},"minimark",[9,13,17,21,46,50,60,64,197,200,249,252,266],[10,11,5],"h1",{"id":12},"nodejs-后端接入指南",[14,15,16],"p",{},"A@UI 的后端接入原则只有一条：消费协议，不消费 SDK。",[18,19,20],"h2",{"id":20},"约束",[22,23,24,28,36],"ul",{},[25,26,27],"li",{},"不安装任何 A@UI 后端 SDK",[25,29,30,31,35],{},"只输出符合 ",[32,33,34],"code",{},"assets\u002Fcommands.schema.json"," 的 JSON 命令",[25,37,38,39,42,43],{},"每条命令写成一行 ",[32,40,41],{},"data: \u003Cjson>","，结束时输出 ",[32,44,45],{},"data: [DONE]",[18,47,49],{"id":48},"最小-sse-示例","最小 SSE 示例",[51,52,58],"pre",{"className":53,"code":55,"language":56,"meta":57},[54],"language-text","data: {\"type\":\"render\",\"component\":\"PersonalProfileCard\",\"params\":{\"name\":\"陈叙\",\"title\":\"内容策略师\"}}\n\ndata: [DONE]\n","text","",[32,59,55],{"__ignoreMap":57},[18,61,63],{"id":62},"示例原生-http-模块","示例：原生 http 模块",[51,65,69],{"className":66,"code":67,"language":68,"meta":57,"style":57},"language-js shiki shiki-themes one-dark-pro","import http from 'node:http'\n\nhttp.createServer((req, res) => {\n  res.writeHead(200, {\n    'Content-Type': 'text\u002Fevent-stream',\n    'Cache-Control': 'no-cache',\n    'Connection': 'keep-alive',\n  })\n\n  const commands = [\n    { type: 'render', component: 'PersonalProfileCard', params: { name: '陈叙', title: '内容策略师' } },\n    { type: 'render', component: 'ArticleList', params: { items: [] } },\n  ]\n\n  for (const cmd of commands) {\n    res.write(`data: ${JSON.stringify(cmd)}\\n\\n`)\n  }\n\n  res.write('data: [DONE]\\n\\n')\n  res.end()\n}).listen(3000)\n","js",[32,70,71,79,86,92,98,104,110,116,122,127,133,139,145,151,156,162,168,174,179,185,191],{"__ignoreMap":57},[72,73,76],"span",{"class":74,"line":75},"line",1,[72,77,78],{},"import http from 'node:http'\n",[72,80,82],{"class":74,"line":81},2,[72,83,85],{"emptyLinePlaceholder":84},true,"\n",[72,87,89],{"class":74,"line":88},3,[72,90,91],{},"http.createServer((req, res) => {\n",[72,93,95],{"class":74,"line":94},4,[72,96,97],{},"  res.writeHead(200, {\n",[72,99,101],{"class":74,"line":100},5,[72,102,103],{},"    'Content-Type': 'text\u002Fevent-stream',\n",[72,105,107],{"class":74,"line":106},6,[72,108,109],{},"    'Cache-Control': 'no-cache',\n",[72,111,113],{"class":74,"line":112},7,[72,114,115],{},"    'Connection': 'keep-alive',\n",[72,117,119],{"class":74,"line":118},8,[72,120,121],{},"  })\n",[72,123,125],{"class":74,"line":124},9,[72,126,85],{"emptyLinePlaceholder":84},[72,128,130],{"class":74,"line":129},10,[72,131,132],{},"  const commands = [\n",[72,134,136],{"class":74,"line":135},11,[72,137,138],{},"    { type: 'render', component: 'PersonalProfileCard', params: { name: '陈叙', title: '内容策略师' } },\n",[72,140,142],{"class":74,"line":141},12,[72,143,144],{},"    { type: 'render', component: 'ArticleList', params: { items: [] } },\n",[72,146,148],{"class":74,"line":147},13,[72,149,150],{},"  ]\n",[72,152,154],{"class":74,"line":153},14,[72,155,85],{"emptyLinePlaceholder":84},[72,157,159],{"class":74,"line":158},15,[72,160,161],{},"  for (const cmd of commands) {\n",[72,163,165],{"class":74,"line":164},16,[72,166,167],{},"    res.write(`data: ${JSON.stringify(cmd)}\\n\\n`)\n",[72,169,171],{"class":74,"line":170},17,[72,172,173],{},"  }\n",[72,175,177],{"class":74,"line":176},18,[72,178,85],{"emptyLinePlaceholder":84},[72,180,182],{"class":74,"line":181},19,[72,183,184],{},"  res.write('data: [DONE]\\n\\n')\n",[72,186,188],{"class":74,"line":187},20,[72,189,190],{},"  res.end()\n",[72,192,194],{"class":74,"line":193},21,[72,195,196],{},"}).listen(3000)\n",[18,198,199],{"id":199},"实现步骤",[201,202,203,217,224,231,242],"ol",{},[25,204,205,206,209,210,209,213,216],{},"根据业务意图决定要输出哪些 ",[32,207,208],{},"render","、",[32,211,212],{},"update",[32,214,215],{},"destroy"," 命令。",[25,218,219,220,223],{},"确保 ",[32,221,222],{},"component"," 名称与前端 manifest\u002Fregistry 一致。",[25,225,226,227,230],{},"让 ",[32,228,229],{},"params"," 只包含纯 JSON 数据。",[25,232,233,234,237,238,241],{},"用 ",[32,235,236],{},"data:"," 包裹每条命令，以 ",[32,239,240],{},"\\n\\n"," 结尾。",[25,243,244,245,248],{},"输出 ",[32,246,247],{},"data: [DONE]\\n\\n"," 结束流。",[18,250,251],{"id":251},"常见错误",[22,253,254,257,260,263],{},[25,255,256],{},"试图在后端创建或依赖 widgetId",[25,258,259],{},"在命令里塞入函数、Date、Map 或不可序列化对象",[25,261,262],{},"使用未注册的组件名",[25,264,265],{},"输出 markdown 或日志文字污染 SSE 数据流",[267,268,269],"style",{},"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);}",{"title":57,"searchDepth":81,"depth":81,"links":271},[272,273,274,275,276],{"id":20,"depth":81,"text":20},{"id":48,"depth":81,"text":49},{"id":62,"depth":81,"text":63},{"id":199,"depth":81,"text":199},{"id":251,"depth":81,"text":251},"md",{},"\u002Fzh\u002Fguides\u002Fbackend-nodejs",{"title":5,"description":16},"zh\u002Fguides\u002Fbackend-nodejs","Tmsy--NoRy-WfdVF8YpbFX4lEhnvs7t-SxuwylINbkA",1779263316048]