You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vaadin 24集成自定义Web组件出现Illegal Constructor错误求助

问题

我们将Vaadin Flow应用从Vaadin 14迁移至Vaadin 24后,大部分问题已解决,但第三方团队提供的自定义Web组件出现异常。该组件在对方Webpack 5项目中正常运行,且曾在我们的Vaadin 14项目中可用,为适配Vaadin 24已升级相关npm包版本。

组件配置

JS组件文件

  1. my-comp.js(升级无改动)
import { MyComp } from './src/MyComp'
import { MyCompPreview } from './src/MyCompPreview'

window.customElements.define('my-comp', MyComp)
window.customElements.define('my-comp-preview', MyCompPreview)
  1. MyComp类(原无构造函数,删除后问题依旧)
export class MyComp extends ScopedElementsMixin(LitElement) { 
  constructor () {
    super() //已调试确认执行
  }

  static get properties () {
    return {
      place: String,
      dir: String,
      data: Object,
      pars: Object
    }
  }

  async firstUpdated (changedProperties) {
    console.log("first updated")
    this.data = JSON.parse(this.data);
    console.log("place id -> ", this.place);
    console.log("dir -> ", this.dir);
    console.log("data -> ", this.pars);
    super.firstUpdated(changedProperties)
  }
}
  1. MyCompPreview类(升级无改动)
export class MyCompPreview extends MyComp {
  static get properties () {
    return {
      ...super.properties,
      factory: Function
    }
  }

  firstUpdated () {
    console.log("preview first updated")
    super.firstUpdated()
  }
}

集成组件的Java代码

@Tag("my-comp-preview")
@JsModule("./src/comps/my-comp.js")
public class MyComp extends Component implements HasSize, HasStyle {

    public MyComp(String place, String json) {
        setProps(place, json);
    }

    public void setProps(String place, String json) {
        setData(json);
        setPlace(place);
        setDir(place);
    }

    public void setPlace(String place) {
        this.getElement().setProperty("place", place);
    }

    public void setData(String jsonData) {
        this.getElement().setProperty("data", jsonData);
    }

    public void setDir(String dir) {
        this.getElement().setProperty("dir", dir);
    }

}

报错信息

实例化组件时触发错误:

generated-flow-imports-rV4Ldkr4.js:833 Uncaught TypeError: Illegal constructor
    at MyCompPreview.attachShadow (generated-flow-imports-rV4Ldkr4.js:833:25)
    ...(省略后续栈信息)

报错的attachShadow方法片段:

attachShadow(options) {
  const { scopedElements } = (
    /** @type {typeof ScopedElementsHost} */
    this.constructor
  );
  const shouldCreateRegistry = !this.registry || // @ts-ignore
  this.registry === this.constructor.__registry && !Object.prototype.hasOwnProperty.call(this.constructor, "__registry");
  if (shouldCreateRegistry) {
    this.registry = new CustomElementRegistry(); /// 此处报错,提示非法构造
    for (const [tagName, klass] of Object.entries(scopedElements ?? {})) {
      this.registry.define(tagName, klass);
    }
  }
  return super.attachShadow({
    ...options,
    // The polyfill currently expects the registry to be passed as `customElements`
    customElements: this.registry,
    // But the proposal has moved forward, and renamed it to `registry`
    // For backwards compatibility, we pass it as both
    registry: this.registry
  });
}

其他情况:该组件用于根据JSON数据及place/dir属性展示不同布局,我们其他Web组件集成方式相同但未出现此问题,已在Vaadin初始项目中复现该问题。查阅Vaadin及open-wc文档未找到相关内容,未直接调用new MyCompPreview,组件已注册为自定义元素,且曾在Vaadin14正常运行,与组件开发者沟通无果。

疑问:是否需要在Vaadin 24应用中引入Scoped Custom Element Registry polyfill?若需要,在Vaadin自动生成index.html的情况下该如何操作?


排查与解决方案

核心原因

报错中new CustomElementRegistry()提示非法构造,是因为原生浏览器目前不支持原生的Scoped Custom Element Registry,而组件依赖的ScopedElementsMixin(来自open-wc的scoped-elements方案)必须依赖对应的polyfill才能正常工作。Vaadin 14默认可能间接引入了该polyfill,而Vaadin 24的前端环境未包含,导致迁移后报错。

必须引入polyfill的验证

根据open-wc的官方文档,使用scoped-elements必须提前引入polyfill:

import '@webcomponents/scoped-custom-element-registry';

Vaadin 24中引入polyfill的方法

方法1:组件入口文件引入(推荐)

直接在第三方组件的入口文件my-comp.js最顶部添加polyfill导入:

import '@webcomponents/scoped-custom-element-registry';
import { MyComp } from './src/MyComp'
import { MyCompPreview } from './src/MyCompPreview'

window.customElements.define('my-comp', MyComp)
window.customElements.define('my-comp-preview', MyCompPreview)

这种方式仅在该组件加载时引入polyfill,不会影响全局其他组件,避免冲突。

方法2:全局引入(适用于多个组件依赖的情况)

如果多个自定义组件都需要该polyfill,可以通过Vaadin的自定义index.html模板实现全局引入:

  1. 在项目的src/main/resources/META-INF/resources/目录下创建index.html文件(若不存在)
  2. 在<head>标签中添加polyfill的导入:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Vaadin App</title>
    <!-- 引入scoped custom element registry polyfill -->
    <script type="module" src="@webcomponents/scoped-custom-element-registry"></script>
</head>
<body>
    <div id="outlet"></div>
</body>
</html>

Vaadin会自动识别并使用该自定义模板代替默认生成的index.html。

额外排查点

  • 检查@open-wc/scoped-elements和@webcomponents/scoped-custom-element-registry的版本兼容性,确保两者版本匹配
  • 确认组件类上是否正确配置了scopedElements静态属性(报错中scopedElements ?? {}说明当前可能为undefined,若组件内部依赖该属性注册子元素,需确保其正确初始化)
  • 检查Vaadin 24的前端打包配置,确认没有排除该polyfill的打包,或存在其他冲突的polyfill

内容的提问来源于stack exchange,提问作者Mari

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 02:36:06