十五.vite搭建之优雅的使用icon图标

·  阅读 785
十五.vite搭建之优雅的使用icon图标

本文正在参加「金石计划 . 瓜分6万现金大奖」

前言

最近在使用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文件: image.png

# 图标结构
# 它由三部分组成:{prefix}-{collection}-{icon}
# prefix:icon的前缀,默认值为'i',可设置成false,如果设置成false,那么组件使用就变成 <ep-edit/>
# collection: iconify 唯一name;
# icon: 图标名字
复制代码

这里看第二个参数 collection对应的是 enabledCollections,这里设置的是['ep'],默认是iconify上的所有图标,这里的 iconify 是一个很火的图标库,上面有很多图标,包括element-plusant-design图标等等。

image.png

这里设置成ep,即element-plus的缩写,代表的是element-plus的图标,它会帮你安装@iconify-json/ep依赖,当然你可以把这个设置成别的,如:mdi,fa等等,这样它会加载其他图标库,也都会在package.json给你安装对应的依赖。

image.png

到这里有人又会问了,中间那个为啥是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: 他有一些可选的配置项,其中如果autoInstalltrue,启动项目时他会自动给你安装一个依赖,这个依赖就是对应的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,他就会自动安装。 image.png

循环图标存在的问题

某些时候我们可能有这样的需求:需要去遍历图标数组,然后展示多个图标,如果你是像这样写,就会发现图标出不来。因为vue3component 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的作者是这样解释的: image.png

解决方法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文件。

image.png

# 安装 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大佬后续会对自动引入图标进行升级,可以期待一手;如果觉得本文对你有帮助,可以点一下赞,如果好的意见可以评论区留言,大家共同进步。

其他文章

收藏成功!
已添加到「」, 点击更改