自定义标签不会被浏览器自动解析,必须通过Custom Elements API注册(如customElements.define)才能触发生命周期并正常工作;未注册时仅作未知元素处理,需手动遍历初始化。

自定义标签在HTML中不会被浏览器自动解析
浏览器只识别标准HTML标签(如 <div>、<p>),所有自定义标签(比如 <sql-query> 或 <math-expr>)默认被当作未知元素处理:不渲染、不执行、不报错,但也不会触发任何逻辑。这意味着光写 <my-chart data='[1,2,3]'></my-chart> 是没用的——它只是个空壳。
真正起作用的是你主动监听并处理这些标签。常见做法是用 document.querySelectorAll('my-chart') 找到它们,再读取 getAttribute('data'),最后调用对应解析器或渲染函数。
- 必须手动遍历并初始化,不能依赖浏览器生命周期(如
connectedCallback除非你注册了 Custom Element) - 如果页面动态插入新标签(如 AJAX 加载后追加),需重新扫描或使用
MutationObserver - IE 不支持
customElements.define(),若需兼容,优先用属性选择器 + 手动解析,而非基于 Web Components 的方案
用 Custom Elements API 实现可复用的 DSL 标签
如果你希望标签具备“即插即用”能力(比如自动渲染、响应属性变化),得注册自定义元素。例如定义 <json-viewer> 并让它监听 json 属性变化:
class JsonViewer extends HTMLElement {
static get observedAttributes() { return ['json']; }
attributeChangedCallback(name, oldVal, newVal) {
if (name === 'json' && newVal) {
this.innerHTML = `<pre class="brush:php;toolbar:false;">${JSON.stringify(JSON.parse(newVal), null, 2)}`;
}
}
}
customElements.define('json-viewer', JsonViewer);
这样写 <json-viewer json='{"a":1}'></json-viewer> 就能自动生效。但注意:
立即学习“前端免费学习笔记(深入)”;
- 属性值只能是字符串,复杂结构(如嵌套对象)必须 JSON 序列化后传入
- 属性名不支持连字符以外的符号(
data-src可以,data_src不会触发attributeChangedCallback) - 首次渲染发生在
connectedCallback,不是构造函数里,避免此时 DOM 还未挂载
解析 DSL 内容时容易忽略 HTML 转义与上下文安全
很多 DSL 标签会把内容当代码执行(如 <template-lang>{{x + 1}}</template-lang>),直接 innerHTML = result 极易引发 XSS。更糟的是,有些 DSL 输出 HTML 片段(如 Markdown 渲染结果),但用户输入可能含恶意 <script>。
- 永远不要用
eval()或Function()解析用户可控的 DSL 表达式 - 若 DSL 输出 HTML,用
DOMPurify.sanitize()过滤后再设innerHTML - 纯文本 DSL(如配置片段)建议用
textContent渲染,避免意外解析标签 - 对内联脚本类 DSL(如
<py-script>print("hello")</py-script>),应限制运行环境(如 Web Worker + pyodide 沙箱),而非直接eval
性能瓶颈常出现在重复解析和无节制重渲染
一个页面若有 50 个 <sql-query>,每个都调用一次 SQL 解析器,又没做缓存或去重,加载就卡顿。DSL 解析本身往往比 DOM 操作更耗 CPU。
- 对相同属性值的标签做记忆化(
Map缓存解析结果),尤其适用于静态 DSL 内容 - 避免在
attributeChangedCallback中频繁触发完整重渲染;区分哪些变更需要重绘(如theme改变只需换 class,不必重跑整个 DSL) - 长列表场景下,考虑用
IntersectionObserver延迟解析可视区域外的 DSL 标签 - 服务端已预渲染 DSL 结果?那就别在客户端再解析一遍——加个
data-rendered="true"属性跳过初始化
DSL 标签真正的复杂点不在语法设计,而在如何平衡表达力、安全性与执行开销。很多人卡在“能跑通”,却没意识到第 100 个 <chart-dsl> 正悄悄拖慢主线程。



















