Vue3!ElementPlus!更加優雅的使用Icon

語言: CN / TW / HK

前言

這可能是目前最便捷、最合適的 Icon 使用方式了,特別是在 Vue3 中,等等,你以為它只是適用於 Vue3?不,不要被標題欺騙,它支持 Vue2/Vue3、React、Preact、Solid、Svelte 等多種主流框架,同樣,它也支持 Vite、Roullp、Webpack、Nuxt、VueCLI、Svelte Kit、Svelte + Vite、Next.js 等多種主流構建工具。之所以標題中帶有 Vue3 以及 ElementPlus 單純是因為此文是以此背景下產生的,示例代碼也是基於此。

假如你並不是此環境也無礙,你依然可以看完此文,因為不同環境只是在配置上稍微有些不同,使用上是完全一致的,當然,最終是否使用取決於你自己的取捨!

寫在前面

最近一年以來,Vue 正式將默認分支改為 Vue3.0,ElementPlus 也正式發版,技術更新的很快,隨之而來的就是生態圈會出現更多好玩的、新的、有創意的工具。

如果有條件,一定要去嘗試更新,一方面隨着框架大版本的迭代特別是使用方式的巨大改變以及周圍生態的大更新會讓你發現新大陸,另一方面像 Vue3 官方已經設置為默認庫,陸續會有更多的用户直接去使用 Vue3,當大家都在使用你所認為的新東西的時候,你沒有使用,那就説明你在這股技術潮流中已經落後了。。

幾年前寫過一篇文章叫 Vue項目中優雅的使用icon,主要介紹了當時項目中主流的幾種圖標使用方式,比如 Img圖標、CSS雪碧圖、字體圖標(Icon Font)、SVG 圖標(SVG Icon),文中着重描述的就是 SVG Icon 並且提供了一種較為優雅的 SVG Icon 使用方式,我們來回顧下這種方式:在開發環境將自定義的 Icon 抽離成一個單獨的模塊,使用 svgLoader 去解析 SVG Icon ,然後再寫一個 Vue 組件 <SvgIcon iconClass="iconName"/> 統一的去加載它,在每次需要使用圖標時就去下載一個 SVG 圖標到 Icon 模塊中,使用組件並傳入 SVG 文件名即可加載圖標,如果你不瞭解請看上文。這種方式估計也是當下大多數同學 Vue2 項目中正在使用的。

那麼可能有人會問,這種方式在 Vue3 中不適用了嗎?

不,它依然適用。那我們為什麼要改呢?

這可就要好好説道説道了。

PS:下文中 ElementUI 指的是適用於 Vue2 的組件庫,ElementPlus 指的是適用於 Vue3 的組件庫!

相信大家有很多同學做項目組件庫是基於 ElementUI,但是在 ElementUI 中內置的圖標庫是字體圖標,很多同學包括我自己為了省事或多或少的會使用一些內置的字體圖標(特別是一些按鈕上的操作圖標),只有迫不得已才會使用自定義的圖標,也就是上面那種 SVG Icon,這樣就導致了一個項目中同時存在字體圖標和SVG圖標兩種使用方式,當然這也不是大問題。

問題是目前 Vue 升級了,現在是 Vue3,我們想要在項目中使用 Vue3,那 ElementUI 就必須升級為 ElementPlus,而 ElementPlus 的升級在使用上和之前相比很多組件是顛覆性的,特別是圖標組件,因為它由字體圖標遷移到了SVG圖標,使用的方式更是大不一樣。

沒有辦法,該升級還得升級,那就改吧,改完了之後問題就出現了,如果我們還使用之前的 <SvgIcon /> 去加載自定義的圖標,那就出現了一種尷尬的情況,同是 SVG 圖標,同一個項目,兩種使用方式。。。

為了項目的統一(好吧,是強迫症!!!),絕對不能容許有這種情況,解決辦法有兩個:

  • 不使用組件庫內置圖標。
  • 將自定義圖標使用方式和組件庫保持一致。

嗯。。恰好發現了 antFu 大佬寫的 unplugin-icons 插件可以做到自定義圖標和組件庫中圖標使用一致並且還能為我們的開發提供很大的便捷(其實我覺得ElementPlus 也是參考了這個),簡直 nice 啊,於是就有了這篇文章。

所以此文我們講的還是使用 SVG Icon,不同的是使用方式變了,也正如標題所説,這種方式更為優雅!!!

嗯?為什麼使用 SVG Icon?

嗯,你還不知道為什麼要使用 SVG Icon

