<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:wfw="http://wellformedweb.org/CommentAPI/">
<channel>
<title>Gabriel Ryder&#039;s Blog - 图标选择器</title>
<link>https://www.dooper.top/index.php/tag/%E5%9B%BE%E6%A0%87%E9%80%89%E6%8B%A9%E5%99%A8/</link>
<atom:link href="https://www.dooper.top/index.php/feed/tag/%E5%9B%BE%E6%A0%87%E9%80%89%E6%8B%A9%E5%99%A8/" rel="self" type="application/rss+xml" />
<language>zh-CN</language>
<description></description>
<lastBuildDate>Tue, 16 Dec 2025 20:49:00 +0800</lastBuildDate>
<pubDate>Tue, 16 Dec 2025 20:49:00 +0800</pubDate>
<item>
<title>Vue 3 图标选择器实现：基于 lucide-vue-next + shadcn/vue 的高效方案</title>
<link>https://www.dooper.top/index.php/archives/9/</link>
<guid>https://www.dooper.top/index.php/archives/9/</guid>
<pubDate>Tue, 16 Dec 2025 20:49:00 +0800</pubDate>
<dc:creator>Gabriel Ryder</dc:creator>
<description><![CDATA[最近在开发 Vue 3 项目时，需要一个灵活的图标选择器组件 —— 既要支持大量图标快速检索，又能无缝集成到现有界面，还得让父组件动态接收选中结果。对比了几个图标库后，最终选择了 lucide-...]]></description>
<content:encoded xml:lang="zh-CN"><![CDATA[
<p>最近在开发 Vue 3 项目时，需要一个灵活的图标选择器组件 —— 既要支持大量图标快速检索，又能无缝集成到现有界面，还得让父组件动态接收选中结果。对比了几个图标库后，最终选择了 lucide-vue-next（轻量、图标丰富、支持 Vue 3 按需导入），再配合 shadcn/vue 的基础组件快速搭建交互界面，完美实现了需求。这里把完整实现过程分享出来，希望能帮到有同样需求的朋友。</p><h1>先说说核心需求与技术选型</h1><h2>需求明确</h2><p>支持 lucide-vue-next 全量图标选择（无需手动逐个导入）<br>提供搜索功能，快速筛选目标图标<br>支持分页加载（避免一次性渲染过多图标导致性能问题）<br>选择图标后，通过事件通知父组件<br>父组件能动态渲染选中的图标组件</p><h2>技术选型原因</h2><ul><li><strong>lucide-vue-next</strong>：轻量级矢量图标库，内置 1000+ 常用图标，支持批量导入，且与 Vue 3 兼容性极佳，图标尺寸、颜色可灵活定制。</li><li><strong>shadcn/vue</strong>：提供现成的基础组件（DropdownMenu、Input、Tooltip、Button 等），样式统一且可自定义，无需从零编写交互组件，节省开发时间。</li><li><strong>Vue 3 动态组件</strong>：通过 <code>defineAsyncComponent</code> 和 <code>&lt;component :is=&quot;xxx&quot;&gt;</code> 语法，实现选中图标后的动态引入，避免初始加载冗余资源。</li></ul><h1>图标选择器组件（IconSelector.vue）</h1><p>这是核心组件，负责图标展示、搜索过滤、分页加载和选中事件触发。整体结构分为「搜索框 + 图标网格 + 加载更多按钮」，利用 shadcn 组件快速搭建交互逻辑：</p><pre><code class="lang-vue">&lt;template&gt;
  &lt;!-- 基于 shadcn DropdownMenu 实现弹出式选择器 --&gt;
  &lt;DropdownMenuContent class=&quot;w-80 p-2&quot;&gt;
    &lt;!-- 搜索框：筛选图标 --&gt;
    &lt;Input 
      class=&quot;w-full mb-2&quot; 
      v-model=&quot;searchQuery&quot; 
      :placeholder=&quot;$t(&#039;searchIconTip&#039;)&quot; 
    /&gt;
    &lt;!-- 图标网格：分页渲染，支持滚动 --&gt;
    &lt;div class=&quot;grid grid-cols-8 max-h-80 gap-2 p-2 overflow-y-auto&quot;&gt;
      &lt;div 
        v-for=&quot;iconName in filteredIcons.slice(0, currentIndex + batchSize)&quot; 
        :key=&quot;iconName&quot;
        class=&quot;flex justify-center items-center cursor-pointer&quot;
      &gt;
        &lt;!-- Tooltip 显示图标名称 --&gt;
        &lt;TooltipProvider&gt;
          &lt;Tooltip&gt;
            &lt;TooltipTrigger as-child&gt;
              &lt;Button 
                variant=&quot;outline&quot; 
                size=&quot;icon&quot; 
                @click=&quot;selectIcon(iconName)&quot;
                class=&quot;hover:bg-primary/10 transition-colors&quot;
              &gt;
                &lt;!-- 动态渲染 lucide 图标 --&gt;
                &lt;component :is=&quot;allIcons[iconName]&quot; :size=&quot;24&quot; /&gt;
              &lt;/Button&gt;
            &lt;/TooltipTrigger&gt;
            &lt;TooltipContent&gt;
              &lt;p&gt;{{ iconName }}&lt;/p&gt;
            &lt;/TooltipContent&gt;
          &lt;/Tooltip&gt;
        &lt;/TooltipProvider&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;!-- 加载更多按钮：按需渲染图标 --&gt;
    &lt;Button 
      class=&quot;w-full mt-2&quot; 
      variant=&quot;outline&quot; 
      v-if=&quot;hasMore&quot; 
      @click=&quot;loadMore&quot;
    &gt;
      {{ $t(&#039;loadMore&#039;) }}
    &lt;/Button&gt;
  &lt;/DropdownMenuContent&gt;
&lt;/template&gt;

&lt;script setup&gt;
import { ref, computed, defineEmits } from &#039;vue&#039;
// 导入 shadcn 基础组件
import { Button } from &#039;@/components/ui/button&#039;
import { DropdownMenuContent } from &#039;@/components/ui/dropdown-menu&#039;
import { Input } from &#039;@/components/ui/input&#039;
import {
  Tooltip,
  TooltipContent,
  TooltipProvider,
  TooltipTrigger,
} from &#039;@/components/ui/tooltip&#039;
// 批量导入 lucide-vue-next 所有图标
import * as allIcons from &#039;lucide-vue-next&#039;

// 定义事件：向父组件传递选中的图标名称
const emit = defineEmits([&#039;iconSelected&#039;])

// 所有图标名称（从导入的模块中提取 key）
const allIconKeys = Object.keys(allIcons)

// 分页配置：每次加载 50 个图标
const batchSize = 50
const currentIndex = ref(0)
// 搜索关键词
const searchQuery = ref(&#039;&#039;)

// 过滤图标：根据搜索关键词模糊匹配（不区分大小写）
const filteredIcons = computed(() =&gt; {
  return allIconKeys.filter(iconName =&gt; {
    return iconName.toLowerCase().includes(searchQuery.value.toLowerCase())
  })
})

// 判断是否还有更多图标可加载
const hasMore = computed(() =&gt; {
  return currentIndex.value + batchSize &lt; filteredIcons.value.length
})

// 加载更多图标
const loadMore = () =&gt; {
  currentIndex.value += batchSize
}

// 选中图标：触发事件向父组件传递结果
const selectIcon = (iconName) =&gt; {
  emit(&#039;iconSelected&#039;, iconName)
}
&lt;/script&gt;

&lt;style scoped&gt;
/* 优化网格布局：自适应列宽，确保图标显示均匀 */
.grid {
  grid-template-columns: repeat(auto-fill, minmax(40px, 1fr));
}
/* 移除按钮默认内边距，让图标居中显示 */
:deep(.btn-icon) {
  padding: 0;
}
&lt;/style&gt;</code></pre><h2>父组件集成与动态渲染</h2><p>父组件需要接收 <code>IconSelector</code> 传递的图标名称，通过 Vue 3 的 <code>defineAsyncComponent</code> 动态导入对应图标组件，再用 <code>&lt;component :is=&quot;xxx&quot;&gt;</code> 渲染：</p><pre><code class="lang-vue">&lt;template&gt;
  &lt;!-- 基于 shadcn DropdownMenu 触发图标选择器 --&gt;
  &lt;DropdownMenu&gt;
    &lt;DropdownMenuTrigger as-child&gt;
      &lt;Button variant=&quot;outline&quot; size=&quot;icon&quot;&gt;
        &lt;!-- 动态渲染选中的图标（默认显示 Squircle 图标） --&gt;
        &lt;component :is=&quot;selectedIconComponent || Squircle&quot; :size=&quot;24&quot; /&gt;
      &lt;/Button&gt;
    &lt;/DropdownMenuTrigger&gt;
    &lt;!-- 引入图标选择器组件，监听选中事件 --&gt;
    &lt;IconSelector @iconSelected=&quot;handleIconSelected&quot; /&gt;
  &lt;/DropdownMenu&gt;
&lt;/template&gt;

&lt;script setup&gt;
// 导入默认图标（未选择时显示）
import { Squircle } from &quot;lucide-vue-next&quot;
import { ref, shallowRef, defineAsyncComponent } from &quot;vue&quot;
// 导入图标选择器组件
import IconSelector from &#039;@/components/IconSelector.vue&#039;

// 存储选中的图标名称和组件
const selectedIconName = ref(&#039;&#039;)
const selectedIconComponent = shallowRef(null)

// 处理图标选中事件：动态导入对应图标组件
const handleIconSelected = (iconName: string) =&gt; {
  selectedIconName.value = iconName
  // 动态导入 lucide-vue-next 中的目标图标（TS 忽略类型检查）
  // @ts-ignore
  selectedIconComponent.value = defineAsyncComponent(() =&gt;
    import(&#039;lucide-vue-next&#039;).then((mod) =&gt; mod[iconName])
  )
}
&lt;/script&gt;</code></pre><h1>最后总结</h1><p>这个图标选择器组件的核心优势在于 “高效 + 灵活”：</p><ul><li>借助 lucide-vue-next 的批量导入能力，无需手动导入单个图标，极大提升开发效率；</li><li>基于 shadcn/vue 的基础组件，快速搭建交互界面，样式统一且可复用；</li><li>分页加载和搜索功能确保了大量图标场景下的性能和易用性；</li><li>父组件通过动态组件接收结果，集成成本极低，可灵活用于按钮、表单、导航等多种场景。<br>整个实现过程中，最大的感受是：Vue 3 的组合式 API 和动态组件语法让组件通信和资源加载更灵活，而 lucide-vue-next 与 shadcn/vue 的组合则完美平衡了 “功能丰富” 和 “开发效率”。如果你的 Vue 3 项目也需要图标选择功能，不妨试试这个方案～</li></ul>
]]></content:encoded>
<slash:comments>2</slash:comments>
<comments>https://www.dooper.top/index.php/archives/9/#comments</comments>
<wfw:commentRss>https://www.dooper.top/index.php/feed/tag/%E5%9B%BE%E6%A0%87%E9%80%89%E6%8B%A9%E5%99%A8/</wfw:commentRss>
</item>
</channel>
</rss>