我们使用iconfont-阿里巴巴矢量图标库来管理自己的一套图标,并且基于它的js资源,封装了自己的icons图标组件。封装的方法是使用了antd提供的createFromIconfontCN方法
但随着图标库越来越大,JS资源文件也变得越来越大。在业务中,哪怕你只需要一个图标,却也要引入整个的JS资源。这使得我们必须考虑对他进行优化。
我们参考@ant-design/icons的设计模式,计划将它的图标拆分成了一个一个组件,在业务中按需引入来达到减少资源浪费的目的。
我们阅读了@ant-design/icons的源码,发现他的图标对象其实都放在了一个叫做@ant-design/icons-svg的包里,里面放了每一个图标的属性。@ant-design/icons引用了这些图标并再次封装了一些属性和方法。
然而我们并不能这样做,我们没有深究他的这些图标对象是如何自动产生的。 我们只有在iconfont-阿里巴巴矢量图标库上下载的JS资源文件。
好在这个JS文件非常的有规律,他其实就是每一个svg图标的path路径。这使得我们可以简单的使用这个js文件来创建我们的图标组件,而不需要在独立创建一个包来存放图标的属性。
const fs = require('fs');
// 读取文件后整理成后的数据源
const fileContent = [];
function handleFormatData(content) {
const symbolStr = content
.split(')[1]
.split('')[0]
.replace(/<\/symbol>/g, '\n');
// 先根据 回车 拆成数组,每一项都包含了一个图标的信息
const IconsStringArr = symbolStr.split('\n').filter(Boolean);
const fileNameRegExp = /id="你的js的svg的id前缀-([^]*?)"/g;
const viewBoxRegExp = /viewBox="([^]*?)"/g;
const pathRegExp = / /g;
IconsStringArr.forEach((element) => {
const fileName = element.match(fileNameRegExp)[0].split('id="你的js的svg的id前缀-')[1].split('"')[0];
const viewBox = element.match(viewBoxRegExp)[0].split('viewBox="')[1].split('"')[0];
const path = element.match(pathRegExp).join('');
fileContent.push({
fileName: `P${fileName}`,
viewBox: viewBox,
svgPath: path,
});
});
}
fs.readFile('./iconfont.js', (err, data) => {
// 将文件文本处理成想要的数据源
handleFormatData(data.toString());
});
import React, { PropsWithChildren } from 'react';
export interface LeeIconProps {
spanProps?: React.HTMLProps<HTMLSpanElement>;
svgProps?: React.SVGProps<SVGSVGElement>;
}
const LeeIcon = (props: PropsWithChildren<LeeIconProps>) => {
return (
<span role="img" {...props.spanProps} >
<svg width="1em" fill="currentColor" aria-hidden="true" {...props.svgProps}>
{props.children}
</svg>
</span>
);
};
export default LeeIcon;
const lodash = require('lodash');
function generateFile() {
const render = lodash.template(
`
/* eslint-disable react/self-closing-comp */
// GENERATE BY ../../scripts/generate.ts
// DON NOT EDIT IT MANUALLY
import LeeIcon, { LeeIconProps } from '../components/icon';
const <%= fileName %> = (props: LeeIconProps) => (
',
...props.svgProps,
}}
>
<%= svgPath %>
);
export default <%= fileName %>;
`.trim(),
);
console.log('generateFileLoading...');
fileContent.forEach(({ fileName, viewBox, svgPath }) => {
// 写入文件内容
fs.writeFileSync(`../src/leeIcons/${fileName}.tsx`, render({ fileName, viewBox, svgPath }), () => {});
console.log(`generateFile${fileName}Down`);
});
}
// 生成文件
generateFile();
function generateTreeShake() {
console.log('generateTreeShakeLoading...');
const render = lodash.template(
`
export { default as <%= fileName %> } from './<%= fileName %>';`,
);
fs.writeFileSync(
`../src/leeIcons/index.ts`,
`
// GENERATE BY ../../scripts/generate.ts
// DON NOT EDIT IT MANUALLY
`.trim(),
() => {},
);
fileContent.forEach(({ fileName }) => {
fs.appendFileSync(`../src/leeIcons/index.ts`, render({ fileName }), () => {});
});
console.log('generateTreeShakeSuccess');
}
// 生成摇树首页
generateTreeShake();
node ./generate.js
/* eslint-disable @typescript-eslint/no-require-imports */
/*
* @Author: atwLee
* @Date: 2023-04-07 15:48:20
* @LastEditors: atwLee
* @LastEditTime: 2023-04-12 10:18:40
* @Description:
* @FilePath: /panui/packages/Icons/script/generate.js
*/
const fs = require('fs');
const lodash = require('lodash');
const rimraf = require('rimraf');
// 读取文件后整理成后的数据源
const fileContent = [];
function handleFormatData(content) {
const symbolStr = content
.split(')[1]
.split('')[0]
.replace(/<\/symbol>/g, '\n');
// 先根据 回车 拆成数组,每一项都包含了一个图标的信息
const IconsStringArr = symbolStr.split('\n').filter(Boolean);
const fileNameRegExp = /id="你的js的svg的id前缀-([^]*?)"/g;
const viewBoxRegExp = /viewBox="([^]*?)"/g;
const pathRegExp = / /g;
IconsStringArr.forEach((element) => {
const fileName = element.match(fileNameRegExp)[0].split('id="你的js的svg的id前缀-')[1].split('"')[0];
const viewBox = element.match(viewBoxRegExp)[0].split('viewBox="')[1].split('"')[0];
const path = element.match(pathRegExp).join('');
fileContent.push({
fileName: `P${fileName}`,
viewBox: viewBox,
svgPath: path,
});
});
}
function generateFile() {
const render = lodash.template(
`
/* eslint-disable react/self-closing-comp */
// GENERATE BY ../../scripts/generate.ts
// DON NOT EDIT IT MANUALLY
import LeeIcon, { LeeIconProps } from '../components/icon';
const <%= fileName %> = (props: LeeIconProps) => (
',
...props.svgProps,
}}
>
<%= svgPath %>
);
export default <%= fileName %>;
`.trim(),
);
console.log('generateFileLoading...');
fileContent.forEach(({ fileName, viewBox, svgPath }) => {
// 写入文件内容
fs.writeFileSync(`../src/leeIcons/${fileName}.tsx`, render({ fileName, viewBox, svgPath }), () => {});
console.log(`generateFile${fileName}Down`);
});
}
function generateTreeShake() {
console.log('generateTreeShakeLoading...');
const render = lodash.template(
`
export { default as <%= fileName %> } from './<%= fileName %>';`,
);
fs.writeFileSync(
`../src/leeIcons/index.ts`,
`
// GENERATE BY ../../scripts/generate.ts
// DON NOT EDIT IT MANUALLY
`.trim(),
() => {},
);
fileContent.forEach(({ fileName }) => {
fs.appendFileSync(`../src/leeIcons/index.ts`, render({ fileName }), () => {});
});
console.log('generateTreeShakeSuccess');
}
function generateIcons() {
// 删除目录
rimraf('../src/leeIcons', () => {
// 增加目录
fs.mkdirSync('../src/leeIcons', () => {});
// 读取icons-js文件,获取需要的数据
fs.readFile('./iconfont.js', (err, data) => {
// 将文件文本处理成想要的数据源
handleFormatData(data.toString());
// 生成文件
generateFile();
// 生成摇树首页
generateTreeShake();
});
});
}
generateIcons();