Web 组件(view.component)
view.component 条目描述一个可复用的自定义元素(web 组件),Web Host 能够自动发现、注入并注册它。与页面不同,组件没有自己的 iframe——它是一个自定义 HTML 标签,可以出现在页面或宿主模板放置它的任何位置。
关于如何编写组件实现,参见 Web 组件。
前端字段(package.json 的 wippy 块)
这些字段由前端开发者在 package.json 的 wippy 块中编写。vite 插件在构建时把它们烘焙进 wippy-meta.json,wippy/views 从那里读取它们作为默认值。
本节所有字段都可以由运维人员在
_index.yaml中覆盖。YAML 始终优先。
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
type |
string | — | 必须是 "component" 或 "widget";"widget" 是模板惯例 |
tagName |
string | — | 自定义元素名称;按 HTML 规范必须包含连字符 |
props |
object | — | 描述组件所接受属性的 JSON Schema |
events |
object | — | 描述组件所发出的自定义 DOM 事件的 JSON Schema |
package.json 中的 wippy.type
Web 组件包在其 wippy 块内设置 "type": "widget" 或 "type": "component"(而不是 "page")。app-template 目前使用 "widget",vite 插件在这一运行时契约中接受两个组件名称。
{
"specification": "wippy-component-1.0",
"wippy": {
"tagName": "example-reaction-bar",
"type": "widget",
"props": { ... },
"events": { ... }
}
}
部署时,运维人员 YAML 中的 meta.tag_name 具有权威性并覆盖打包值;wippy.tagName(由 package.json 烘焙进 wippy-meta.json)只是 YAML 条目省略 tag_name 时 wippy/views 所使用的回退值(解析顺序:YAML meta.tag_name → 打包的 wippy.tagName)。请让两者保持一致以避免意外,但若两者不同,YAML 胜出。
Props Schema
package.json 中的 wippy.props key 是一个 JSON Schema 对象,描述组件所接受的属性。vite 插件把它包含进 wippy-meta.json,Web Host 在向消费方(例如聊天产物渲染器和标签清洗器,后者需要知道哪些属性是合法的,以免把它们剥离)暴露组件元数据时使用它。
{
"wippy": {
"props": {
"type": "object",
"properties": {
"reactions": {
"type": "array",
"items": { "type": "string" },
"default": ["👍", "👎", "❤️", "🎉", "🤔"],
"description": "Array of emoji reactions to display"
},
"allow-multiple": {
"type": "boolean",
"default": false,
"description": "Whether multiple reactions can be active simultaneously"
}
}
}
}
}
properties 中的属性名使用 HTML 属性惯例(短横线命名)。当某个属性缺失时,schema 中的 default 值也会由 web 组件的 prop 解析器在运行时应用。
Events Schema
wippy.events key 与 props 的形状一致,但描述的是组件通过 useEvents() 发出的自定义 DOM 事件。每个 key 是一个事件名;值是该事件 detail 载荷的 JSON Schema。
{
"wippy": {
"events": {
"type": "object",
"properties": {
"reaction": {
"type": "object",
"properties": {
"emoji": { "type": "string" },
"count": { "type": "number" },
"active": { "type": "boolean" }
},
"description": "Fired when a reaction is toggled"
}
}
}
}
}
Web Host 的聊天消息清洗器会把 wippy-meta.json 中 props.properties 里的组件属性加入允许列表。事件 schema 为工具和消费方记录所发出的自定义事件;它们不用于让 DOM 事件监听器属性通过被清洗的聊天内容。
运维配置(_index.yaml)
这些字段由运维人员在 _index.yaml 注册表条目的 meta 块中设置。其中大多数代表纯粹的部署策略——路由、访问控制和服务方式——只有在部署时才有意义,没有 package.json 的编写面(announced、secure、url、auto_register)。有两个字段不同:tag_name 和 entry_point 是前端编写的,位于 package.json 中(烘焙进 wippy-meta.json),对应的 YAML key 只是这些打包值的可选的按部署覆盖。
announced、secure、url和auto_register是纯粹的部署策略,不能在 package.json 中设置——它们由运维人员为每个环境设置。tag_name和entry_point是前端编写的默认值,运维人员可以在 YAML 中覆盖。
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
tag_name |
string | wippy.tagName |
由前端在 package.json 中以 wippy.tagName 编写(vite 插件要求);该 YAML key 覆盖打包值。自定义元素名称;按 HTML 规范必须包含连字符 |
announced |
boolean | false |
必须为 true,组件才会出现在 /api/public/components/list 中。若设置了 meta.public,则回退到它。 |
auto_register |
boolean | false |
true → Web Host 在启动时自动加载并注册该组件 |
secure |
boolean | false |
需要身份验证 |
url |
string | — | 组件构建产物 bundle 的静态挂载路径 |
base_path |
string | "" |
可选的子路径,追加到 url 之后构成项目根;解析后的 bundle URL 组合为 <url>/<base_path>/<entry_point>。与页面的处理方式完全相同,尽管当前 app-template 的组件条目省略了它 |
entry_point |
string | wippy.browser → index.js |
由前端在 package.json 中以顶层 browser 字段编写(烘焙进 wippy-meta.json);该 YAML key 覆盖打包值,最终回退到 index.js。入口模块文件;宿主以 <script type="module"> 形式注入它 |
一个最小条目如下所示:
- name: reaction-bar
kind: registry.entry
meta:
type: view.component
name: reaction-bar
tag_name: example-reaction-bar
announced: true
secure: false
auto_register: true
url: /app/wc/reaction-bar
entry_point: index.js
自动加载的三道关卡
要让 Web Host 自动加载组件,以下三个条件必须同时成立:
-
announced: true——wippy/views在list_components.lua中于服务端按此标志过滤。没有任何查询参数可以绕过它。announced: false的组件无论其他设置如何,都绝不会出现在/api/public/components/list中。 -
auto_register: true—— 宿主的loadGlobalAutoloadWidgets函数以?auto_register=true查询列表端点。没有该标志的组件会被排除在这份过滤后的响应之外。 -
该标签尚未注册 —— 注入脚本之前,宿主会检查
customElements.get(tagName)。如果标签已被定义(例如来自上一次导航),宿主会跳过注入以避免重复定义。
只要缺少任何一道关卡,组件就会静默缺席。验证方法:curl /api/public/components/list?auto_register=true —— 你的标签必须出现在响应中。
自动加载序列
Web Host 内的页面挂载完成后,宿主执行以下序列:
-
GET /api/public/components/list?auto_register=true—— 获取所有已公告、需自动注册的组件。 -
对每个
customElements.get(tagName)为undefined的组件,宿主向document.head追加:<script type="module" src="/app/wc/reaction-bar/index.js?declare-tag=example-reaction-bar"></script>?declare-tag=查询参数是告知入口 chunk 应以哪个自定义元素名称注册的通道。 -
入口 chunk 调用
define(import.meta.url, ElementClass)。组件作者从@wippy-fe/webcomponent-vue(或@wippy-fe/webcomponent-core)导入define,二者都重新导出 proxy 的define;运行时 import map 会把它解析到唯一的@wippy-fe/proxy实例。define辅助函数读取new URL(import.meta.url).searchParams.get('declare-tag')并调用customElements.define(tagName, ElementClass)。 -
Vue(或任何框架)渲染一个
<example-reaction-bar>元素。浏览器升级该元素,connectedCallback触发,WippyVueElement在 shadow root 内挂载它的 Vue 应用。
为什么 auto_register: false 有用
设置 auto_register: false 会把组件排除在全局自动加载扫描之外。以下情形适用:
- 组件体积很大,应当只在确实需要它的页面上加载。
- 组件在调用处通过
loadByTagName('example-heavy-chart')(从@wippy-fe/proxy导入)以编程方式注册。 - 组件是仅在另一个 bundle 内部使用的构建块,而不是独立的自定义元素。
import { loadByTagName } from '@wippy-fe/proxy'
await loadByTagName('example-heavy-chart')
延迟注册让首屏加载保持轻量。组件仍然需要 announced: true,loadByTagName() 才能通过 API 解析它——当该标志为 false 时,GET /components/by-tag/{tag} 端点返回 404 "Component is not announced"。