👍 Vue 不仅适合 CSS 变量,还可以取代 CSS 变量

934 阅读5分钟

00-css.png

作者: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-size16px;
}
/* 使用 CSS 变量 */
body {
  background-colorvar(--main-color);
  font-sizevar(--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 变量,页面的背景颜色也会响应式更新。

01-click.gif

04. 使用 watch

watch API[3] 也可以根据数据更改响应式更新 CSS 变量。比如主题切换或深/浅模式时,这特别有用。

假设在 :root 中有 --main-color 变量,我们用它来设置主体颜色:

:root {
  --main-color#42b983;
}
body {
  background-colorvar(--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…