Vue Composition API与SVG雪碧图动态图标实践
Vue Composition API 与 SVG 雪碧图的高级实践
动态图标切换实现
在现代前端应用中,图标动态切换是常见的交互需求。通过 Composition API 我们可以优雅地实现这一功能。
响应式图标控制
使用 ref
控制 <use>
元素的引用是实现动态切换的核心:
<template>
<svg class="icon" @click="toggleIcon">
<use :xlink:href="`#${currentIcon}`" />
</svg>
</template>
<script setup>
import { ref } from 'vue'
const icons = ['home', 'settings', 'user'] // 可用的图标名称数组
const currentIcon = ref(icons[0])
const toggleIcon = () => {
const currentIndex = icons.indexOf(currentIcon.value)
const nextIndex = (currentIndex + 1) % icons.length
currentIcon.value = icons[nextIndex]
}
</script>
关键点解析:
xlink:href
属性通过响应式变量动态绑定- 点击事件触发图标顺序切换
- 使用模运算实现循环切换
进阶实现:带状态的图标
<script setup>
import { ref, computed } from 'vue'
const iconState = ref('normal') // normal, hover, active
const currentIcon = computed(() => {
return `${baseIconName}-${iconState.value}`
})
</script>
实践建议:
- 图标命名采用
iconname-state
的约定(如home-active
) - 使用
computed
派生复杂图标名称 - 通过 CSS 类名控制不同状态下的样式
组合式函数封装
将 SVG 雪碧图逻辑抽象为可复用的组合式函数:
useSvgSprite 实现
// useSvgSprite.js
import { ref, computed } from 'vue'
export default function useSvgSprite(initialIcon, availableIcons = []) {
const currentIcon = ref(initialIcon)
const icons = ref(availableIcons)
const setIcon = (iconName) => {
if (!icons.value.includes(iconName)) {
console.warn(`Icon ${iconName} is not available`)
return
}
currentIcon.value = iconName
}
const nextIcon = () => {
const currentIndex = icons.value.indexOf(currentIcon.value)
const nextIndex = (currentIndex + 1) % icons.value.length
currentIcon.value = icons.value[nextIndex]
}
return {
currentIcon,
setIcon,
nextIcon
}
}
在组件中使用
<script setup>
import useSvgSprite from '@/composables/useSvgSprite'
const { currentIcon, nextIcon } = useSvgSprite('home', ['home', 'search', 'user'])
</script>
封装优势:
- 统一管理图标切换逻辑
- 内置输入验证防止无效图标
- 可在多个组件间复用
- 易于扩展新功能(如随机切换、动画效果等)
类型增强版本(配合 TypeScript)
// useSvgSprite.ts
import { ref, computed } from 'vue'
type IconName = 'home' | 'search' | 'user' // 联合类型限定可用图标
export default function useSvgSprite(
initialIcon: IconName,
availableIcons: IconName[] = []
) {
// 实现逻辑...
}
性能优化实践
动态加载雪碧图
const loadSprite = async () => {
if (!document.getElementById('sprite-container')) {
const response = await fetch('/sprite.svg')
const svg = await response.text()
const div = document.createElement('div')
div.id = 'sprite-container'
div.style.display = 'none'
div.innerHTML = svg
document.body.appendChild(div)
}
}
// 在组件挂载时调用
onMounted(loadSprite)
缓存策略实现
优化建议:
- 使用
localStorage
缓存雪碧图内容 - 添加版本控制避免缓存过期问题
- 大项目考虑按需加载图标分组
常见问题解决方案
1. 图标闪烁问题
原因:雪碧图加载完成前组件已渲染
解决方案:
<template>
<svg v-if="isLoaded" class="icon">
<use :xlink:href="`#${currentIcon}`" />
</svg>
<div v-else class="placeholder"></div>
</template>
<script setup>
const isLoaded = ref(false)
onMounted(() => {
loadSprite().then(() => {
isLoaded.value = true
})
})
</script>
2. 跨组件状态同步
// sharedIconState.js
import { reactive } from 'vue'
const state = reactive({
currentTheme: 'light',
icons: {
light: ['sun', 'moon'],
dark: ['moon', 'stars']
}
})
export function useSharedIconState() {
return {
state,
setTheme: (theme) => {
state.currentTheme = theme
}
}
}
总结
通过 Composition API 封装 SVG 雪碧图逻辑,我们获得了:
- 更清晰的代码组织 - 将图标相关逻辑集中管理
- 更好的复用性 - 通过组合式函数跨组件共享
- 更强的类型安全 - 配合 TypeScript 增强可靠性
- 更优的性能 - 实现按需加载和智能缓存
进阶方向建议:
- 实现图标过渡动画
- 开发可视化图标选择器
- 集成到设计系统中作为基础组件
- 实现服务端渲染(SSR)兼容方案
评论已关闭