那我們就再簡單的對比一下幾種 Icon 使用方式,當然,Img 和 CSS雪碧圖就不説了,因為現在最常用的都是矢量圖標,所以我們簡單對比下當下最常用的字體圖標和SVG圖標的優缺。

如上,我們可以看到,除了在瀏覽器支持上,SVG圖標是完勝字體圖標的,至於瀏覽器支持嗎,在當下這個 Chrome 內核霸主時代也就沒有了意義。

好了,接下來就開始實戰了!

ElementPlus中使用Icon

先來簡單瞭解一下 ElementPlus 中的 Icon 怎麼用,如果你想使用 ElementPlus 的圖標庫,首先要先安裝官方提供的圖標庫包,因為它並不在 ElementPlus 的包中。

安裝圖標庫

```bash

NPM

$ npm install @element-plus/icons-vue

Yarn

$ yarn add @element-plus/icons-vue

pnpm

$ pnpm install @element-plus/icons-vue ```

使用圖標

ElementPlus 的圖標庫由之前的 Icon Font 遷移了 SVG Icon,使用方式大不一樣,我們只需要將所用到的圖標引入後再將圖標名作為一個 Vue 組件使用即可,如下:

```html

```

另外,ElementPlus 還為我們提供了 ElIcon 組件,用於修改圖標樣式,我們只需要將圖標組件通過插槽的形式傳入進去,然後通過該組件的 colorsize 屬性去修改圖標的顏色和大小,當然,如果有特殊需求你也可以使用 class 屬性和 style 屬性去定義圖標樣式,但一般我們只會去修改顏色和大小就夠了,如下:

```html

```

可以看到,ElementPlus 中將 SVG圖標單獨抽離了出來,對於加載一個 Icon,我們不需要關注修改它的樣式,只是引入加載就好了,然後再由一個統一的組件去修改樣式。

一部分人剛開始使用這種方式會覺得不太適應,當你使用久了就會發現這種方式使用真的超級便捷,因為我們不需要再去寫 CSS Class 類然後再寫樣式去改變圖標,寫一個圖標只需要引入和使用就行了,至於樣式修改,通過 ElIcon 組件兩個屬性就可以解決,有時候我們甚至並不需要傳入屬性,ElIcon 的默認樣式就 OK 了,還能夠保持統一,而在我們自定義圖標時,也不用那麼麻煩再去專門寫一個組件來加載 SVG圖標。

接下來我們來看下如何自定義圖標。

自定義 Icon

自定義圖標就要用到 antFu 大佬寫的 unplugin-icons 插件了,我們首先了解一下此插件是做什麼的。

插件核心是用來做 svg Icon 按需解析並加載的,同時它基於 iconify 圖標庫支持按需訪問上萬種圖標,當然,我們不使用圖標庫也是可以的。

安裝插件

首先我們需要安裝此插件:

bash npm i -D unplugin-icons

使用插件

插件安裝完成後,使用起來也非常簡單,我們以 Vue3 + VueCLI 為例,看看它是如何使用的,在 vue.config.js 文件配置下面內容:

js // vue.config.js // 引入 const Icons = require('unplugin-icons/webpack') module.exports = { configureWebpack: { plugins: [ // 使用 Icons({ compiler: 'vue3' }), ], } }

如果你是 Vue3 + Vite ,只需要在 vite.config.js 中做如下配置即可:

```js // vite.config.js import Icons from 'unplugin-icons/vite'

export default defineConfig({ plugins: [ Icons({ compiler: 'vue3' }), ], }) ```

沒錯,使用就是這麼簡單,當然,如果你是其他環境,也可以按照 GitHub 上的文檔配置。

使用圖標庫

上面説了,插件基於 iconify 圖標庫支持按需訪問上萬種圖標,我們來看如何使用 iconify 圖標庫,先來介紹下此圖標庫的一些概念。

iconify 這個圖標庫能夠作為 unplugin-icons 插件的數據源,是因為它真的很好用,此庫內部有 100+ 個圖標集,每個圖標集中都有成千上萬種圖標,所以,非常非常全。

iconify 庫下的圖標集你可以理解為模塊,每個模塊(圖標集)下才是對應的圖標文件,每個圖標集中的圖標數量也是非常多的。

我們打開此圖標庫的官網:

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e1fb384a015048b98889dfc260ae8c2d~tplv-k3u1fbpfcp-zoom-1.image

如上圖,其中圈紅的就是圖標集了,拿第一個 Google Material Icons 來説,此圖標集下就有 1w+ 圖標。

