十五.vite搭建之优雅的使用icon图标
前言
最近在使用vite
搭建项目,其中就用到了icon
图标,其中用到element-plus
的自动导入图标出了点问题,看过许多文章都是复制粘贴一样,没有解决问题,最后自己通过去看插件说明,终于解决了这个问题,这里记录下解决办法,希望对你有所帮助。
ps:element-plus的icon图标独立出来了,所以如果想要使用,需要单独引入
常规使用
# 选择一个你喜欢的包管理器
# NPM
$ npm install @element-plus/icons-vue
# Yarn
$ yarn add @element-plus/icons-vue
# pnpm
$ pnpm install @element-plus/icons-vue
- 单独使用
# 页面内单独使用
<template>
<Edit/>
</template>
<script setup>
import { Edit } from '@element-plus/icons-vue'
</scirpt
- 全局引入
# 全局使用
# main.ts
# 引入所有图标并注册
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
const app = createApp(App)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
# 组件使用
<template>
<Edit/>
</template>
自动导入
使用自动导入,可以不用安装 element-plus/icons-vue,main.ts也不需要全局注册
# 自动导入需要安装:unplugin-icons,unplugin-vue-components
# pnpm 安装
$ pnpm install unplugin-icons unplugin-vue-components -D
这里先贴配置:
# vite.config.ts 配置
import Components from "unplugin-vue-components/vite";
import Icons from "unplugin-icons/vite";
import IconsResolver from "unplugin-icons/resolver";
export default defineConfig({
plugins:[
Components({
dirs: ['src/components'], //默认components
extensions: ["vue", "md"],
dts: "./src/auto-imports.d.ts", //可设置为false,则不生成
include: [/\.vue$/, /\.vue\?vue/, /\.md$/],
exclude: [/[\\/]node_modules[\\/]/, /[\\/]\.git[\\/]/, /[\\/]\.nuxt[\\/]/],
resolvers: [
IconsResolver({
prefix: 'i', // 默认为i,设置为false则不显示前缀
enabledCollections: ["ep"]
})
]
}),
Icons({
autoInstall: true
})
]
})
使用:
# 错误的写法(包括我开始也是犯这个错) 这样写图标不会出来
<template>
<edit/>
</template>
# 正确的写法:
# 使用方法 i-ep-xxx 后面的就是icon名字,至于为啥要这样后面会详细说
<template>
<i-ep-plus/>
</template>
配置详解
到这里有人可能就有疑问了,为啥前面多了i-ep
这种前缀,不能直接使用呢,回到刚刚的那个配置上去,一步步详解。
export default defineConfig({
plugins:[
Components({
dirs: ['src/components'], //默认components
extensions: ["vue", "md"],
dts: "./src/auto-imports.d.ts", //可设置为false,则不生成
include: [/\.vue$/, /\.vue\?vue/, /\.md$/],
exclude: [/[\\/]node_modules[\\/]/, /[\\/]\.git[\\/]/, /[\\/]\.nuxt[\\/]/],
resolvers: [
IconsResolver({
prefix: 'i', // 默认为i,设置为false则不显示前缀
enabledCollections: ["ep"]
})
]
}),
Icons({
autoInstall: true
})
]
})
首先说下这个Component: 前面的Components
的主要作用就是自动注册dirs
目录下的组件,注意他不是全量注册,是按需注册,你使用了才会注册,然后在你 dts
的指定目录下自动生成 auto-import.d.ts
文件(当前,设置成false就不生成
), IconsResolver
就是主要配置icon
的。
auto-import.d.ts文件:
# 图标结构
# 它由三部分组成:{prefix}-{collection}-{icon}
# prefix:icon的前缀,默认值为'i',可设置成false,如果设置成false,那么组件使用就变成 <ep-edit/>
# collection: iconify 唯一name;
# icon: 图标名字
这里看第二个参数 collection
对应的是 enabledCollections
,这里设置的是['ep']
,默认是iconify
上的所有图标,这里的 iconify 是一个很火的图标库,上面有很多图标,包括element-plus
和ant-design
图标等等。
这里设置成ep
,即element-plus
的缩写,代表的是element-plus
的图标,它会帮你安装@iconify-json/ep
依赖,当然你可以把这个设置成别的,如:mdi
,fa
等等,这样它会加载其他图标库,也都会在package.json
给你安装对应的依赖。
到这里有人又会问了,中间那个为啥是ep
,我既想用el-icon图标,又不想用ep这个名字,可以变吗?告诉你可以!
export default defineConfig({
plugins:[
Components({
dirs: ['src/components'], //默认components
extensions: ["vue", "md"],
dts: "./src/auto-imports.d.ts", //可设置为false,则不生成
include: [/\.vue$/, /\.vue\?vue/, /\.md$/],
exclude: [/[\\/]node_modules[\\/]/, /[\\/]\.git[\\/]/, /[\\/]\.nuxt[\\/]/],
resolvers: [
IconsResolver({
prefix: 'i', // 默认为i,设置为false则不显示前缀
enabledCollections: ["ep"],
alias: {
test: "ep" //配置别名
},
})
]
}),
Icons({
autoInstall: true
})
]
})
如上配置,加了一个alias
别名,注意下,这里的key
就是 你想要变的名字,后面的value
必须要和enabledCollections
保持一致。
# 使用
<template>
<i-test-plus/>
</template>
其次说这个Icons: 他有一些可选的配置项,其中如果autoInstall
为true
,启动项目时他会自动给你安装一个依赖,这个依赖就是对应的icon
图标包,同时会自动生成 auto-imports.d.ts
里面会有这些图标的引入位置。
# vite.config.ts 配置
import Icons from "unplugin-icons/vite";
export default defineConfig({
plugins:[
Icons({
autoInstall:true, // 是否自动注册
scale: 1, // 图标缩放,默认为1
defaultStyle: '', // 图标style
defaultClass: '', // 图标class
compiler: null, // 编译方式,可选值:'vue2', 'vue3', 'jsx'
jsx: '', // jsx风格:'react' or 'preact'
})
]
})
这里是他自动安装@iconify-json/ep
的依赖,初始化的时候autoInstall
设置为false
,如果修改把它变为true
,他就会自动安装。
循环图标存在的问题
某些时候我们可能有这样的需求:需要去遍历图标数组,然后展示多个图标,如果你是像这样写,就会发现图标出不来。因为vue3
中component is
动态组件必须绑定的是组件实例,而不是组件组件名字,这是vue3
一个很恶心的地方,除非你全局注册使用图标或组件。
# index.vue
<template>
<div v-for="icon in iconList" :key="icon.title">
<component :is="icon.name"/>{{icon.title}}
</div>
<el-input prefix-icon="user"/>
</template>
<script setup lang="ts">
import {ref} from "vue"
const iconList=ref([{
title:'张三',
name:'plus'
},{
title:'李四',
name:'minus'
},{
title:'王五',
name:'search'
}])
</script>
有人问过antfu大佬,如何动态引入图标,对此,unplugin-icons
的作者是这样解释的:
解决方法1
不使用自动导入,直接全局注册,如上面第二点一样main.ts
全局注册,直接使用即可
# index.vue
<template>
<div v-for="icon in iconList" :key="icon.title">
<component :is="icon.name"/>{{icon.title}}
</div>
<el-input prefix-icon="user"/>
</template>
<script setup lang="ts">
import {ref} from "vue"
const iconList=ref([{
title:'张三',
name:'plus'
},{
title:'李四',
name:'minus'
},{
title:'王五',
name:'search'
}])
</script>
解决方法2
通过unplugin-auto-import
实现自动加载图标,unplugin-auto-import
这个插件也是vite
项目必备插件,他可以自动帮你引入ref,reactive
等vue内置组件,它会在初始化时在dts
指定位置生成一个components.d.ts
文件。
# 安装 unplugin-auto-import
$ pnpm install -D unplugin-auto-import
# 修改vite.config.ts
import AutoImport from "unplugin-auto-import/vite";
import Components from "unplugin-vue-components/vite";
import { ElementPlusResolver } from "unplugin-vue-components/resolvers";
import Icons from "unplugin-icons/vite";
import IconsResolver from "unplugin-icons/resolver";
export default defineConfig({
plugins: [
vue(),
AutoImport({
dts: resolve("src/components.d.ts"),
imports: ["vue","pinia","vue-router",{"@vueuse/core": []}],
resolvers: [
ElementPlusResolver(),
//主要是加上这个
IconsResolver({
//配置前缀,效果等用于Components中的 prefix
componentPrefix: "",
enabledCollections: ["ep"]
})
]
}),
Components({
extensions: ["vue", "md"],
dts: "./src/auto-imports.d.ts",
include: [/\.vue$/, /\.vue\?vue/, /\.md$/],
exclude: [/[\\/]node_modules[\\/]/, /[\\/]\.git[\\/]/, /[\\/]\.nuxt[\\/]/],
resolvers: [
ElementPlusResolver(),
IconsResolver({
prefix: false, // 默认为i,设置为false则不显示前缀
enabledCollections: ["ep"]
})
]
}),
Icons({
autoInstall: true,
compiler: "vue3"
})
]
});
# index.vue
<template>
<div v-for="icon in iconList" :key="icon.title">
<component :is="icon.name"/>{{icon.title}}
</div>
<el-input :prefix-icon="user" />
</template>
<script setup lang="ts">
const iconList=ref([{
title:'张三',
name:EpPlus --修改,直接写组件名字,这样会自动去导入该组件,无需引入
},{
title:'李四',
name:EpMinus --修改
},{
title:'王五',
name:EpSearch --修改
}])
const user = EpPlus; --有点恶心。。。
</script>
使用自动导入or不使用
总的来说,自动导入能按需帮你自动引入,开箱即用,但是使用起来有点限制,比如这个input
的,想给它加上前缀图标使用起来很难受(也或者是我没找到正确打开方式
),这里大家可以根据自己项目需要,决定是否使用自动导入。
为了使用方便,这里我放弃使用自动导入,直接使用全局注册的方式,理由有几点:
- el-icon的图标本身也体积不是很大,对项目的体量影响不是很大;
- unplugin-icons 无法动态引入;
- 为了后续封装使用方便;
- input的前缀图标等无法有效解决;
- 等等。。。
进阶
由于项目中不可能只使用el-icon,还有可能用到iconfont或者svg图标,所以这里对icon图标进行一层封装,方便之后使用(这里用的全局引入el-icon)
创建base-icon组件
通过elName,svgName,iconName
分别去加载对应的图标,
- elName: element-plus的图标,通过resolveComponent去加载图标。
- iconName: iconfont图标。
- svgName: 加载本地svg图标。
# base-icon.vue
<script lang="ts">
export default {
props: {
size: {
type: [Number, String],
default: 16
},
color: {
type: String
},
elName: {
type: String
},
iconName: {
type: String
},
svgName: {
type: String
}
},
render() {
const { size, color, elName, iconName, svgName } = this;
if (!elName && !iconName && !svgName) return null;
if (elName) {
return h(resolveComponent(elName), { color, width: `${size}px`, height: `${size}px` });
}
const iconStyle = { color, fontSize: `${size}px` };
if (iconName) {
return h("i", {
class: ["iconfont", `icon-${iconName}`],
style: iconStyle
});
}
if (svgName) {
return h(
"svg",
{
class: ["svgClass"],
style: iconStyle,
ariaHidden: true
},
h("use", {
"xlink:href": `#icon-${svgName}`
})
);
}
return null;
}
};
</script>
<style scoped>
.svgClass {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
outline: 0;
}
</style>
这里对svg图标进行特别的说明: 有些项目会涉及到将svg文件下载放到项目内部,所以这里单独对svg进行特殊说明,为了正常使用svg,这里需要再安装一个插件。
# pnpm安装
# pnpm install vite-plugin-svg-icons -D
#vite.config.ts
import { createSvgIconsPlugin } from "vite-plugin-svg-icons";
export default defineConfig({
plugins: [
...之前的插件,
createSvgIconsPlugin({
// 加载svg的目录
iconDirs: [resolve("src/icons")],
// 指定symbolId格式
symbolId: "icon-[name]"
})
]
})
# main.ts
import "virtual:svg-icons-register";
这样使用后,每次svg图标为icon-xxx
的时候,都会去iconDirs
指定的目录内,加载对应的图标。
组件使用方法:
#index.vue
<template>
<base-icon el-name="plus" :size="42" color="#f00"/>
<base-icon icon-name="test"/> // 需要引入iconfont图标
<base-icon svg-name="add"/> // 需要指定目录下有add.svg图标
</template>
最后
到这里vite中使用图标已经完成了,具体需不需要使用自动导入取决于自己系统的业务需求,相信antfu大佬后续会对自动引入图标进行升级,可以期待一手;如果觉得本文对你有帮助,可以点一下赞,如果好的意见可以评论区留言,大家共同进步。
其他文章
转载自:https://juejin.cn/post/7169485227188813860