Skip to content

原子化组件-主题选择

简述

最近在研究无头组件,看到shadcn-vue的主题切换组件,又看了una-ui实现。以此做个记录。

shadcn-vue

ts
import { isClient, watchImmediate } from '@vueuse/core'

const COOKIE_NAME = 'active_theme'
const DEFAULT_THEME = 'default'

export function useThemeConfig() {
  const activeTheme = useCookie<string>(COOKIE_NAME, { default: () => DEFAULT_THEME, path: '/', maxAge: 31536000, sameSite: 'lax' })

  watchImmediate(activeTheme, () => {
    if (!isClient)
      return

    Array.from(document.body.classList)
      .filter(className => className.startsWith('theme-'))
      .forEach((className) => {
        document.body.classList.remove(className)
      })
    document.body.classList.add(`theme-${activeTheme.value}`)
    if (activeTheme.value.endsWith('-scaled')) {
      document.body.classList.add('theme-scaled')
    }
  })

  return {
    activeTheme,
  }
}

其核心在于使用useCookie来存储主题选择,使用watchImmediate来监听主题变化并更新document.body.classList

una-ui

ts
import { defineNuxtPlugin } from '#app'
import { computed, watchEffect } from 'vue'
import { useUnaSettings } from '../composables/useUnaSettings'

let unaUIStyle: HTMLStyleElement

export default defineNuxtPlugin(() => {
  const { settings } = useUnaSettings()

  unaUIStyle = document.createElement('style')
  unaUIStyle.id = 'una-ui-theme'
  document.head.appendChild(unaUIStyle)

  // remove mock attribute from html tag
  const html = document.documentElement
  html.removeAttribute('style')

  // created a computed property for styles
  const computedStyles = computed(() => {
    return `
    :root {
        ${Object.entries(settings.value.primaryColors).map(([k, v]) => `${k}: ${v};`).join('\n')}
        ${Object.entries(settings.value.grayColors).map(([k, v]) => `${k}: ${v};`).join('\n')}
        --una-radius: ${settings.value.radius}rem;
        --una-font-size: ${settings.value.fontSize}px;
    }
    `.replace(/\s*\n\s*/g, '')
  })

  watchEffect(() => {
    const styleTag = document.getElementById('una-ui-theme')
    if (styleTag)
      styleTag.innerHTML = computedStyles.value // Use computed styles
  })
})

其核心在于使用computed来计算主题样式,并使用watchEffect来监听样式变化并更新<style>标签的内容。

更新于:

夜茶 2020 ~ 2026