我們點開這個圖標集,選中一個圖標:

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3922cf793e754c3fa54ff01a0c9c8cd9~tplv-k3u1fbpfcp-zoom-1.image

我們選中了 5G 這個圖標,如上圖圈紅的位置,此圖標的完整名字即 ic:baseline-5g,其中 ic 是圖標集的名字,baseline-5g 則是圖標名字,這點我們要先知道。

瞭解了 iconify 圖標庫的相關知識,接下來我們就配合插件使用它吧,既然要使用,那肯定得安裝。unplugin-icons 插件支持三種安裝此圖標庫的方式,我們依次來看。

手動安裝圖標庫

第一種是手動安裝圖標庫,如名,直接安裝 iconify 整個庫,這個庫大約在 120MB 左右大小,當然你不需要擔心,在生產環境只會打包你所使用到的圖標。

bash npm i -D @iconify/json

手動安裝圖標集

雖然插件在生產環境只會打包你所使用到的圖標,但 120MB 安裝下來還是太慢了,所以,第二種安裝方式即手動安裝圖標集合,因為圖標太多了,有時候我們只需要用到一個圖標集的 Icon 就夠了,這時我們就可以只安裝使用到的圖標集。

bash npm i -D @iconify-json/xxx

如上命令,我們只需要選中要使用的圖標集,將圖標集的名字替換 xxx 即可,而圖標集的名字你可以通過上面我們説的那種方式在圖標庫官網查看。

自動安裝圖標集

安裝圖標集也挺累的?沒關係,插件還支持自動安裝圖標集,非常簡單,只需要在配置裏添加一行代碼即可。

js // vue.config.js const Icons = require('unplugin-icons/webpack') module.exports = { configureWebpack: { plugins: [ Icons({ compiler: 'vue3', // 自動安裝 autoInstall: true }), ], } }

如上,只要我們在配置中傳入 autoInstall 屬性為 true 就 ok 了,插件就會在檢測到我們引入一個圖標時自動去下載該圖標集,這樣就省事多了。

不用想,大家應該都會使用自動安裝這種方式。

使用圖標庫

我們再來看看項目中如何使用一個圖標庫中的圖標。拿上面的 5G 圖標為例,它的圖標集是 ic,圖標名叫 baseline-5g ,使用如下:

```html

```

沒錯就是這麼簡單,如果我們配置了自動安裝,在寫完上面代碼保存時,插件會檢測到並且通過包管理工具自動安裝 ic 圖標集了。

我們發現這種 Icon 使用方式和 ElementPlus 簡直一模一樣,那是不是也可以使用 ElementPlusElIcon 組件呢?

答案是當然可以:

```html

```

嗯,總算是有點意思了。

自定義SVG圖標

最後就是我們自定義的圖標了,雖然 iconify 提供了超級多的圖標庫,ElementPlus 裏也有一些,但有時候 UI 設計師總是不按套路出牌,非得自己設計,,,當然,這也沒有問題,你可以直接讓設計師導出一個 SVG 文件給到你。

還是以 Vue3 + VueCLI 為例子,我們先來做一些準備,首先在 src/assets 目錄下新增 svg/ 目錄(按自己喜好定義),我們可以按照模塊在 svg/ 下建不同的文件夾,這些文件夾用來存放我們自定義的 SVG 圖標文件。

bash src/assets/svg/home/jihua.svg src/assets/svg/about/kefu.svg

上面是我建的兩個圖標,接下來我們來配置自定義圖標的加載。

unplugin-icons 插件中有一個 customCollections 屬性,用來做自定義圖標的加載,但是由於我們需要引入 SVG 文件,所以還需要一個 SVG 文件解析的 loader ,這點插件也為我們考慮到了,unplugin-icons 插件包下就有這個 loader,我們直接引入使用即可,如下:

js // vue.config.js const Icons = require('unplugin-icons/webpack') // 引入loader const { FileSystemIconLoader } = require('unplugin-icons/loaders') module.exports = { configureWebpack: { plugins: [ Icons({ compiler: 'vue3', autoInstall: true, // 自定義圖標加載 customCollections: { // home圖標集 // 給svg文件設置fill="currentColor"屬性,使圖標的顏色具有適應性 home: FileSystemIconLoader('src/assets/svg/home', svg => svg.replace(/^<svg /, '<svg fill="currentColor" ')), // about圖標集 about: FileSystemIconLoader('src/assets/svg/about', svg => svg.replace(/^<svg /, '<svg fill="currentColor" ')), }, }), ], } }

如上,我們把 svg/ 目錄下的每個文件夾作為一個模塊,其下文件使用 loader 解析即可,然後就可以快樂的使用了:

