——通过 custom-elements.json 实现 yp-* 组件的跨框架自动补全
背景
在构建 Web Component 组件库(如 yp-button、yp-input 等)时,组件具备天然的跨框架能力,但**开发体验(DX)**往往成为落地的最大障碍:
- HTML 中没有组件与属性补全
- Vue / React 模板里无法识别自定义标签
- Markdown 中示例代码没有任何智能提示
- IDE 插件维护成本高,难以定制
而 Monaco Editor(VS Code 内核)为 Web 编辑器场景提供了一个理想的解决方案:
通过插件机制,在运行时注入组件元信息,实现“类 IDE”级别的智能提示。
本文将完整拆解一个实战方案:
基于
custom-elements.json,在 Monaco Editor 中实现 yp- Web Component 的:*
- 标签自动补全
- 属性 / 类型 / 描述提示
- 事件提示
- 支持 HTML / Vue / React / Markdown
一、整体方案设计
目标能力
| 能力 | 说明 |
|---|---|
| 固定前缀识别 | 仅对 yp- 开头组件生效 |
| 标签补全 | <yp-button> |
| 属性补全 | type、size、disabled |
| 属性说明 | description / default / type |
| 事件提示 | @click / onClick |
| 多语言支持 | HTML / Vue / React / Markdown |
| 无侵入 | 不依赖 VS Code 插件 |
核心技术选型
| 技术 | 作用 |
|---|---|
| monaco-editor | 编辑器内核 |
| custom-elements.json | Web Component 元数据标准 |
| CompletionItemProvider | Monaco 插件扩展点 |
| HTML / TS Language Service | 多语言能力复用 |
二、custom-elements.json 作为唯一数据源
1️⃣ custom-elements.json 是什么
这是由 Web Components 社区推动的组件元信息标准,典型结构如下:
{
"schemaVersion": "1.0.0",
"tags": [
{
"name": "yp-button",
"description": "按钮组件",
"attributes": [
{
"name": "type",
"type": "primary | success | warning",
"description": "按钮类型",
"default": "default"
}
],
"events": [
{
"name": "click",
"description": "点击事件"
}
]
}
]
}
2️⃣ 为什么选择它
- 与 Lit / Stencil / FAST / Vue Custom Element 生态兼容
- 可自动生成
- 一次定义,多处消费
- 非 Monaco / VS Code 强绑定
三、解析 custom-elements.json
1️⃣ 数据结构归一化
interface ComponentMeta {
tag: string
description?: string
attributes: AttributeMeta[]
events: EventMeta[]
}
function parseCustomElements(json: any): ComponentMeta[] {
return json.tags
.filter((tag: any) => tag.name.startsWith('yp-'))
.map((tag: any) => ({
tag: tag.name,
description: tag.description,
attributes: tag.attributes ?? [],
events: tag.events ?? []
}))
}
四、Monaco 插件机制实现
1️⃣ 注册 HTML 补全提供器
monaco.languages.registerCompletionItemProvider('html', {
triggerCharacters: ['<', ' ', ':', '@'],
provideCompletionItems(model, position) {
return getCompletions(model, position)
}
})
2️⃣ 标签自动补全(yp-*)
function getTagCompletions(components: ComponentMeta[]) {
return components.map(comp => ({
label: comp.tag,
kind: monaco.languages.CompletionItemKind.Class,
insertText: comp.tag,
documentation: comp.description
}))
}
效果:
<yp-│(提示所有组件)
3️⃣ 属性补全(HTML / Vue)
function getAttributeCompletions(component: ComponentMeta) {
return component.attributes.map(attr => ({
label: attr.name,
kind: monaco.languages.CompletionItemKind.Property,
insertText: `${attr.name}="$1"`,
documentation: `
${attr.description}
类型:${attr.type}
默认值:${attr.default ?? '-'}
`
}))
}
4️⃣ Vue 指令 / 事件补全
function getVueEventCompletions(component: ComponentMeta) {
return component.events.map(evt => ({
label: `@${evt.name}`,
kind: monaco.languages.CompletionItemKind.Event,
insertText: `@${evt.name}="$1"`,
documentation: evt.description
}))
}
5️⃣ React JSX 事件支持
function toReactEvent(name: string) {
return 'on' + name.charAt(0).toUpperCase() + name.slice(1)
}
label: toReactEvent(evt.name)
insertText: `${toReactEvent(evt.name)}={$1}`
五、多语言支持策略
HTML
- 原生支持
- 直接注册
htmlprovider
Vue(template)
monaco.languages.registerCompletionItemProvider('vue', {
provideCompletionItems: htmlProvider
})
复用 HTML 规则即可。
React(JSX / TSX)
monaco.languages.registerCompletionItemProvider('typescript', {
provideCompletionItems: jsxProvider
})
识别 <yp- 上下文即可。
Markdown
monaco.languages.registerCompletionItemProvider('markdown', {
provideCompletionItems(model, position) {
// 仅在 ```html / ```vue 区块内生效
}
})
六、插件化设计
1️⃣ 插件接口定义
interface MonacoWebComponentPlugin {
name: string
setup(monaco: typeof import('monaco-editor')): void
}
2️⃣ 插件注册
const ypWebComponentPlugin: MonacoWebComponentPlugin = {
name: 'yp-webcomponent',
setup(monaco) {
registerHtmlProvider(monaco)
registerVueProvider(monaco)
registerJsxProvider(monaco)
registerMarkdownProvider(monaco)
}
}
七、最终效果
✅ 输入 <yp- 自动提示组件
✅ 输入属性立即补全
✅ hover 查看属性说明
✅ Vue / React 语法差异自动适配
✅ Markdown 示例代码也有智能提示
八、最佳实践总结
关键经验
- custom-elements.json 是 DX 的核心资产
- Monaco 插件应尽量无状态、可组合
- 固定前缀能显著降低补全干扰
- Vue / React 差异应在补全层解决
- Markdown 支持极大提升文档体验
九、适用场景
- Web Component 组件库官网
- 在线 Playground / Demo
- 低代码 / 页面搭建器
- 跨框架组件文档系统
结语
通过 Monaco Editor + custom-elements.json + 插件机制,
可以在 浏览器中复刻 IDE 级别的 Web Component 开发体验,并且:
- 不依赖 VS Code
- 不绑定单一框架
- 可持续演进
这是一条非常适合 Web Component 组件库、Design System 的工程化路径。
如果你正在做组件库、低代码或在线编辑器,这套方案值得长期投入。
文章评论