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
相关产品推荐
相关产品推荐

