作者:
Mostafa Said
译者:
林语冰
资源:VueSchool 官方博客[1]
免责声明:活人翻译,略有删改,仅供粉丝参考!
00. Hello World
大家好,我是大家的 林语冰
。
CSS 变量已经成为创建动态可维护样式的强大工具。
在本文中,我们会探讨如何使用 JS 更新 CSS 变量,以及如何将 CSS 变量集成到 Vue 组件中,使用 v-bind
替代 CSS 变量,从而实现动态主题切换等功能。
01. CSS 变量的优势
CSS 变量[2] 允许你只定义一次值,就能在整个样式表中重复使用。
无论你是处理主题切换还是响应式设计,CSS 变量都能信手拈来。
举个栗子:
/* 定义 CSS 变量 */
:root {
--main-color: #42b983;
--text-size: 16px;
}
/* 使用 CSS 变量 */
body {
background-color: var(--main-color);
font-size: var(--text-size);
}
上述代码中,--main-color
和 --text-size
是在根级别定义的,可以在整个样式表中访问。
如果你想根据用户交互动态更改 --main-color
,那该怎么办?这就是 JS 的用武之地。
02. 使用 JS 读写 CSS 变量
JS 通过与 DOM 的 style
属性交互,可以在运行时操作 CSS 变量。
document.documentElement
和 CSS 中的 :root
允许你读写全局 CSS 变量。getComputedStyle
方法会检索 CSS 变量的当前值,而 setProperty
方法则可以更新它。
// 读取根元素
const root = document.documentElement
// 检索 CSS 变量的当前值
const currentColor = getComputedStyle(root).getPropertyValue('--main-color')
// 更新 CSS 变量
root.style.setProperty('--main-color', '#ff5733')
03. Vue 更新 CSS 变量
Vue 是响应式框架,所以它非常适合动态更新 CSS 变量。
你可以轻松地将更新逻辑集成到 Vue 组件的生命周期中,或者将其绑定到计算属性,实现无缝更新。
更新 Vue 组件中的 CSS 变量:
<template>
<div>
<button @click="changeColor">改变主题</button>
</div>
</template>
<script setup>
const changeColor = () => {
const root = document.documentElement
root.style.setProperty('--main-color', '#ff5733')
}
</script>
<style>
:root {
--main-color: #42b983;
}
body {
background-color: var(--main-color);
}
</style>
在这个示例中,单击按钮会触发 changeColor
方法,该方法会更新 --main-color
CSS 变量,页面的背景颜色也会响应式更新。
04. 使用 watch
watch API[3] 也可以根据数据更改响应式更新 CSS 变量。比如主题切换或深/浅模式时,这特别有用。
假设在 :root
中有 --main-color
变量,我们用它来设置主体颜色:
:root {
--main-color: #42b983;
}
body {
background-color: var(--main-color);
}
使用组合式 API + <script setup>
+ watch
全家桶:
<template>
<div>
<button @click="toggleTheme">切换主题</button>
</div>
</template>
<script setup>
import { ref, watch } from 'vue'
const root = document.documentElement
const darkThemeHex = '#333'
const lightThemeHex = '#42b983'
const isDarkTheme = ref(false)
watch(isDarkTheme, (oldValue, newValue) => {
const newColor = newValue ? lightThemeHex : darkThemeHex
root.style.setProperty('--main-color', newColor)
})
const toggleTheme = () => {
isDarkTheme.value = !isDarkTheme.value
}
</script>
05. 现实场景
5.1. 主题切换
动态更改 CSS 变量非常适合主题切换。你可以将多个主题存储为 CSS 变量集,并根据用户偏好进行更新。
比如,你可以在浅/深色主题间切换,甚至可以对字体大小精细控制。
5.2 响应式设计
CSS 变量的另一个用法是响应式设计,你可以根据屏幕尺寸更新布局样式。
比如,通过根据调整大小事件更新 CSS 变量,可以轻松地更改移动用户的间距或字体大小。
window.addEventListener('resize', () => {
const root = document.documentElement
const newFontSize = window.innerWidth < 600 ? '14px' : '16px'
root.style.setProperty('--text-size', newFontSize)
})
06. 性能考虑
JS 更新 CSS 变量必须注意性能,过多的 DOM 操作会导致布局重排。
Vue 的响应性系统通过高效的批量更新来优化性能。但是,最好尽量减少更新 CSS 变量的次数,尤其是在滚动或调整大小等频繁事件中。
6.1 批量更新
更新多个 CSS 变量时,将它们分组到单个操作中,可以减少浏览器回流并提高性能。
使用组合式 API 和 <script setup>
批量更新 CSS 变量:
<template>
<div>
<button @click="updateVariables">更新变量</button>
</div>
</template>
<script setup>
const updateVariables = () => {
const root = document.documentElement
// 批量更新
root.style.cssText = `
--main-color: #ff5733;
--text-size: 18px;
--border-radius: 10px;
`
}
</script>
<style>
:root {
--main-color: #42b983;
--text-size: 14px;
}
body {
background-color: var(--main-color);
font-size: var(--text-size);
}
</style>
6.2 防抖更新
当更新 CSS 变量触发频繁事件时,请对更新进行防抖操作,从而提高性能。
使用组合式 API 消除 resize
事件的抖动:
<template>
<p>调整窗口可以看到字体大小变化</p>
</template>
<script setup>
import { onMounted, onBeforeUnmount } from 'vue'
let debounceTimeout = null
const updateTextSize = () => {
const newFontSize = window.innerWidth < 600 ? '14px' : '16px'
document.documentElement.style.setProperty('--text-size', newFontSize)
}
const debounceResize = () => {
clearTimeout(debounceTimeout)
debounceTimeout = setTimeout(() => {
updateTextSize()
}, 200)
}
onMounted(() => {
window.addEventListener('resize', debounceResize)
})
onBeforeUnmount(() => {
window.removeEventListener('resize', debounceResize)
})
</script>
<style>
:root {
--text-size: 16px;
}
p {
font-size: var(--text-size);
}
</style>
这里,debounceResize
方法确保 CSS 变量更新只会在窗口大小调整停止后发生。
我们可以直接使用 VueUse 的 useDebounceFn
辅助函数来实现同款功能:
<script setup>
import { onMounted, onBeforeUnmount } from 'vue'
import { useDebounceFn } from '@vueuse/core'
const updateTextSize = () => {
const newFontSize = window.innerWidth < 600 ? '14px' : '16px'
document.documentElement.style.setProperty('--text-size', newFontSize)
}
const debounceResize = useDebounceFn(() => {
updateTextSize()
}, 200)
onMounted(() => {
window.addEventListener('resize', debounceResize)
})
onBeforeUnmount(() => {
window.removeEventListener('resize', debounceResize)
})
</script>
6.3 作用域限制
如果可能,将 CSS 变量更新的作用域限制到特定组件,而不是全局应用。这有助于保持影响局部化,并提高可维护性。
下面是 Vue 组件作用域变量更新的示例:
<template>
<div ref="themeContainer" class="theme-container">
<button @click="changeTheme">切换主题</button>
</div>
</template>
<script setup>
import { useTemplateRef } from 'vue'
const container = useTemplateRef('themeContainer')
const changeTheme = () => {
container.value.style.setProperty('--theme-color', '#007bff')
}
</script>
<style scoped>
/* 作用域限制 */
.theme-container {
--theme-color: #42b983;
width: 100%;
height: 100vh;
background-color: var(--theme-color);
}
button {
color: #fff;
background-color: var(--theme-color);
}
</style>
上述示例中,CSS 变量 --theme-color
的作用域是 .theme-container
,其效果受限于该组件。
我们甚至可以依赖 Vue 的 SFC CSS 功能,而不是定义 CSS 变量:
<template>
<div class="theme-container">
<button @click="changeTheme">主题切换</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
const themeColor = ref('#42b983')
const changeTheme = () => {
themeColor.value = '#007bff'
}
</script>
<style scoped>
.theme-container {
width: 100%;
height: 100vh;
background-color: v-bind(themeColor);
}
button {
color: #fff;
background-color: v-bind(themeColor);
}
</style>
上述示例中,我们没有定义 CSS 变量。
相反,我们定义了一个 themeColor
变量 ref,并使用 CSS 中的 v-bind()
将其值绑定到相应的 CSS 属性。
高潮总结
使用 JS 更新 CSS 变量提供了一种创建动态和响应式 UI 的强大方案,和 Vue 一起使用更加强大。
无论是实现主题切换还是调整字体样式,将 CSS 变量与 Vue 的响应性结合都能信手拈来。
我是大家的 林语冰
👨💻,欢迎持续 关注,随时了解海内外前端开发的最新情报。
谢谢的大家点赞、留言和友情转发,我们下期再见~👍
参考文献
[1] VueSchool 官方博客: vueschool.io/articles/vu…
[2] CSS 变量: www.w3schools.com/css/css3_va…
[3] watch API: vueschool.io/lessons/the…