Nue 交互式组件(Interactive Components)开发指南:用:is标记把静态 HTML 变成可交互组件
【免费下载链接】nueFastest way to build modern websites项目地址: https://gitcode.com/GitHub_Trending/nu/nue
Nue 的交互式组件(Interactive Components)是一类小巧、聚焦的动态单元——如表单、对话框、图片画廊——它们与你的内容(Content)和布局(Layout)并肩工作,为页面注入动态行为。本篇指南以 interactive-components.md 为骨架,结合 nuedom 编译器与运行时源码,系统讲解组件的创建、使用、自动发现机制与模板数据级联。读完你将掌握:如何用:is属性定义一个可复用的交互组件、如何把组件嵌入 Markdown 内容与布局模块、组件如何被自动发现,以及组件的模板数据从哪些来源级联而来。
交互式组件是什么
在 Nue 中,交互式组件并不是"另一个 JavaScript 框架里的组件"。它的核心理念是渐进增强:
- 组件首先以服务端渲染的 HTML存在(构建期由 Nue 生成完整 HTML);
- 当浏览器加载页面时,一个约2.5KB 的运行时(对应仓库中的 nuedom 客户端部分)让组件变得可交互;
- 组件与内容、布局自然共存,不需要引入重量级框架。
这种设计让页面在无 JavaScript 环境下依然保有完整内容,JavaScript 只是"锦上添花"的动态层。仓库根目录的 README.md 将其概括为 "Fastest way to build modern websites"——交互组件正是这一理念在页面动态行为上的落地。
创建组件::is属性与脚本方法
组件存放在.html文件中,使用 Nue 的模板语法编写。通过:is属性把一个元素标记为组件名,再通过:on*属性挂接事件处理器。下面是最典型的示例——一个订阅表单:
<form :is="newsletter-form" :onsubmit="submit" autocomplete="on"> <label> <span>Your name</span> <input type="text" name="name" placeholder="John Doe" required> </label> <label> <span>Your email</span> <input type="email" name="email" placeholder="your@email.com" required> </label> <button>Submit</button> <script> async submit(e) { const data = Object.fromEntries(new FormData(e.target)) await fetch('/api/subscribe', { method: 'POST', body: JSON.stringify(data) }) location.href = '/thanks' } </script> </form>关键点:
:is="newsletter-form"声明组件身份,让该表单可以在别处通过<newsletter-form>或[newsletter-form]引用;:onsubmit="submit"绑定submit方法。:onsubmit处理器会阻止表单的默认提交行为,转而执行你的自定义逻辑;<script>块定义组件的方法(这里是一个async的submit),方法体内的this即组件实例。
源码视角::is与事件处理器如何被解析
在 attributes.js 中,属性解析器parseAttributes对每个 token 做分支处理:
:is被解析为result.is = value(组件名);- 形如
:onxxx且xxx属于EVENTS列表(如click、submit)的属性,被解析为handlers数组项{ name: base, h_fn: addContext(value, imports) },即"事件名 + 编译后的处理函数"。
在运行时侧,node.js 的renderTag中为每个 handler 注册原生addEventListener,并且有一条关键逻辑:
if (name == 'submit') e.preventDefault()这正是文档所述":onsubmit处理器阻止默认表单提交"的实现位置。事件执行后还会调用update()触发一次重渲染,经由 diff.js 做 DOM 差异更新(无虚拟 DOM,直接映射到 DOM 操作,见 nuedom 的 README)。
使用组件:内容与布局双入口
组件可以被"投放"到内容或布局中的任何需要交互的位置,这是它与传统 JS 组件最大的不同——使用方不需要写任何 JavaScript。
在内容(Markdown)中使用
在 Markdown 文件中直接使用组件标签:
### Join our newsletter Get updates on new releases. [newsletter-form]Nue 会把[newsletter-form]替换为渲染后的 HTML。内容作者只需使用简单的标签,完全不用接触代码——这正是 Nue "HTML over JavaScript、文档结构驱动" 开发模型的体现。
在布局模块中使用
把组件嵌入布局模块,为静态结构补充交互:
<footer> <h3>Stay updated</h3> <p>Get our latest news delivered to your inbox</p> <newsletter-form/> </footer>组件"插入"到静态结构中,按需添加交互。这里用的是自闭合标签<newsletter-form/>形式。关于布局模块的插槽(slots)体系——如header、pagehead、aside等预定义位置——可参见 layout-system.md。
组件库:基于文件位置的自动发现
组件无需注册、无需导入,Nue 根据文件位置自动发现它们:
@shared/ui/ # 全局组件(作用于所有页面) blog/ui/ # 博客专属组件 app/components.html # 应用区域组件说明:文档原文使用
@system/ui/表示全局组件目录,当前仓库模板中对应的实际目录是@shared/ui/,例如 templates/full/@shared/ui/ 下就存放着全局可用的组件与布局模块。
你可以把多个组件放在同一个文件里,也可以各自独立成文件,Nue 都能找到它们:
<!-- In @shared/ui/forms.html --> <form :is="newsletter-form"> ... </form> <form :is="contact-form"> ... </form>文件类型声明与自动检测
组件所在.html文件的类型由文档类型声明决定(详见 html-file-types.md):
| 声明 | 含义 |
|---|---|
<!html lib> | 服务端 HTML 组件库,用于服务端渲染页面与布局模块 |
<!dhtml lib> | 客户端交互组件库,组件成为浏览器中可交互的元素 |
<!html+dhtml> | 同构组件库:服务端构建期渲染,客户端也能作为交互组件使用 |
若省略声明,Nue 会自动检测:出现事件处理器(:onclick、:onsubmit等)、JavaScriptimport或客户端脚本模式时判定为 DHTML;纯内容结构判定为 HTML;元素全部为自定义元素时自动判定为库。文档建议至少显式声明<!dhtml>以保证健壮性与清晰度。
仓库中的真实组件
- templates/full/@shared/ui/components.html:一个文件中同时定义
placeholder与logo两个组件,其中placeholder使用:is声明并使用--height="{ height }px"这类 CSS 自定义属性绑定; - templates/blog/posts/components.html:
<header :is="pagehead">结合<script>中定义的formatDate(date)方法,展示"布局插槽 + 组件脚本方法"的协作方式; - templates/spa/ui/entry.html 与 templates/full/@shared/ui/isomorphic.html:SPA 入口与同构组件的实际用法。
模板数据:多来源级联
组件通过{ }插值语法访问数据,数据来自多个来源并按优先级级联:
<form :is="newsletter-form"> <h3>{ newsletter_title }</h3> <p>{ site_name } newsletter</p> <button>{ cta || 'Submit' }</button> </form>上面这个组件可能用到:
newsletter_title—— 来自site.yaml;site_name—— 来自全局配置;cta—— 来自页面 front matter(并带有|| 'Submit'默认值兜底)。
通过属性传递页面级数据
在 Markdown 文件的 front matter 中定义,组件即可读取:
--- cta: "Subscribe now" --- [newsletter-form]数据级联的完整链条
结合 template-data.md,组件可见的数据上下文按从低到高的优先级叠加:
- 全局数据:
@shared/data/*.yaml与*.json(可附带.js/.ts数据处理脚本); - 根级数据:根目录的
site.yaml与其他 YAML 文件; - 应用级数据:子目录的
app.yaml与应用专属 YAML; - 页面 front matter:页面级覆盖,优先级最高(同名属性总是被覆盖)。
这一级联在 site.js 的mergeSharedData中有直接实现:先加载目录下所有 YAML/JSON 合并为一个 data 对象,再按字母序执行@shared/data/下的处理脚本,每个脚本接收当前合并数据并返回修改后的数据。
此外,组件还能访问内容集合(collections)与解析后的内容结构(如headings数组、title、description)。例如 template-data.md 中的目录组件[toc]就是在<script>里对this.headings过滤出h2级别标题。完整的来源与优先级说明见 template-data.md。
原理小结:从模板到交互的完整链路
从源码结构看,一个交互式组件从编写到在浏览器中生效,经历了如下链路:
- 解析:
parseNue(document.js)把.html模板解析为 AST;parseAttributes识别:is、:on*、:if、:each等指令(attributes.js); - 编译:
compileNue(compiler.js)把 AST 中的脚本与表达式编译为可执行的 JS 函数(事件函数形如(_,$e)=>...); - 服务端渲染:构建期由
renderNue(render.js)输出完整 HTML; - 客户端激活:浏览器加载 2.5KB 运行时(nuedom/src/index.js 的客户端侧),
mount时通过opts.deps查找组件定义(node.js 的findComponent),替换占位元素并绑定事件; - 更新:事件处理器执行后调用
update(),经domdiff做最小 DOM 差异更新,无需虚拟 DOM。
这套"服务端 HTML + 轻量客户端激活"的架构,正是 Nue 交互式组件在性能与可维护性上的立足点。要在大规模应用中组织组件与布局,可进一步阅读 project-structure.md 与 layout-system.md。
【免费下载链接】nueFastest way to build modern websites项目地址: https://gitcode.com/GitHub_Trending/nu/nue
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考