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

原生JavaScript实现的Web Component在属性变更后如何触发重新渲染?

问题原因
  • 模板渲染逻辑仅在组件挂载触发的connectedCallback生命周期中执行了一次,后续数据变更不会自动同步到DOM
  • attributeChangedCallback监听到属性变更后,仅更新了实例上的变量值,没有触发视图更新逻辑
修复方案

方案1:全量重绘(实现最简单)

抽离公共渲染方法,属性变更时主动触发重绘,每次重绘前清空旧的DOM内容,避免重复挂载:

class TimePanel extends HTMLElement {
    constructor() {
        super();
        this.attachShadow({ mode: "open" })
    }

    getTemplate() {
        const template = document.createElement('template');
        template.innerHTML = `
            <section class="general-panel">
            <div class="inner-card work">
                <div class="sub-panel">
                <div class="flex-container">
                    <p class="card-title">${this.area}</p>
                    <span class="options">
                            <img src="../../images/icon-ellipsis.svg" alt="">
                    </span>
                </div>
                <div class="flex-container">
                    <span class="time">${this.time}Hs</span>
                    <span class="previous-date">last week - ${this.previousDate}Hs</span>
                </div>
                </div>
            </div>
        `
        return template;
    }

    // 抽离公共渲染方法
    render() {
        // 清空原有内容避免重复挂载
        this.shadowRoot.innerHTML = '';
        this.shadowRoot.append(this.getTemplate().content.cloneNode(true))
    }

    connectedCallback() {
        this.render();
    }

    static get observedAttributes() {
        return ['area','time','previous-date'];
    }

    attributeChangedCallback(name, old, newV) {
        // 新旧值一致时跳过更新,避免无效渲染
        if(old === newV) return;
        if(name === 'area') {
            this.area = newV;
        }
        if(name === 'time') {
            this.time = newV;
        }
        if(name === 'previous-date') {
            this.previousDate = newV;
        }
        // 属性更新后触发重绘
        this.render();
    }
}

customElements.define('time-panel', TimePanel)

方案2:局部更新(性能更优)

仅更新发生变化的DOM节点,不需要重绘整个组件,适合结构复杂的组件:

class TimePanel extends HTMLElement {
    constructor() {
        super();
        this.attachShadow({ mode: "open" })
        // 缓存DOM节点引用
        this.areaEl = null;
        this.timeEl = null;
        this.previousDateEl = null;
    }

    getTemplate() {
        const template = document.createElement('template');
        template.innerHTML = `
            <section class="general-panel">
            <div class="inner-card work">
                <div class="sub-panel">
                <div class="flex-container">
                    <p class="card-title">${this.area}</p>
                    <span class="options">
                            <img src="../../images/icon-ellipsis.svg" alt="">
                    </span>
                </div>
                <div class="flex-container">
                    <span class="time">${this.time}Hs</span>
                    <span class="previous-date">last week - ${this.previousDate}Hs</span>
                </div>
                </div>
            </div>
        `
        return template;
    }

    connectedCallback() {
        this.shadowRoot.append(this.getTemplate().content.cloneNode(true))
        // 挂载时缓存需要更新的节点引用
        this.areaEl = this.shadowRoot.querySelector('.card-title');
        this.timeEl = this.shadowRoot.querySelector('.time');
        this.previousDateEl = this.shadowRoot.querySelector('.previous-date');
    }

    static get observedAttributes() {
        return ['area','time','previous-date'];
    }

    attributeChangedCallback(name, old, newV) {
        if(old === newV) return;
        // 直接更新对应节点的文本内容,无需重绘整个组件
        if(name === 'area') {
            this.area = newV;
            this.areaEl?.textContent = newV;
        }
        if(name === 'time') {
            this.time = newV;
            this.timeEl?.textContent = `${newV}Hs`;
        }
        if(name === 'previous-date') {
            this.previousDate = newV;
            this.previousDateEl?.textContent = `last week - ${newV}Hs`;
        }
    }
}

customElements.define('time-panel', TimePanel)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 01:06:03