buildAdmin开源项目引入四种图标方式详解
Sapper 人气:0正文
在项目开发中,我们经常使用可能都是UI组件库里的图标,当然由于业务需要,可能当前图标库没有我们需要的图标这时候就需要引入其它图标库的图标,比如iconfont、FontAweSome、本地图标库。在了解引入这些图标库之前,我们先学习一下各种图标库的引入使用:
Element-Plus:由于elemen官方已经把图标封装成了组件,所以当我们引入图标的时候,需要全局声明组件。
import * as Icons from '@element-plus/icons'; const app = createApp(App); // 全局注册图标,牺牲一点性能 for (let i in Icons) { // 官方图标名称首字母都是大写,所以转为小写,并命名组件未el-icon-图标名 app.component(`el-icon-${toLine(i)}`, (Icons as any)[i]); } // 组件中使用图标 <el-icon-user />
Iconfont:阿里巴巴图标库,通过创建一个项目目录,然后把我们需要的图标添加进去,然后在项目中引入图标目录的cdn(三种方式之一:css代码、css链接、js链接)就可以使用了,如果没有特殊处理一般是在项目的index.html中引入相关链接,css代码可以在根目录的样式文件中引入。然后就可以在项目中使用:
<i class="iconfont icon-user"></i>
FontAwesome:一个比较好用的字体图标库,可以直接通过cdn引入,也可以通过安装package包引入,然后就可以使用了:
<i class="fa fa-user"></i>
引用本地图标:一般使用svg格式图标,因为svg性能好,相对于其它格式,它体积更小,可以任意放大图形显示,不以牺牲图标质量为代价,项目中是不能直接加载svg格式,需要额外插件实现(后面会详细介绍)。
<svg class="svg-icon icon" style="width: 1em;height: 1em;color: black;"> <use href="#local-vue" rel="external nofollow" rel="external nofollow" /> </svg>
为了方便维护以及扩展,我们可以把四种图标封装为统一组件使用,在封装前我们需要明确三点:
- 获取所有图标。
- 实现图标可复制,复制就可用的原则。
- 图标使用统一组件。
在学习各类的图标库之前我们先了解一下如何封装一下图标共用组件,它向外暴露的名称是Icon:
实现组件健壮性、易维护:支持图标名称(name)、图标颜色(color)、图标大小(size)三要素的自定义。四种图标格式引入为element-plus(el-icon-iconName
)、iconfont(iconfont iconName
)、fontawesome(fa fa-iconName
)、本地图标(local-iconName
),iconName是图标名称。
props: { name: { type: String, required: true, }, size: { type: String, default: '30px', }, color: { type: String, default: '#00000', }, }, // 处理样式,去掉多余px命名 const iconStyle = computed((): CSSProperties => { const { size, color } = props; let s = `${size.replace('px', '')}px`; return { fontSize: s, color: color, }; });
兼容上面四种图标实现:通过Vue3的setup的返回值中使用渲染函数实现,不需要在template中定义标签使用,分三种情况。
createVNode函数:创建虚拟节点,从左到右有三个参数:html标签名称或组件(String)、标签属性(Object)、嵌套标签定义(Array)。
对于element-plus图标的渲染:官方是通过el-icon标签内直接使用图标组件,所以创建虚拟节点标签就是el-icon,由于图标组件是嵌套的,所以需要用到第三参数。
setup(props) { // 当前引入的是element-plus图标 if (props.name.indexOf('el-icon-') === 0) { return () => createVNode( 'el-icon', { class: 'icon el-icon', style: iconStyle.value }, [createVNode(resolveComponent(props.name))] ); } }
对于iconfont、fontawesome图标的渲染:由于使用这两种的图标的标签都是i,它们唯一不同就是图标名称的命名所以可以共用同一个渲染函数。
setup(props){ // 当前引入的是iconfont或fontawesome图标 if (props.name.indexOf('local-') === 0 || isExternal(props.name)) { return () => createVNode('i', { class: [props.name, 'icon'], style: iconStyle.value, }); } }
对于本地svg图标的渲染:直接引入本地封装的svg组件,把这个组件当作渲染标签,这里图标命名以local-iconName格式引入的。
setup(props){ // 当前引入的是本地svg图标 if (props.name.indexOf('local-') === 0 || isExternal(props.name)) { return () => createVNode(svg, { name: props.name, size: props.size, color: props.color, }); } }
最终就可以通过这样使用图标:
<Icon name="" color="" size=""/>
其实上面就已经实现了四种图标的类型统一封装,一致使用。接下来为了更方便获取图标,我们把所有图标封装起来就可以直接cv使用了。
点击实现cv方式:通过点击图标传入我们想要复制的内容,一般都是整个组件的字符串。
export const useCopy = (text: string) => { let input = document.createElement('input'); // 创建输入框 input.value = text; // 给输入框value赋值 document.body.appendChild(input); // 追加到body里面去 input.select(); // 选择输入框的操作 document.execCommand('Copy'); // 执行复制操作 document.body.removeChild(input); // 删除加入的输入框 ElMessage.success('复制成功!'); };
引入Element-Plus图标库
import * as elIcons from '@element-plus/icons-vue'; // 获取所有Element-Plus图标组件名称,如搜索图标Search export function getElementPlusIconfontNames() { return new Promise<string[]>((resolve, reject) => { nextTick(() => { const iconfonts = []; const icons = elIcons as any; // 遍历添加icons组件名称 for (const i in icons) { iconfonts.push(icons[i].name); } if (iconfonts.length > 0) { resolve(iconfonts); } else { reject('No ElementPlus Icons'); } }); }); }
引入Iconfont图标库
先加载图标样式表:
const cssUrls: Array<string> = [ '//at.alicdn.com/t/c/font_3846007_vf3shrhbpya.css', // 阿里图标库cs,每添加一次图标都需要更换 '//cdn.bootcdn.net/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css', // font-awesome的css ]; export default function init() { // 遍历加载图标链接样式 if (cssUrls.length > 0) { cssUrls.map((v) => { loadCss(v); }); } } // 通过创建link标签引入样式链接 export function loadCss(url: string): void { const link = document.createElement('link'); // 创建link标签 link.rel = 'stylesheet'; link.href = url; // 是否采用跨域的方式加载。它可以取两个值 // anonymous(跨域请求时,不发送用户凭证,主要是 Cookie) // use-credentials(跨域时发送用户凭证)。 link.crossOrigin = 'anonmous'; document.getElementsByTagName('head')[0].appendChild(link); }
获取当前页面中从指定域名加载到的样式表内容:在调用这个函数之前必须要先引入样式。
// 获取样式表内容 function getStylesFromDomain(domain: string) { const sheets = []; const styles: StyleSheetList = document.styleSheets; for (const key in styles) { if (styles[key].href && (styles[key].href as string).indexOf(domain) > -1) { sheets.push(styles[key]); } } return sheets; }
调用这个函数之后就可以获取到当前图标库相关样式表内容,我们的目的就是拿到图标名称,可以提取rules数组内的样式名称就可以拿到所有图标名称了。
// 获取所有iconfont图标库图标名称 export function getIconfontNames() { init(); return new Promise<string[]>((resolve, reject) => { nextTick(() => { const iconfonts = []; const sheets = getStylesFromDomain('at.alicdn.com'); for (const key in sheets) { const rules: any = sheets[key].cssRules; for (const k in rules) { // .表示匹配除换行符 \n 之外的任何单字符 // *表示单个字符匹配任意次 if ( rules[k].selectorText && /^\.icon-(.*)::before$/g.test(rules[k].selectorText) ) { // 去掉样式的.符号以及::before iconfonts.push( `${rules[k].selectorText .substring(1, rules[k].selectorText.length) .replace(/\:\:before/gi, '')}` ); } } } if (iconfonts.length > 0) { resolve(iconfonts); } else { reject('No Iconfont style sheet'); } }); }); }
引入FontAwesome图标库
先加载图标样式表:也就是直接调用init函数,加载相应的样式链接。
获取当前页面中从指定域名加载到的样式表内容:如下图所示。
export function getAwesomeIconfontName() { init(); return new Promise<string[]>((resolve, reject) => { nextTick(() => { const iconfonts = []; // 获取所有图标名称 const sheets = getStylesFromDomain( 'cdn.bootcdn.net/ajax/libs/font-awesome/' ); for (const key in sheets) { const rules: any = sheets[key].cssRules; // 处理方法与iconfont一致,只不过名称不一样 for (const k in rules) { if ( rules[k].selectorText && /^\.fa-(.*)::before$/g.test(rules[k].selectorText) ) { if (rules[k].selectorText.indexOf(', ') > -1) { // selectorText里有多个图标,只提取第一个 const iconNames = rules[k].selectorText.split(', '); iconfonts.push( `${iconNames[0] .substring(1, iconNames[0].length) .replace(/\:\:before/gi, '')}` ); } else { iconfonts.push( `${rules[k].selectorText .substring(1, rules[k].selectorText.length) .replace(/\:\:before/gi, '')}` ); } } } } if (iconfonts.length > 0) { resolve(iconfonts); } else { reject('No AwesomeIcon style sheet'); } }); }); }
引入本地svg图标
在引入本地svg图标之前,我们先了解一下svg标签的相关知识:
加载全部内容