Svelte 展开属性后自定义 button 变空:字符串没有进入 setter
在带 is 属性的定制内置元素上,字符串经 spread 可能写成 HTML 属性,绕过 setter。对必要属性显式绑定,并保留原生控件语义。
同一个定制按钮,写 label="hello" 能显示文字,改成 {...{ label: 'hello' }} 后却空白;开发者工具里多出一个 label 属性。若元素使用 is="fancy-button",检查字符串是否没有调用自定义 setter。
这条路径在 Svelte 5.57.2 的 Chromium 环境中存在,挂载、水合和后续更新都需要留意。它针对定制内置元素,例如扩展原生 HTMLButtonElement 的按钮,不能套到所有带连字符名称的自主自定义元素,更不能用来解释浏览器不支持该元素机制。
attribute 写入不等于 property setter 调用
核心差别可以由以下结构看出:
<button is="fancy-button" label="hello"></button>
<button is="fancy-button" {...{ label: 'hello' }}></button>
这里假设客户端已经按原流程注册 fancy-button,它的 label setter 负责写入 textContent。第一行调用该 setter;第二行可能只留下 label="hello",不会自动执行元素的自定义赋值逻辑。
Svelte 的 spread 更新会按元素分类、值类型和可用 setter 选择路径。5.57.2 的定制内置元素在相关分类中没有得到与自主自定义元素相同的处理,因此字符串和 null 会走属性写入/移除路径,数字或对象又可能表现正常。只用一个数字值回归,容易漏掉原故障。
对确实需要 setter 的属性显式绑定
可以在自己控制的模板中,把这些已知属性从 spread 里取出,改为显式绑定:
<button is="fancy-button" label={label}></button>
沿用组件原来的 label 状态和自定义元素注册流程。若还有其他 spread 属性,整理它们的归属和覆盖顺序,确保同一个 label 不再由两条路径争抢。对内部封装组件,也要检查最终传给原生按钮的模板,而不只是上层调用。
不要只把字符串包装成对象来诱导另一条分支。setter 的输入契约已经改变,可能把错误转移到元素内部。也不要直接改共享元素分类器:原生 option 的 value、选择绑定和对象身份有特殊处理,粗略把所有 is 元素当成自主自定义元素可能破坏这些语义。
覆盖初始化、更新和清除
验收应查看 setter 对应的真实结果,不能只看 DOM attribute 存在。按原挂载方式检查初值,再改变字符串、清除值,确认 setter 收到预期参数、按钮显示与状态一致。采用 SSR 时还需核对水合前后,别只检查客户端新建节点。
如果元素扩展了 input、option 等原生表单控件,另验原生 value、选择状态和提交值。后续正式框架修正应绑定到实际安装版本,再评估是否恢复 spread;目前仍需按具体控件检查,不能把一条局部分类改动视为所有控件已经安全。


