vue引入iconfont圖示庫的優雅實戰記錄
前言
本文撰寫的初衷是為了向組內成員推行使用svg sprites的方式管理專案的圖示,由於實際工作中很多專案仍然採用font class的方式,這樣不自覺帶來一個痛點.
當專案一期開發完畢後,過段時間進入到專案二期。新增的開發需求不可避免的會增加新的圖示,而font class需要全量打包圖示的字型檔案.
哪怕新需求只添加了一個圖示,而前端同學卻要將舊圖示和新圖示融合後重新打包生成一次字型檔案,這樣的結果讓人無法接受.
svg sprites能完美的解決這一問題.整體思路是先將專案中每一個圖示都生成一個svg檔案與之對應,那麼有多少個svg檔案就相當於對應了多少個圖示.
以後如果想新增一個圖示,那麼只需要新增一個新svg檔案即可.那些已經存在的圖示和svg檔案則不需要再參與進來.
生成SVG
svg sprites簡介
svg sp程式設計客棧rites這項技術很早就出來了,具體詳情可以點選檢視張鑫旭在2014年寫的文章未來必熱:SVG Sprites技術介紹.
我們這裡做一下簡單介紹就進入實踐階段.svg sprites主要基於兩個標籤元素:<symbol>和<use>.
<symbol>對元素進行分組,它不會顯示在介面上,相當於定義一個模板.<use>元素用於引用並渲染圖示.
例如存在以下某個svg圖示(程式碼如下),它是一個愛心的形狀.
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="24px" height="24px" viewBox="0 0 24 24"> <path fill="#E86C60" d="M17,0c-1.9,0-3.7,0.8-5,2.1C10.7,0.8,8.9,7,0C3.1,3.1,7c0,6.4,10.9,15.4,11.4,15.8 c0.2,0.2,0.4,0.6,0.2s0.4-0.1,0.6-0.2C13.1,22.4,24,13.4,7C24,20.9,17,0z"></path> </svg>
現在使用symbol標籤將上面的path內容包裹一層,程式碼如下:
<svg> <symbol viewBox="0 0 24 24" id="heart"> <path fill="#E86C60" d="M17,0z"></path> </symbol> </svg>
接下來把symbol包裹後的程式碼放入頁面中(程式碼如下),再新增一個display: none隱藏起來.這就相當於在頁面上註冊了一個id名為heart的圖示.
此時頁面的其他部分就可以引用這個圖示,引用方式是在svg標籤裡面放入一個use標籤,use標籤的xlink:href填上要引用的圖示id,介面就會渲染出愛心的形狀.
<body> <svg style="display: none;"> <symbol viewBox="0 0 24 24" id="heart"> <path fill="#E86C60" d="M17,0z"></path> </symbol> </svg> <svg> <use xlink:href="#heart" rel="external nofollow" /> <!-- 使用圖示 --> </svg> </body>
獲取專案圖示
前端同學拿到設計圖之後,通常會整體瀏覽一遍整個專案需要用到的所有圖示.
iconfont是阿里巴巴體驗團隊傾力打造的向量圖示庫,裡邊包含海量的圖示可供前端工程師選擇和使用.
瀏覽器點選開啟 iconfont官網,選擇好自己的專案中要用到的圖示,滑鼠移動到圖示上點選新增入庫.
圖示收集完後點擊頭部右側導航欄的購物車,出現彈出框,點選新增至專案.所有圖示確定新增到專案後,頁面會自動跳轉到導航欄資源管理--我的專案下的頁面(如下圖).
我們的目標是為了生成圖示對應的svg檔案,這裡要做一下設定.開啟上圖中的專案設定選項,彈框開啟後如下圖.
彈框字型格式的那一欄,只保留SVG勾選項,其他都取消,設定好後點擊儲存按鈕.
頁面此時會重新整理一遍,然後點選頁面上的下載至本地的按鈕,將所有圖示的svg檔案下載下來並解壓,解壓後的檔案結構如下圖.
觀察上圖中的檔案結構,我們發現所有svg圖示的程式碼全部都寫在iconfont.svg這一個檔案,這並不符合預期.我們希望的結果是一個圖示對應一個svg檔案,而不是像現在一樣全部揉進了一個檔案內.
雖然iconfont目前沒有提供檔案分離的機制,但是我們可以藉助其他平臺幫我們將融合的svg檔案分離成單個檔案.
iconmoon 網站便具備這個功能,它也是一家和iconfont類似的圖示庫網站.
瀏覽器點選開啟iconmoon官網,選擇頂部導航欄右側的IcoMoon App進入圖示選擇頁面,點選頁面頭部導航欄左側的Imports Icons,將從iconfont下載的iconfont.svg檔案匯入,結果如下圖.
在iconfont那一欄可以看到我們導進去的圖示展現到了頁面上,接下來使用滑鼠單擊導進去的圖示將其標記為選中,再點選頁面左下角的Generate SVG & More按鈕(如下圖).
按鈕點選後頁面跳轉,此時依舊點選左下角的Download按鈕(如下圖)下載圖示.
下載完成後解壓目錄,解壓後的目錄下出現了SVG資料夾,開啟該資料夾會發現所有圖示都被分離成了單個檔案(如下圖).
專案設定
svg檔案順利獲取到了,現在在vue3專案目錄結構src -> assets資料夾下新建資料夾fonts和子資料夾fonts/svg,將上面生成的所有svg單檔案扔到fonts/svg下面.
檔案的設定完成,現在開始專案的配置,讓vue3能順利的管理和使用圖示.
- 第一步在專案根目錄下開啟命令列執行npm i svg-sprite-loader -D.我們之所以要安裝依賴svg-sprite-loader,因為它能將svg檔案的程式碼自動塞到一個個symbol標籤中.
- 第二步專案根目錄下新建檔案vue.config.js,熟悉vue的同學應該知道vue.config.js用來配置構建環境.
vue.config.js詳細配置引數可點選查詢 vue-cli官網,我們這裡只需要知道如何配置svg-sprite-loader就可以.
眾所周知,vue-cli的構建環境基於webpack,我們通過在vue.config.js檔案中新增各類配置引數,vue-cli最終會將這些引數合併到webpack的配置裡.
如此一來我們通過vue.config.js就能達到配置開發環境的目的,而不用直接去操作webpack的配置檔案.
當前已經安裝了依賴svg-sprite-loader,現在要把這個loader載入到webpack的配置中,通過在vue.config.js填寫下面程式碼便可實現.
const resolve = require("path").resolve; module.exports = { chainWebpack(config){ //引入圖示 config.module.rule("svg").exclude.add(resolve("./src/assets/fonts/svg")); config.module.rule("icon").test(/\.svg$/) .include.add(resolve("./src/assets/fonts/svg")).end() .use("svg-sprite-loader") .loader("svg-sprite-loader") .options({ symbolId:'icon-[name]' }); } }
系統學習過webpack配置的同學很容易能看出來上面程式碼的含義,上方程式碼首先將rule中設定的svg規則排除"./src/assets/fonts/svg"目錄.
然後新增加一條規則icon將"./src/assets/fonts/svg"目錄包含了進去,這個目錄就是我們存放所有svg檔案的資料夾.
程式碼接下來使用.use和.loader將svg-sprite-loader配置到專案環境裡,並設定symbolId為icon-[name].
這裡的symbolId關乎到<symbol>標籤生成的id名稱.如果設定symbolId為icon-[name],那麼最後頁面上<use>標籤引用圖示時就會使用icon-加上檔名.
- 第三步在assets/fonts下面新建檔案index.js(檔案結構如下圖),並填寫下面兩行程式碼.
這兩行程式碼主要使用了webpack中的 require.context函式,它可以幫助我們自動引入檔案模組.
require.context第一個引數代表目標檔案目錄,第二個引數是否應用於子資料夾,第三個引數匹配檔案格式.
const load = require.context("./svg",false,/\.svg$/); load.keys().map(load);
require.context執行完畢後返回結果load,返回值load本身就是一個引入模組的函式,另外它還包含一個keys屬性,執行load.keys()返回結果如下.
["./arrow.svg","./arrowon.svg","./downarrow.svg","./jiantou.svg","./trash.svg","./yiwenicon.svg"]
我們從這裡可以看出load.keys()會返回fonts/svg資料夾下所有圖示的相對路徑,再使用loda函式去載入這些路徑的檔案,這樣便實現了動態引入fonts/svg資料夾下的所有以.svg結尾的檔案.
那麼以後如果出現新增一個圖示的需求,先在iconfont網站上下載單個svg檔案,下載完成後直接丟到fonts/svg資料夾下就可以完成自動引入了.
最後一步在專案的入口檔案main.js呼叫第三步新建的index.js,執行所有svg檔案的自動引入(程式碼如下).
import { createApp } from 'vue'; import App from './App.vue'; // 根元件 import "@/assets/fonts/index"; // 執行自動引入 import router from '@/router/index'; // 路由 createApp(App).use(router).mount('#app');
通過以上四步基本完成了專案的配置,整個執行過程可以做一下簡單的梳理.
入口檔案main.js啟動後,執行assets/fonts/index.js啟動所有svg檔案的自動引入.
svg-sprite-loader一旦監聽到專案中引入了以.svg結尾的檔案,它就會把這些svg的程式碼內容全部都封裝到一個個<symbol>標籤裡面(如下圖),再一起插入到頁面文件中.
這將相當於svg-sprite-loader幫助我們將所有的svg圖示在頁面上註冊了,而我們剩下的事情就是在頁面上去引用圖示就行了.
圖示引用
我們在Home主頁下填寫如下程式碼(效果圖如下).將#icon-字首加上fonts/svg下對應的檔名拼接而成的字串賦予xlink:href屬性,那麼就會渲染出該檔案對應的程式設計客棧圖示.
<template> <div class="home"> <p class="title">Hello world</p> <svg> <use xlink:href="#icon-trash" rel="external nofollow" /> <!-- 使用圖示 --> </svg> </div> </template>
元件引用
頁面上使用svg、use標籤引用圖示的方式不太優雅,我們可以將它改造成元件.
在全域性元件資料夾components下新建檔案Icon/index.vue.該元件接受兩個引數name和color(程式碼如下).
引數name對應要渲染的圖示名稱,color為需要渲染的顏色.這裡需要格外注意,svg的顏色修改只能通過fill屬性,color屬性賦值時不奏效.
<template> <svg :style="{fill:color?color:''}"> <use :xlink:href="'#icon-'+name" rel="external nofollow" rel="external nofollow" /> </svg> </template> <script> export default { props:{ name:String,//圖示名稱 color:{ // 圖示顏色 type:String,deafult:null } } } </script>
現在在Home頁面引用Icon元件(程式碼如下).
渲染的圖示名稱為trash,顏色為藍色(效果圖如下).
<template> <div class="home"> <p class="title">Hello world</p> <Icon name="trash" color="blue"/><!-- 使用圖示 --> </div> </template> <script> import Icon from "@/components/Icon/index"; export default { components:{ Icon } } </script>
多主題支援
通過上文講解可知,當我們給<svg>標籤賦予樣式屬http://www.cppcns.com性fill,最終圖示的顏色也會發生改變,這就為我們完成多主題的開發需求提供了可能.
我們接下來搭建一個點選按鈕線上切換主題的場景,讓svg圖示也能隨著主題的變換而改變.
配置多主題樣式
首選在專案資料夾src/assets下新建檔案scss/variable.scss,程式碼內容如下.
程式碼定義了三個主題,分別為預設主題、主題1和主題2.每個主題都定義了自己主題下的圖示顏色和背景顏色.
程式碼下半部分定義了3個mixin,分別用來設定fill、color和background-color屬性.在每一個mixin裡面,不同主題設定的顏色採用自己主題下的顏色設定.
// 預設主題 $icon-color:red; $background-color:#fff; // 主題1 $icon-color1:gray; $background-color1:#eee; // 主題2 $icon-color2:blue; $background-color2:#999; // 用於給svg填充顏色 @mixin fill { fill:$i程式設計客棧con-color; //預設顏色用預設主題 [data-theme = "theme1"] & { //切換到主題1時的顏色 fill:$icon-color1; } [data-theme = "theme2"] & { //切換到主題2時的顏色 fill:$icon-color2; } } //設定color屬性 @mixin color { color:$icon-color; //預設顏色用預設主題 [data-theme = "theme1"] & { //切換到主題1時的顏色 color:$icon-color1; } [data-theme = "theme2"] & { //切換到主題2時的顏色 color:$icon-color2; } } //設定背景顏色 @mixin backgroudColor { background-color:$background-color; //預設顏色用預設主題 [data-theme = "theme1"] & { //切換到主題1時的顏色 background-color:$background-color1; } [data-theme = "theme2"] & { //切換到主題2時的顏色 background-color:$background-color2; } }
variable.scss是一份全域性多主題配置檔案,該檔案內不光可以配置各個主題下應該渲染的顏色,還可以配置字型大小,常用寬高等.
配置檔案編寫完成後,現在要將這份檔案引用到專案當中.編輯器開啟根目錄下vue.config.js專案配置檔案,新增程式碼如下.
const resolve = require("path").resolve; module.exports = { chainWebpack(config){ //引入圖示 config.module.rule("svg").exclude.add(resolve("./src/assets/fonts/svg")); config.module.rule("icon").test(/\.svg$/) .include.add(resolve("./src/assets/fonts/svg")).end() .use("svg-sprite-loader") .loader("svg-sprite-loader") .options({ symbolId:'icon-[name]' }); },css: { loaderOptions: { scss: { prependData: `@import "@/assets/scss/variable.scss";` },} } }
在module.exports新增配置屬性css,隨後將我們在上面編寫多主題配置檔案的路徑填入到prependData對應的值.
這裡為了避免因為sass版本的不同導致檔案引入失敗,統一一下sass和sass-loader的版本.
"sass": "1.26.5","sass-loader": "8.0.2",
vue.config.js配置完成後重啟應用,variable.scss已經全域性注入了應用.接下來我們在頁面元件內不需要使用@import匯入主題配置檔案,variable.scss裡面定義的變數和mixin可以直接拿來使用.
Icon改造
為了讓圖示響應主題的變換,全域性的Icon元件做如下程式碼修改.color屬性如果有傳值,那麼圖示就按照傳入的顏色渲染.如果沒有傳color,那麼決定圖示顏色的因素變成了類名icon.
<template> <svg class="icon" :style="{fill:color?color:''}"> <use :xlink:href="'#icon-'+name" rel="external nofollow" rel="external nofollow" /> </svg> </template> <script> export default { props:{ name:String,color:{ type:String,deafult:null } } } </script> <style lang="scss" scoped> .icon{ @include fill; } </style>
類名 icon 裡面呼叫 fill對應的mixin,檔案variable.scss對fill的定義如下面程式碼.
它最後返回一個屬性 fill:color.預設情況下fill的顏色值為$icon-color.
當頁面文件html標籤上的屬性data-theme值變成theme1時,fill渲染的顏色變成了主題1定義的顏色.同理切換到theme2,fill渲染的顏色變成了主題2定義的顏色.
// 用於給svg填充顏色 @mixin fill { fill:$icon-color; //預設顏色用預設主題 [data-theme = "theme1"] & { //切換到主題1時的顏色 fill:$icon-color1; } [data-theme = "theme2"] & { //切換到主題2時的顏色 fill:$icon-color2; } }
我們觀察一下頁面最終生成的dom結構就可以理解上面配置的目的,@mixin最終會將每個主題下的樣式都生成了一份(如下圖),這樣一來只要<html>標籤的data-theme等於哪個主題,對應主題的樣式表就會生效.
頁面校驗
Home頁面元件填寫如下程式碼.在原來頁面基礎上新增了三個按鈕預設主題、主題1和主題2.
點選按鈕觸發updateTheme函式,函式會修改<html>標籤上data-theme的屬性值,從而實現了主題切換的功能(效果圖如下).
<template> <div class="home"> <p class="title">Hello world</p> <Icon name="trash"/><!-- 使用圖示 --> <button @click="updateTheme()">預設主題</button> <button @click="updateTheme('theme1')">主題1</button> <button @click="updateTheme('theme2')">主題2</button> </div> </template> <script> import Icon from "@/components/Icon/index"; export default { components:{ Icon },methods: { updateTheme(name){ if(name == null){ // 採用預設主題 document.documentElement.removeAttribute("data-theme"); }else{ document.documentElement.setAttribute("data-theme",name); } } },} </script> <style scoped lang="scss"> .home{ height: 100%; @include backgroudColor; } .title{ @include color; } </st程式設計客棧yle>
最終效果圖:
尾言
上文介紹的多主題實現方案操作起來非常簡單,但不適合用於大型複雜的專案.
試想一下,如果一個大型專案包含十幾種主題,而每一類主題下的css程式碼十分龐大,一次性將所有主題下的樣式程式碼全部注入到應用裡是不合適的.
最佳實踐應該是使用者點選切換某一類主題時,就按需載入那一類主題的樣式,再注入到應用中渲染,這樣可以極大的提升整體效能.最佳實踐方式可以參考社群內關於多主題切換的文章.
到此這篇關於vue引入iconfont圖示庫的文章就介紹到這了,更多相關vue引入iconfont內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!
原始碼