一、问题背景
在 Vue 项目中,我们通常会在入口文件 App.vue 或根元素 #app 上编写样式。例如设置字体、主题色、背景色等。然而,许多开发者容易忽略一个细节:并不是所有的 DOM 元素都在 #app 内部渲染。
这会导致一些组件(如使用 teleport 到 body 的弹窗、提示框、遮罩层等)无法继承到 #app 的样式,从而出现字体错乱、主题色不生效等问题。
二、teleport 导致的样式隔离问题
Vue 的 teleport 特性允许组件内容渲染到 #app 以外的节点中,例如:
html
<teleport to="body">
<div class="global-dialog">我是弹窗</div>
</teleport>
此时,这个弹窗的 DOM 实际上是直接插入到 <body> 下,而不是 Vue 挂载的 #app 内部。
如果我们之前把全局样式(如字体、颜色变量)都写在 #app 上,比如:
css
#app {
font-family: "PingFang SC", sans-serif;
color: var(--font-color);
}
那么 teleport 到 body 的元素将无法继承这些样式 ,因为它并不在 #app 的作用范围中。
三、正确的全局样式写法
全局样式应当写在 html 或 body 上,而不是 #app。
推荐写法:
css
html,
body {
font-family: "PingFang SC", "Microsoft YaHei", sans-serif;
color: var(--font-color, #333);
background-color: var(--bg-color, #fff);
}
#app {
min-height: 100%;
display: flex;
flex-direction: column;
}
这样,无论组件内容挂载在哪个节点下(包括 body、#app、teleport 节点),都能共享同样的全局主题样式。
四、实际项目中的应用建议
-
字体、颜色、主题变量统一写在
html, body中。这样能确保任何层级的组件都能继承这些基础样式。
-
#app 用于布局和结构控制。
比如
display: flex;、min-height: 100vh;、overflow: hidden;等结构性样式。 -
全局 CSS 变量放在 :root 或 html。
示例:
css:root { --color-primary: #1677ff; --font-color: #333; --bg-color: #fff; } -
重置样式或主题文件在入口处引入。
如在
main.ts中:cssimport "@/styles/reset.scss" import "@/styles/theme.scss"
五、总结
Vue 项目中的全局样式不应依赖 #app,而应当绑定在 html 或 body 上。
这是因为部分组件(尤其是通过 teleport 实现的弹窗、浮层等)并不会在 #app 节点内渲染。
良好的样式结构分层能让整个项目在视觉和逻辑上更加统一,避免组件层级带来的样式继承问题。