Icon 图标组件
学习目标
- 掌握 Element Plus 图标系统的使用
- 学会图标的安装、导入和注册
- 理解全局注册与按需导入的区别
- 掌握图标尺寸和颜色控制
- 学会在不同组件中使用图标
- 能够使用自定义 SVG 图标
- 理解图标的语义化和可访问性
详细学习内容
1. 图标系统安装与配置(20分钟)
1.1 安装图标库
bash
# 安装 Element Plus 图标库
npm install @element-plus/icons-vue
# 或者
yarn add @element-plus/icons-vue
# 或者
pnpm install @element-plus/icons-vue
1.2 全局注册图标
javascript
// main.js
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import App from './App.vue'
const app = createApp(App)
// 注册 Element Plus
app.use(ElementPlus)
// 全局注册所有图标
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
app.mount('#app')
1.3 按需导入图标
vue
<template>
<div class="icon-demo">
<!-- 直接使用图标组件 -->
<el-icon><Edit /></el-icon>
<el-icon><Delete /></el-icon>
<el-icon><Search /></el-icon>
</div>
</template>
<script setup>
// 按需导入图标
import { Edit, Delete, Search } from '@element-plus/icons-vue'
</script>
2. 基础图标使用(30分钟)
2.1 基础用法
vue
<template>
<div class="basic-icon-demo">
<!-- 基础图标 -->
<el-icon><Star /></el-icon>
<el-icon><StarFilled /></el-icon>
<el-icon><Heart /></el-icon>
<el-icon><Like /></el-icon>
<!-- 带文字的图标 -->
<div class="icon-with-text">
<el-icon><User /></el-icon>
<span>用户</span>
</div>
<div class="icon-with-text">
<el-icon><Setting /></el-icon>
<span>设置</span>
</div>
</div>
</template>
<script setup>
import {
Star,
StarFilled,
Heart,
Like,
User,
Setting
} from '@element-plus/icons-vue'
</script>
<style>
.basic-icon-demo {
display: flex;
align-items: center;
gap: 16px;
}
.icon-with-text {
display: flex;
align-items: center;
gap: 4px;
cursor: pointer;
}
.icon-with-text:hover {
color: var(--el-color-primary);
}
</style>
2.2 图标尺寸控制
vue
<template>
<div class="icon-size-demo">
<!-- 使用 size 属性 -->
<el-icon :size="12"><Star /></el-icon>
<el-icon :size="16"><Star /></el-icon>
<el-icon :size="20"><Star /></el-icon>
<el-icon :size="24"><Star /></el-icon>
<el-icon :size="32"><Star /></el-icon>
<!-- 使用 CSS 控制 -->
<el-icon class="icon-small"><Heart /></el-icon>
<el-icon class="icon-medium"><Heart /></el-icon>
<el-icon class="icon-large"><Heart /></el-icon>
<el-icon class="icon-xlarge"><Heart /></el-icon>
</div>
</template>
<script setup>
import { Star, Heart } from '@element-plus/icons-vue'
</script>
<style>
.icon-size-demo {
display: flex;
align-items: center;
gap: 16px;
}
.icon-small {
font-size: 14px;
}
.icon-medium {
font-size: 18px;
}
.icon-large {
font-size: 24px;
}
.icon-xlarge {
font-size: 32px;
}
</style>
2.3 图标颜色控制
vue
<template>
<div class="icon-color-demo">
<!-- 使用 color 属性 -->
<el-icon color="#409eff"><Star /></el-icon>
<el-icon color="#67c23a"><Star /></el-icon>
<el-icon color="#e6a23c"><Star /></el-icon>
<el-icon color="#f56c6c"><Star /></el-icon>
<el-icon color="#909399"><Star /></el-icon>
<!-- 使用 CSS 类控制 -->
<el-icon class="icon-primary"><Heart /></el-icon>
<el-icon class="icon-success"><Heart /></el-icon>
<el-icon class="icon-warning"><Heart /></el-icon>
<el-icon class="icon-danger"><Heart /></el-icon>
<el-icon class="icon-info"><Heart /></el-icon>
</div>
</template>
<script setup>
import { Star, Heart } from '@element-plus/icons-vue'
</script>
<style>
.icon-color-demo {
display: flex;
align-items: center;
gap: 16px;
}
.icon-primary {
color: var(--el-color-primary);
}
.icon-success {
color: var(--el-color-success);
}
.icon-warning {
color: var(--el-color-warning);
}
.icon-danger {
color: var(--el-color-danger);
}
.icon-info {
color: var(--el-color-info);
}
</style>
3. 图标在组件中的应用(40分钟)
3.1 按钮中的图标
vue
<template>
<div class="button-icon-demo">
<!-- 图标按钮 -->
<el-button type="primary" :icon="Edit">编辑</el-button>
<el-button type="success" :icon="Check">确认</el-button>
<el-button type="warning" :icon="Warning">警告</el-button>
<el-button type="danger" :icon="Delete">删除</el-button>
<!-- 纯图标按钮 -->
<el-button type="primary" :icon="Search" circle />
<el-button type="success" :icon="Edit" circle />
<el-button type="warning" :icon="Star" circle />
<el-button type="danger" :icon="Delete" circle />
<!-- 图标在右侧 -->
<el-button type="primary">
下载
<el-icon class="el-icon--right"><Download /></el-icon>
</el-button>
<el-button type="success">
上传
<el-icon class="el-icon--right"><Upload /></el-icon>
</el-button>
</div>
</template>
<script setup>
import {
Edit,
Check,
Warning,
Delete,
Search,
Star,
Download,
Upload
} from '@element-plus/icons-vue'
</script>
<style>
.button-icon-demo {
display: flex;
flex-wrap: wrap;
gap: 12px;
}
</style>
3.2 输入框中的图标
vue
<template>
<div class="input-icon-demo">
<!-- 前缀图标 -->
<el-input
v-model="searchText"
placeholder="搜索内容"
:prefix-icon="Search"
style="width: 200px;"
/>
<!-- 后缀图标 -->
<el-input
v-model="password"
type="password"
placeholder="请输入密码"
:suffix-icon="View"
style="width: 200px;"
show-password
/>
<!-- 自定义图标插槽 -->
<el-input
v-model="username"
placeholder="用户名"
style="width: 200px;"
>
<template #prefix>
<el-icon><User /></el-icon>
</template>
</el-input>
<el-input
v-model="email"
placeholder="邮箱地址"
style="width: 200px;"
>
<template #suffix>
<el-icon><Message /></el-icon>
</template>
</el-input>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { Search, View, User, Message } from '@element-plus/icons-vue'
const searchText = ref('')
const password = ref('')
const username = ref('')
const email = ref('')
</script>
<style>
.input-icon-demo {
display: flex;
flex-direction: column;
gap: 16px;
}
</style>
3.3 菜单和导航中的图标
vue
<template>
<div class="menu-icon-demo">
<!-- 垂直菜单 -->
<el-menu
default-active="1"
class="el-menu-vertical"
style="width: 200px;"
>
<el-menu-item index="1">
<el-icon><House /></el-icon>
<span>首页</span>
</el-menu-item>
<el-sub-menu index="2">
<template #title>
<el-icon><User /></el-icon>
<span>用户管理</span>
</template>
<el-menu-item index="2-1">
<el-icon><UserFilled /></el-icon>
<span>用户列表</span>
</el-menu-item>
<el-menu-item index="2-2">
<el-icon><Plus /></el-icon>
<span>添加用户</span>
</el-menu-item>
</el-sub-menu>
<el-menu-item index="3">
<el-icon><Setting /></el-icon>
<span>系统设置</span>
</el-menu-item>
<el-menu-item index="4">
<el-icon><Document /></el-icon>
<span>文档中心</span>
</el-menu-item>
</el-menu>
<!-- 水平导航 -->
<el-menu
mode="horizontal"
default-active="1"
class="horizontal-menu"
>
<el-menu-item index="1">
<el-icon><House /></el-icon>
首页
</el-menu-item>
<el-menu-item index="2">
<el-icon><Goods /></el-icon>
产品
</el-menu-item>
<el-menu-item index="3">
<el-icon><Service /></el-icon>
服务
</el-menu-item>
<el-menu-item index="4">
<el-icon><Phone /></el-icon>
联系我们
</el-menu-item>
</el-menu>
</div>
</template>
<script setup>
import {
House,
User,
UserFilled,
Plus,
Setting,
Document,
Goods,
Service,
Phone
} from '@element-plus/icons-vue'
</script>
<style>
.menu-icon-demo {
display: flex;
gap: 32px;
}
.horizontal-menu {
border-bottom: none;
}
</style>
4. 自定义图标(30分钟)
4.1 使用自定义 SVG 图标
vue
<template>
<div class="custom-icon-demo">
<!-- 内联 SVG 图标 -->
<el-icon :size="24">
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
<path fill="currentColor" d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"/>
<path fill="currentColor" d="M623.6 316.7C593.6 290.4 554 276 512 276s-81.6 14.4-111.6 40.7C369.2 344 352 380.7 352 420v7.6c0 4.4 3.6 8 8 8h48c4.4 0 8-3.6 8-8V420c0-44.1 43.1-80 96-80s96 35.9 96 80c0 31.1-22 59.6-56.1 72.7-21.2 8.1-39.2 22.3-52.1 40.9-13.1 19-19.8 41.3-19.8 64.9V620c0 4.4 3.6 8 8 8h48c4.4 0 8-3.6 8-8v-22.7a48.3 48.3 0 0 1 30.9-44.8c59-22.7 97.1-74.7 97.1-132.5.1-39.3-17.1-76-48.4-103.3z"/>
<div>
<path fill="currentColor" d="M512 732a40 40 0 1 0 0 80 40 40 0 0 0 0-80z"/>
</div>
</svg>
</el-icon>
<!-- 自定义图标组件 -->
<el-icon :size="24" color="#409eff">
<CustomIcon />
</el-icon>
<!-- 图标字体 -->
<i class="custom-font-icon"></i>
</div>
</template>
<script setup>
// 自定义图标组件
const CustomIcon = {
name: 'CustomIcon',
render() {
return (
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path
fill="currentColor"
d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"
/>
</svg>
)
}
}
</script>
<style>
.custom-icon-demo {
display: flex;
align-items: center;
gap: 16px;
}
.custom-font-icon {
font-family: 'iconfont';
font-size: 24px;
color: #67c23a;
}
</style>
4.2 图标组件封装
vue
<!-- IconWrapper.vue -->
<template>
<el-icon
:size="size"
:color="color"
:class="iconClass"
@click="handleClick"
>
<component :is="icon" v-if="icon" />
<slot v-else />
</el-icon>
</template>
<script setup>
import { computed } from 'vue'
const props = defineProps({
icon: {
type: [String, Object],
default: null
},
size: {
type: [Number, String],
default: 16
},
color: {
type: String,
default: ''
},
clickable: {
type: Boolean,
default: false
},
disabled: {
type: Boolean,
default: false
}
})
const emit = defineEmits(['click'])
const iconClass = computed(() => {
return {
'icon-clickable': props.clickable && !props.disabled,
'icon-disabled': props.disabled
}
})
const handleClick = (event) => {
if (!props.disabled && props.clickable) {
emit('click', event)
}
}
</script>
<style scoped>
.icon-clickable {
cursor: pointer;
transition: color 0.3s;
}
.icon-clickable:hover {
color: var(--el-color-primary);
}
.icon-disabled {
opacity: 0.5;
cursor: not-allowed;
}
</style>
5. 图标库展示组件(40分钟)
5.1 图标展示网格
vue
<template>
<div class="icon-gallery">
<div class="gallery-header">
<h2>Element Plus 图标库</h2>
<el-input
v-model="searchKeyword"
placeholder="搜索图标..."
:prefix-icon="Search"
style="width: 300px;"
clearable
/>
</div>
<div class="icon-categories">
<el-tabs v-model="activeCategory" @tab-click="handleCategoryChange">
<el-tab-pane
v-for="category in categories"
:key="category.name"
:label="category.label"
:name="category.name"
>
<div class="icon-grid">
<div
v-for="iconName in filteredIcons(category.icons)"
:key="iconName"
class="icon-item"
@click="copyIconName(iconName)"
>
<el-icon :size="32">
<component :is="iconName" />
</el-icon>
<span class="icon-name">{{ iconName }}</span>
</div>
</div>
</el-tab-pane>
</el-tabs>
</div>
<!-- 复制提示 -->
<el-dialog v-model="showCopyDialog" title="复制成功" width="400px">
<p>图标名称已复制到剪贴板:</p>
<el-input v-model="copiedIconName" readonly />
<p class="usage-tip">使用方式:</p>
<pre><code><el-icon><{{ copiedIconName }} /></el-icon></code></pre>
</el-dialog>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
import { ElMessage } from 'element-plus'
import {
Search,
Edit,
Delete,
Plus,
Minus,
Star,
Heart,
User,
Setting,
House,
Document,
Folder,
Picture,
VideoCamera,
Headset,
Phone,
Message,
Bell,
Warning,
Check,
Close,
ArrowLeft,
ArrowRight,
ArrowUp,
ArrowDown
} from '@element-plus/icons-vue'
const searchKeyword = ref('')
const activeCategory = ref('common')
const showCopyDialog = ref(false)
const copiedIconName = ref('')
const categories = ref([
{
name: 'common',
label: '常用图标',
icons: ['Edit', 'Delete', 'Plus', 'Minus', 'Search', 'Star', 'Heart']
},
{
name: 'user',
label: '用户相关',
icons: ['User', 'Setting', 'Bell', 'Message']
},
{
name: 'navigation',
label: '导航图标',
icons: ['House', 'ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown']
},
{
name: 'media',
label: '媒体图标',
icons: ['Picture', 'VideoCamera', 'Headset', 'Document', 'Folder']
},
{
name: 'status',
label: '状态图标',
icons: ['Check', 'Close', 'Warning', 'Phone']
}
])
const filteredIcons = (icons) => {
if (!searchKeyword.value) return icons
return icons.filter(icon =>
icon.toLowerCase().includes(searchKeyword.value.toLowerCase())
)
}
const handleCategoryChange = (tab) => {
activeCategory.value = tab.name
}
const copyIconName = async (iconName) => {
try {
await navigator.clipboard.writeText(iconName)
copiedIconName.value = iconName
showCopyDialog.value = true
ElMessage.success('图标名称已复制到剪贴板')
} catch (err) {
ElMessage.error('复制失败,请手动复制')
}
}
</script>
<style>
.icon-gallery {
padding: 24px;
}
.gallery-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
}
.gallery-header h2 {
margin: 0;
color: #303133;
}
.icon-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
gap: 16px;
padding: 16px 0;
}
.icon-item {
display: flex;
flex-direction: column;
align-items: center;
padding: 16px;
border: 1px solid #e4e7ed;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s;
}
.icon-item:hover {
border-color: #409eff;
background-color: #f0f9ff;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.15);
}
.icon-name {
margin-top: 8px;
font-size: 12px;
color: #606266;
text-align: center;
word-break: break-all;
}
.usage-tip {
margin: 16px 0 8px 0;
font-weight: 600;
color: #303133;
}
pre {
background-color: #f5f7fa;
padding: 12px;
border-radius: 4px;
margin: 0;
overflow-x: auto;
}
code {
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
font-size: 14px;
color: #e74c3c;
}
</style>
实践练习
练习1:基础图标使用
创建一个工具栏组件,包含常用的操作图标(编辑、删除、搜索、设置等)。
练习2:图标按钮组合
实现一个操作按钮组,包含不同状态和样式的图标按钮。
练习3:导航菜单
创建一个带图标的侧边栏导航菜单,支持展开收起。
练习4:图标库展示
实现一个完整的图标库展示页面,支持搜索、分类和复制功能。
设计原则
- 语义化使用:选择与功能相符的图标,确保用户能够直观理解
- 一致性:在整个应用中保持图标风格和使用规范的一致性
- 可访问性:为图标添加适当的 aria-label 或 title 属性
- 性能优化:按需导入图标,避免全量引入造成包体积过大
- 响应式设计:确保图标在不同设备上都有良好的显示效果
学习资源
作业
- 基础作业:创建一个包含所有常用图标的展示页面
- 进阶作业:实现一个带搜索功能的图标选择器组件
- 挑战作业:设计一个支持自定义图标上传的图标管理系统
总结
Icon 图标组件是构建现代 Web 界面的重要元素。通过合理使用 Element Plus 提供的图标系统,可以大大提升用户界面的视觉效果和用户体验。掌握图标的安装、导入、使用和自定义方法,对于前端开发者来说是必备技能。
学习日期: ___________ 完成状态: ___________ 学习笔记:
遇到的问题:
解决方案: