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

引入JS时钟组件后Vaadin应用自定义CSS样式丢失问题

问题:引入LitElement时钟组件后Vaadin应用自定义CSS全部丢失

我在Vaadin应用中通过Spring的@JsModule注解引入了一个JavaScript时钟组件(代码如下),引入后发现原有自定义CSS样式全部被移除,我对这类JS组件使用经验有限。相关场景说明:

  • 组件类:通过@JsModule注解指定了时钟组件的JS文件路径
  • 引入时钟的视图:在Vaadin视图中添加了<analog-clock>标签
  • 样式丢失效果:原有页面的自定义样式完全消失,仅显示时钟组件

时钟组件代码:

import {css, html, LitElement} from 'lit';

class AnalogClock extends LitElement {

    static get properties() {
        return {
            time: {type: Date},
            interval: {type: Object}
        };
    }

    static get styles() {
        return css`
       
    .clock {
  width: 30rem;
  height: 30rem;
  border: 7px solid #282828;
  box-shadow: -4px -4px 10px rgba(67,67,67,0.5),
                inset 4px 4px 10px rgba(0,0,0,0.5),
                inset -4px -4px 10px rgba(67,67,67,0.5),
                4px 4px 10px rgba(0,0,0,0.3);
  border-radius: 50%;
  margin: 50px auto;
  position: relative;
  padding: 2rem;
 
}

.outer-clock-face {
  position: relative;
  width: 100%;
  height: 100%;
  border-radius: 100%;
  background: #282828;
  
 
  overflow: hidden;
}

.outer-clock-face::after {
  -webkit-transform: rotate(90deg);
  -moz-transform: rotate(90deg);
  transform: rotate(90deg)
}

.outer-clock-face::before,
.outer-clock-face::after,
.outer-clock-face .marking{
  content: '';
  position: absolute;
  width: 5px;
  height: 100%;
  background: #1df52f;
  z-index: 0;
  left: 49%;
}

.outer-clock-face .marking {
  background: #bdbdcb;
  width: 3px;
}

.outer-clock-face .marking.marking-one {
  -webkit-transform: rotate(30deg);
  -moz-transform: rotate(30deg);
  transform: rotate(30deg)
}

.outer-clock-face .marking.marking-two {
  -webkit-transform: rotate(60deg);
  -moz-transform: rotate(60deg);
  transform: rotate(60deg)
}

.outer-clock-face .marking.marking-three {
  -webkit-transform: rotate(120deg);
  -moz-transform: rotate(120deg);
  transform: rotate(120deg)
}

.outer-clock-face .marking.marking-four {
  -webkit-transform: rotate(150deg);
  -moz-transform: rotate(150deg);
  transform: rotate(150deg)
}

.inner-clock-face {
  position: absolute;
  top: 10%;
  left: 10%;
  width: 80%;
  height: 80%;
  background: #282828;
  -webkit-border-radius: 100%;
  -moz-border-radius: 100%;
  border-radius: 100%;
  z-index: 1;
}

.inner-clock-face::before {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 16px;
  height: 16px;
  border-radius: 18px;
  margin-left: -9px;
  margin-top: -6px;
  background: #4d4b63;
  z-index: 11;
}

.hand {
  width: 50%;
  right: 50%;
  height: 6px;
  background: #61afff;
  position: absolute;
  top: 50%;
  border-radius: 6px;
  transform-origin: 100%;
  transform: rotate(90deg);
  transition-timing-function: cubic-bezier(0.1, 2.7, 0.58, 1);
}

.hand.hour-hand {
  width: 30%;
  z-index: 3;
}

.hand.min-hand {
  height: 3px;
  z-index: 10;
  width: 40%;
}

.hand.second-hand {
  background: #ee791a;
  width: 45%;
  height: 2px;
}

        `;
    }

    render() {
        return html`
            <div class="clock">
                <div class="outer-clock-face">
                    <div class="marking marking-one"></div>
                    <div class="marking marking-two"></div>
                    <div class="marking marking-three"></div>
                    <div class="marking marking-four"></div>
                    <div class="inner-clock-face">
                        <div class="hand hour-hand"></div>
                        <div class="hand min-hand"></div>
                        <div class="hand second-hand"></div>
                    </div>
                </div>
            </div>
        `;
    }

    setDate(time) {

        const secondHand = this.shadowRoot.querySelector('.second-hand');
        const minsHand = this.shadowRoot.querySelector('.min-hand');
        const hourHand = this.shadowRoot.querySelector('.hour-hand');

        const now = time;

        const seconds = now.getSeconds();
        const secondsDegrees = ((seconds / 60) * 360) + 90;
        secondHand.style.transform = `rotate(${secondsDegrees}deg)`;

        const mins = now.getMinutes();
        const minsDegrees = ((mins / 60) * 360) + ((seconds / 60) * 6) + 90;
        minsHand.style.transform = `rotate(${minsDegrees}deg)`;

        const hour = now.getHours();
        const hourDegrees = ((hour / 12) * 360) + ((mins / 60) * 30) + 90;
        hourHand.style.transform = `rotate(${hourDegrees}deg)`;
    }

    constructor() {
        super();
        this.time = new Date();

        this.updateComplete.then(() => {
                this.setDate(this.time);

                this.interval = setInterval(() => {
                    this.time = new Date(this.time.setSeconds(this.time.getSeconds() + 1));
                    this.setDate(this.time);
                }, 1000);
            }
        );
    }
}

window.customElements.define('analog-clock', AnalogClock);

问题原因

  1. Lit版本冲突:Vaadin自带了Lit库,手动引入外部lit包会和内置版本冲突,导致样式加载机制异常,覆盖全局样式
  2. 样式加载顺序/优先级问题:@JsModule引入组件后,Lit的样式处理打乱了Vaadin原本的全局样式加载流程,导致自定义CSS被忽略

解决方法

1. 对齐Lit依赖版本

把组件中的Lit导入路径改成Vaadin内置的版本:

// Vaadin 14-22
import {css, html, LitElement} from '@vaadin/flow-frontend/lit';
// Vaadin 23+
import {css, html, LitElement} from '@vaadin/lit';

删除项目中手动添加的外部lit依赖,避免版本冲突

2. 规范Vaadin样式导入

确保自定义全局CSS放在frontend/styles目录下,通过@CssImport注解引入:

@CssImport("./styles/custom-styles.css")
public class YourView extends VerticalLayout {
    // ...
}

3. 组件迁移到Vaadin标准目录

把时钟组件JS文件放到frontend/components/analog-clock.js,然后在视图中直接使用<analog-clock>标签,Vaadin会自动处理组件的加载和样式隔离

4. 临时方案:关闭Shadow DOM(不推荐)

如果上面方法无法快速解决,可以在Lit组件中添加方法关闭Shadow DOM(会失去样式隔离优势):

class AnalogClock extends LitElement {
    // ...
    createRenderRoot() {
        // 不使用Shadow DOM,直接渲染到主DOM树
        return this;
    }
    // ...
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 06:16:20