如何在Vue.JS中使用圖標(biāo)組件
原文鏈接:https://gist.github.com/Justineo/fb2ebe773009df80e80d625132350e30
本文對(duì)原文進(jìn)行一次翻譯,并從React開(kāi)發(fā)者的角度簡(jiǎn)單地做了一些解讀。
此文不包含字體圖標(biāo)和SVG sprite。僅在此討論允許用戶(hù)按需導(dǎo)入的圖標(biāo)系統(tǒng)。
There are three major ways of exposing API of an icon component in Vue.js and each one of them has its own pros & cons:
在Vue.js的生態(tài)里,有3種主流的API形態(tài),它們有各自的優(yōu)缺點(diǎn):
1.使用單一的組件(如<v-icon>),讓乃通過(guò)name或者type屬性來(lái)指定真正的圖標(biāo)。
圖標(biāo)的數(shù)據(jù)通過(guò)一個(gè)全局的“池子”來(lái)注冊(cè)。
// v-icon/flag.js
import Icon from 'v-icon'
import { mdiFlag } from '@mdi/js'
Icon.add('flag', mdiFlag)
然后這樣子使用:
<template>
<v-icon name="flag" />
</template>
<script>
import VIcon from 'v-icon'
import 'v-icon/flag'
export default {
components: {
VIcon
}
}
</script>
在我維護(hù)的VueAwesome(內(nèi)置了FontAwesome圖標(biāo)的組件庫(kù))中用了這個(gè)方案,同時(shí)我認(rèn)為這是當(dāng)前最符合人機(jī)工程學(xué)的形式。不過(guò)圖標(biāo)的name屬性和那些純副作用的模塊的導(dǎo)入之間的關(guān)系比較隱式,圖標(biāo)的數(shù)據(jù)也在全局注冊(cè)。如果你有多個(gè)不同版本的v-icon,就可能出現(xiàn)問(wèn)題。
FontAwesome官方的Vue.js組件用了一個(gè)稍微不同的方案,它們讓用戶(hù)自己主動(dòng)把圖標(biāo)加到全局的池子中(也可能我不應(yīng)該把這個(gè)方式歸類(lèi)到這個(gè)方案中):
import { library } from '@fortawesome/fontawesome-svg-core'
import { faUserSecret } from '@fortawesome/free-solid-svg-icons'
library.add(faUserSecret)
2.用一個(gè)單一的維護(hù)(如<v-icon),用戶(hù)通過(guò)data或content之類(lèi)的屬性創(chuàng)建真正的圖標(biāo)。
用戶(hù)主動(dòng)把圖標(biāo)的數(shù)據(jù)傳遞給組件:
<template>
<v-icon :content="mdiFlag" />
</template>
<script>
import VIcon from 'v-icon'
import { mdiFlag } from '@mdi/js'
export default {
components: {
VIcon
},
created() {
Object.assign(this, {
mdiFlag
})
}
}
</script>
這是Vuetify支持的方式(Vuetify通過(guò)這種方式支持多種圖標(biāo)的使用方式),這種試在人機(jī)工程和直觀(guān)性上有些損失,但沒(méi)有方案1的缺點(diǎn)。
3.每個(gè)組件代表不同的圖標(biāo)(如<icon-flag />、<icon-star />等)。
這個(gè)方案里,每個(gè)組件通過(guò)一個(gè)圖標(biāo)工廠(chǎng)創(chuàng)造出來(lái):
// icon-flag.js
import { mdiFlag } from '@mdi/js'
import { createIcon } from 'v-icon'
export default createIcon('flag', mdiFlag)
并通過(guò)這種方式使用:
<template>
<icon-flag />
</template>
<script>
import { IconFlag } from 'v-icon'
export default {
components: {
VIcon,
IconFlag
}
}
</script>
這種方案在React社區(qū)里被廣泛采用,我在本文的后續(xù)部分將展開(kāi)討論。
每個(gè)組件代表一個(gè)圖標(biāo)
我將更深入地說(shuō)一下這種方案在Vue.js中的使用。
在Vue.js中,模板和腳本是分開(kāi)的,組件通過(guò)components選項(xiàng)注冊(cè)。不過(guò)就像我們知道的,如果一個(gè)組件要用很多圖標(biāo)的話(huà),這種方式會(huì)挺麻煩。
Vue 2
<template>
<div>
<!-- inline -->
<icon-flag />
<!-- conditional -->
<icon-flag v-if="flag" />
<icon-star v-else />
<!-- dynamic -->
<component :is="flag ? IconFlag : IconStar" />
</div>
</template>
<script>
import { IconFlag, IconStar } from 'foo-icons'
export default {
components: {
IconFlag,
IconStar
},
data() {
return {
flag: true
}
},
created() {
Object.assign(this, {
IconFlag,
IconStar
})
}
}
</script>
可以看到如果想用圖標(biāo)的is綁定,我們必須把components手動(dòng)暴露到渲染上下文中。我們可以用字符串去替換組件定義來(lái)繞過(guò),但對(duì)代碼檢查和類(lèi)型系統(tǒng)來(lái)說(shuō)就不那么友好。
<template>
<div>
<!-- inline -->
<icon-flag />
<!-- conditional -->
<icon-flag v-if="flag" />
<icon-star v-else />
<!-- dynamic -->
<component :is="flag ? 'icon-flag' : 'icon-star'" />
</div>
</template>
<script>
import { IconFlag, IconStar } from 'foo-icons'
export default {
components: {
IconFlag,
IconStar
},
data() {
return {
flag: true
}
}
}
</script>
Vue 3
<template>
<!-- inline -->
<icon-flag />
<!-- conditional -->
<icon-flag v-if="flag" />
<icon-star v-else />
<!-- dynamic -->
<component :is="flag ? IconFlag : IconStar" />
</template>
<script>
import { ref } from 'vue'
import { IconFlag, IconStar } from 'foo-icons'
export default {
components: {
IconFlag,
IconStar
},
setup() {
const flag = ref(true)
return {
flag,
IconFlag,
IconStar
}
}
}
</script>
如果用:is綁定,<script>部分會(huì)變成這樣:
import { ref } from 'vue'
import { IconFlag, IconStar } from 'foo-icons'
export default {
components: {
IconFlag,
IconStar
},
setup() {
const flag = ref(true)
return {
flag
}
}
}
如果我們采納<script components>這樣的形式的話(huà):
<template>
<!-- inline -->
<icon-flag />
<!-- conditional -->
<icon-flag v-if="flag" />
<icon-star v-else />
<!-- dynamic -->
<component :is="flag ? 'icon-flag' : 'icon-star'" />
</template>
<script components>
export { IconFlag, IconStar } from 'foo-icons'
</script>
<script>
import { ref } from 'vue'
export default {
setup() {
const flag = ref(true)
return {
flag
}
}
}
</script>
或者用<script setup>提案:
<script setup>
import { ref } from 'vue'
export const flag = ref(true)
</script>
后記
這很篇文章很精練地介紹了在Vue中按需引入圖標(biāo)的方式,與React社區(qū)做比較,可以看到兩個(gè)生態(tài)的差異還是存在的。在React社區(qū)中,使用第3種方式(每個(gè)圖標(biāo)一個(gè)組件)非常普遍,如NPM上排名較高的react-icons和知名組件庫(kù)@ant-design/icons、@material-ui/icons都是這一形態(tài)。
這可能是由于React社區(qū)中并不傾向?qū)ⅰ敖M件”這一概念特殊化,組件就是普通的函數(shù)、普通的類(lèi),所以它的復(fù)用于其它的函數(shù)、類(lèi)的復(fù)用相同,如同lodash會(huì)導(dǎo)出很多個(gè)工具函數(shù)一樣,一個(gè)圖標(biāo)庫(kù)會(huì)導(dǎo)出很多個(gè)圖標(biāo)組件非常合理。
在文中對(duì)于使用createIcon工廠(chǎng)函數(shù)的使用有一些可以?xún)?yōu)化的點(diǎn)。正常使用工廠(chǎng)函數(shù)會(huì)讓創(chuàng)建的組件不可被tree shaking,其原因是語(yǔ)法分析會(huì)認(rèn)為createIcon函數(shù)本身是有副作用的,因此這個(gè)調(diào)用不能被安全地刪除??梢酝ㄟ^(guò)terser的特殊注釋來(lái)標(biāo)記:
// icon-flag.js
import { mdiFlag } from '@mdi/js'
import { createIcon } from 'v-icon'
export default /*#__PURE__*/createIcon('flag', mdiFlag)
以上就是如何在Vue.JS中使用圖標(biāo)組件的詳細(xì)內(nèi)容,更多關(guān)于Vue.JS中使用圖標(biāo)組件的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue element 關(guān)閉當(dāng)前tab 跳轉(zhuǎn)到上一路由操作
這篇文章主要介紹了vue element 關(guān)閉當(dāng)前tab 跳轉(zhuǎn)到上一路由操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-07-07
vue 解決mintui彈窗彈起來(lái),底部頁(yè)面滾動(dòng)bug問(wèn)題
這篇文章主要介紹了vue 解決mintui彈窗彈起來(lái),底部頁(yè)面滾動(dòng)bug問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-11-11
Vue實(shí)現(xiàn)拖拽穿梭框功能四種方式實(shí)例詳解
這篇文章主要介紹了Vue實(shí)現(xiàn)拖拽穿梭框功能四種方式,使用原生js實(shí)現(xiàn)拖拽,VUe使用js實(shí)現(xiàn)拖拽穿梭框,結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-09-09
electron-vite工具打包后如何通過(guò)內(nèi)置配置文件動(dòng)態(tài)修改接口地址
使用electron-vite?工具開(kāi)發(fā)項(xiàng)目打包完后每次要改接口地址都要重新打包,對(duì)于多環(huán)境切換或者頻繁變更接口地址就顯得麻煩,這篇文章主要介紹了electron-vite工具打包后通過(guò)內(nèi)置配置文件動(dòng)態(tài)修改接口地址實(shí)現(xiàn)方法,需要的朋友可以參考下2024-05-05