```html

```

到此,整個項目的圖標就只有一種使用方式了,隨用隨引即可,nice!

等等,你以為這就完了?No!

用的時候還需要引入,這確實也挺麻煩的。。有沒有辦法自動引入,而我們直接想再哪裏使用就在哪裏使用呢?

當然可以,我們接着看。

自動引入

我們想要做到自動引入,還需要一個插件的配合,同樣也是 antFu 大佬寫的 unplugin-vue-components 插件。

不得不説 antFu 真是 Vue3 生態輪子一哥啊!!!

該插件的作用是給 Vue 提供自動引入組件的功能,它支持 Vue2 和 Vue3 的開箱即用,同時支持組件和指令,也支持 Vite、Webpack、VueCLI、Rollup、ESbuild。最重要的是,它和 unplugin-icons 完美契合。

安裝插件

首先是安裝:

bash npm i unplugin-vue-components -D

使用插件

接下來是使用,還是沿用 Vue3 + VueCLI 的環境,其他環境自行查看文檔,其實就是換個環境的包引入即可。

先來使用插件,如下配置:

js // vue.config.js const Icons = require('unplugin-icons/webpack') const { FileSystemIconLoader } = require('unplugin-icons/loaders') // 引入自動引入插件 const Components = require('unplugin-vue-components/webpack') module.exports = { configureWebpack: { plugins: [ // 使用自動引入插件 Components({ /** options **/ }), Icons({ compiler: 'vue3', autoInstall: true, customCollections: { home: FileSystemIconLoader('src/assets/svg/home', svg => svg.replace(/^<svg /, '<svg fill="currentColor" ')), about: FileSystemIconLoader('src/assets/svg/about', svg => svg.replace(/^<svg /, '<svg fill="currentColor" ')), }, }), ], } }

如上,在我們使用插件之後,再使用項目中我們自定義的組件就無需引入了。

寫個例子,比如我們在 src/ 目錄下的任意位置寫一個 HelloWord 組件,然後找個地方直接使用它:

```html

```

如上,像往常一樣在一個頁面組件中使用該組件,它將按需導入組件,不再需要導入和組件註冊。插件會在編譯時自動將上面代碼解析成下面這樣:

```html

```

當然,這僅限於我們自己在項目中寫的組件(異步註冊父組件或延遲路由的情況下同樣支持,自動導入的組件將與其父組件一起進行代碼拆分)。

項目中組件自動引入的解析是插件中內置的,但是對於上文中的 Icon 組件,由於是外部引入的組件,我們就需要配置它獨有的解析器了,unplugin-icons 插件提供給我們一個 unplugin-icons/resolver 包,這個包就是用來處理 Icon 相關的自動引入解析的,我們叫它 Icon自動引入解析器,其實實現起來也是非常簡單的,具體等看了下面自定義自動引入就明白了。

隨後我們配置如下:

js // vue.config.js const Icons = require('unplugin-icons/webpack') const { FileSystemIconLoader } = require('unplugin-icons/loaders') // 引入 Icon自動引入解析器 const IconsResolver = require('unplugin-icons/resolver') // 引入自動引入插件 const Components = require('unplugin-vue-components/webpack') module.exports = { configureWebpack: { plugins: [ // 使用自動引入插件 Components({ // 配置解析器 resolvers: [ // Icon自動引入解析器 IconsResolver({ // 自動引入的Icon組件統一前綴,默認為 i,設置false為不需要前綴 prefix: 'icon', // 當圖標集名字過長時,可使用集合別名 alias: { system: 'system-uicons' } }) ] }), Icons({ compiler: 'vue3', autoInstall: true, customCollections: { home: FileSystemIconLoader('src/assets/svg/home', svg => svg.replace(/^<svg /, '<svg fill="currentColor" ')), about: FileSystemIconLoader('src/assets/svg/about', svg => svg.replace(/^<svg /, '<svg fill="currentColor" ')), }, }), ], } }

OK,啟動項目,我們就可以直接使用圖標組件無需引入了,注意,使用圖標組件解析器時,必須遵循名稱轉換才能正確推斷圖標,也就是説想要自動引入 Icon 組件,我們必須按照下面格式書寫組件名:

```js // prefix - 前綴,默認為 i,上面我們配置成了 icon,即組件名以 icon 開頭 // collection - 圖標集名 // icon - 圖標名 {prefix}-{collection}-{icon}

// 當然大駝峯也可以,下面是用的就是大駝峯,因為看着順眼 ```

