背景
目前大多数组件库的Icon图标都是使用的font-family加载字体文件,这样存在一个很大的问题就是开发者可能只使用了其中几个图标,但是最终打包的时候会把整个字体文件打包进去,即使打包时开启压缩以及在文件传输时开启gzip,相对于几个图标而引入几百kb以及几m的体积代价依然很大。
但是提供Icon组件,开发者又因这种情况不去使用,那么干脆不如不提供?
既然页面都能实现按需加载,那么为什么Icon组件加载图标时进行按需加载呢?
实现
ionicons 是一个维护了非常久且非常稳定的图标库,近期再次阅读README.md和搜索issue demand load时发现该项目在4.x版本已经支持上述的图标按需加载了。
相关issue:
ionic-team/ionicons#499
ionic-team/ionicons#611
以下是官方描述
If you're using Ionic Framework, Ionicons is packaged by default, so no installation is necessary. Want to use Ionicons without Ionic Framework? Place the following <script> near the end of your page, right before the closing tag, to enable them.
<script src="https://unpkg.com/ionicons@4.2.2/dist/ionicons.js"></script>
大意是如果你不使用Ionic框架,却又要引入Ionicons,引入下列script标签即可
按需加载示例: jsbin.com/rozujasegi/…
或者直接复制下列这段代码到本地html文件然后打开预览效果
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<ion-icon name="heart"></ion-icon>
</body>
<script src="https://unpkg.com/ionicons@4.2.2/dist/ionicons.js"></script>
</html>
按需加载的原理是加载完ionicons.js
会自动去解析html中的ion-icon
标签,读取name
然后去动态加载对应的.svg文件,然后在对应dom节点中插入svg以及style,如下图
应用在React,实现Icon图标按需加载
最开始我是这样使用的
import * as React from 'react';
import { Component } from 'react';
import 'ionicons/dist/ionicons.js';
export default class Icon extends Component {
render() {
const html = '<ion-icon name="ios-heart"></ion-icon>';
return (
<i dangerouslySetInnerHTML={{__html: html}}/>
);
}
}
很显然是错误的,因为这样写一开始ionicons/dist/ionicons.js
就打包到了bundle中,icon组件还未插入到dom中,ionicons的解析就已经结束。
经过改进后如下:
import * as React from 'react';
import { Component } from 'react';
import 'ionicons/dist/ionicons.js';
export default class Icon extends Component {
componentDidMount() {
const script = document.createElement('script');
script.src = 'https://unpkg.com/ionicons@4.4.2/dist/ionicons.js';
script.id = id;
script.onload = () => {
document.body.removeChild(script);
};
document.body.appendChild(script);
}
render() {
const html = '<ion-icon name="ios-heart"></ion-icon>';
return (
<i dangerouslySetInnerHTML={{__html: html}}/>
);
}
}
即等待Icon组件渲染完成,已经插入到真实Dom树中后,我们再创建一个script标签去动态加载ionicons.js
去解析ion-icon
标签,待script标签加载完成后再进行移除。
粗看之下似乎没什么问题了?
我们再来分析一下下面这个场景,Yoshino组件库的文档Icon组件部分会把Icon展示出来,供开发者输入相关关键词查找Icon,大概接近800icons, 地址:yoshino-ui.github.io/#/docs/comp…
这个时候上面的Icon按需加载方案就有问题。
我们假设Icon组件文档页有800个图标,也就是说创建了800个Icon实例,那么对应的当这800个Icon组件被插入到dom树后出触发componentDidMount
,此时会创建800个script标签去加载ionicons.js,此时浏览器页面会出现假死
状态,即无法响应请求。
因此,我们需要判断一下body中是否已经存在了加载ionicons.js
的script标签了。
最后改造结果如下:
github.com/Yoshino-UI/…
喜欢Yoshino组件库就点个star吧,欢迎pr! 🚀