原子化组件-主题选择
简述
最近在研究无头组件,看到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>标签的内容。
