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

Vue3 setup函数内原生JS创建的DOM元素CSS样式未生效问题

问题场景

开发小型网站时,为了避免在模板中硬编码重复元素,计划编写函数批量渲染同类元素,初始实现代码如下:

  • 模板部分
<template>
<div class="wrapper">
    <div class="skills" ref="canvas">
    </div>
</div>
</template>
  • 逻辑部分
import { ref, onMounted } from 'vue';
import MySkills from '../constants/skills';
export default {
    setup() {
        const skills = MySkills
        const canvas = ref(null);
        const displaySkills = () => {
            skills.forEach(skill => {
                const el = document.createElement('div');
                el.className = 'skill';

                el.innerText = skill.name;
                const level = document.createElement('span');
                level.className = 'level';
                level.innerText = skill.level;
                el.appendChild(level);
                canvas.value.appendChild(el);
            })
        }
        onMounted(() => {
            displaySkills();
        })
        return {
            canvas,
            displaySkills
        }
    }
}
  • 独立CSS文件样式
.skill {
    padding: 10px;
    border-radius: 100px;
    text-align: center;
    position: relative;
    background-color: aqua;
}

.level {
    position: absolute;
    top: -5%;
    left: 0%;
    padding: 5px;
    border-radius: 100px;
    text-align: center;
}
问题表现

通过原生JS动态创建的类名为skill的div元素、类名为level的span元素,对应的CSS样式完全不生效。

原因说明

这类问题绝大多数是因为组件的style标签加了scoped属性:Vue的scoped样式编译时会给当前组件内静态渲染的元素添加唯一的data属性标记(比如data-v-7ba5bd90),对应CSS选择器也会自动拼接这个属性选择器实现样式隔离。但用原生document.createElement创建的元素不会被Vue自动加上这个唯一data标记,自然匹配不到带属性选择器的scoped样式,样式就会失效。

可行解决方案

方案1(最推荐,符合Vue设计逻辑)

放弃原生DOM操作,用Vue原生的v-for指令做列表渲染,从根源避免手动操作DOM带来的各类问题,修改后的代码如下:

<template>
<div class="wrapper">
    <div class="skills">
        <div class="skill" v-for="skill in skills" :key="skill.name">
            {{ skill.name }}
            <span class="level">{{ skill.level }}</span>
        </div>
    </div>
</div>
</template>

<script>
import MySkills from '../constants/skills';
export default {
    setup() {
        const skills = MySkills
        return { skills }
    }
}
</script>

这种写法渲染的元素会被Vue自动加上scoped对应的data标记,原有样式不需要任何修改就能正常生效。

方案2(保留原生DOM操作的前提下修复)

如果确实有特殊原因必须用原生JS创建元素,可以二选一调整样式:

  • 移除对应style标签的scoped属性,把这部分样式改为全局样式,注意做好类名命名避免和其他组件样式冲突
  • 保留scoped的前提下,用Vue的深度选择器:deep()包裹这两个样式规则,修改后样式如下:
:deep(.skill) {
    padding: 10px;
    border-radius: 100px;
    text-align: center;
    position: relative;
    background-color: aqua;
}

:deep(.level) {
    position: absolute;
    top: -5%;
    left: 0%;
    padding: 5px;
    border-radius: 100px;
    text-align: center;
}

深度选择器编译后不会强制要求目标元素本身带scoped的data标记,只要元素在当前组件的DOM树内就能匹配到样式。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 04:48:16