我們先來看看沒有自動引入時:

```html

```

再來看看有自動引入時(注意,iconifysystem-uicons 圖標集我們上面配置了別名為 system,下面加載了這個圖標集中的 bell 圖標):

```html

```

是不是很簡潔!!!

自定義圖標自動引入

如果你在看文章的同時按照上面配置寫了 demo,那一定可以發現我們自定義的兩個圖標在做了自動引入時並沒有加載出來。。

這是因為自定義的圖標集想要自動引入,需要在 Icon 自動引入解析器( IconsResolver )的配置中使用 customCollections 屬性標記出自定義的圖標集模塊名。

我們上面使用了兩個自定義圖標集,所以將兩個自定義圖標集的名字傳入進去,讓自動引入插件可以識別並解析即可:

js // vue.config.js const Icons = require('unplugin-icons/webpack') const { FileSystemIconLoader } = require('unplugin-icons/loaders') const IconsResolver = require('unplugin-icons/resolver') const Components = require('unplugin-vue-components/webpack') module.exports = { configureWebpack: { plugins: [ Components({ resolvers: [ IconsResolver({ prefix: 'icon', alias: { system: 'system-uicons' }, // 標識自定義圖標集 customCollections: ['home', 'about'] }) ] }), Icons({ compiler: 'vue3', autoInstall: true, customCollections: { home: FileSystemIconLoader('src/assets/svg/home', svg => svg.replace(/^<svg /, '<svg fill="currentColor" ')), about: FileSystemIconLoader('src/assets/svg/about', svg => svg.replace(/^<svg /, '<svg fill="currentColor" ')), }, }), ], } }

如上,再次運行項目,就可以看到四個圖標已經完整的顯示出來了!

自動引入的一些其他用處

既然自動引入插件都有了,我們還需要手動按需引入組件庫嗎???

比如在使用 ElementPlus 的時候,大家都知道,他有兩種使用方式:

  • 全局引入
  • 按需引入

一般我們會選擇按需引入,但是按需引入就需要我們每次使用時都要單獨引入對應的組件,很麻煩。那麼,藉助自動引入插件,這件事將變得極為簡單。

其實,自動引入插件直接內置了幾個解析器,用於 Vuetify、Ant Design Vue、ElementPlus 等流行的 UI 庫,我們甚至都不需要手寫解析器,就可以無縫使用了。

還是拿 ElementPlus 為例:

js // vue.config.js const Icons = require('unplugin-icons/webpack') const { FileSystemIconLoader } = require('unplugin-icons/loaders') const IconsResolver = require('unplugin-icons/resolver') const Components = require('unplugin-vue-components/webpack') // 引入 ElementPlus 自動引入解析器 const { ElementPlusResolver } = require('unplugin-vue-components/resolvers') module.exports = { configureWebpack: { plugins: [ Components({ resolvers: [ IconsResolver({ prefix: 'icon', alias: { system: 'system-uicons' }, customCollections: ['home', 'about'] }), // 使用 ElementPlus 自動引入解析器 ElementPlusResolver(), ] }), Icons({ compiler: 'vue3', autoInstall: true, customCollections: { home: FileSystemIconLoader('src/assets/svg/home', svg => svg.replace(/^<svg /, '<svg fill="currentColor" ')), about: FileSystemIconLoader('src/assets/svg/about', svg => svg.replace(/^<svg /, '<svg fill="currentColor" ')), }, }), ], } }

我們寫個示例代碼:

```html

```

如上,是可以完美加載的,不需要全局引入組件庫,也不需要按需引入,想用的話直接寫組件即可!

自定義自動引入解析器的場景

假如公司內部有自己的組件庫,要怎麼做自動引入呢?

由於內部組件庫也是外部引入的組件,同時沒有官網為我們寫的解析器,這就需要我們自己手寫一個解析器了!!!

也非常簡單,例如我司組件庫名為 xx-ui,組件前綴統一是 xx,項目中使用了 xx-button 組件。

首先我們安裝組件庫 xx-ui

bash npm install xx-ui

我們來配置此組件庫的自動引入解析器:

js Components({ resolvers: [ // ... 其他解析器 // 自定義xx-ui解析器 (name) => { // name為項目編譯時加載到的自定義組件,String類型大駝峯格式的組件名 // 例如:name = XxButton // 判斷組件前綴 if (name.startsWith('Xx')){ return { // 包名,XxButton -> Button importName: name.slice(2), // 路徑直接寫包名即可,因為我們已經安裝了這個包 path: 'xx-ui' } } } ] })

這樣在使用時:

```html