把你的阿里巴巴图标库转成你自己的@ant-design/icons

文章描述了一种优化大型iconfont图标库的方法。通过分析和拆分JS资源,创建单个组件并利用TreeShake技术,实现了按需引入图标,减少了不必要的资源加载。具体步骤包括读取JS文件,生成组件,编写模板,以及生成摇树目录。
摘要由CSDN通过智能技术生成

背景

我们使用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文件来创建我们的图标组件,而不需要在独立创建一个包来存放图标的属性。

在这里插入图片描述

实现步骤

  1. 我们需要使用脚本读取这个js文件,输出一个数组,数组里面是每个svg图标的path
  2. 创建一个组件,用来接受svg的path内容
  3. 创建一个模版,循环数组的内容并生成相应的文件
  4. 脚本生成tree shake目录
  5. 执行脚本,生成对应文件

一、读取文件,输出数组

const fs = require('fs');

// 读取文件后整理成后的数据源
const fileContent = [];

function handleFormatData(content) {
  const symbolStr = content
    .split('<svg>')[1]
    .split('</svg>')[0]
    .replace(/<\/symbol>/g, '</symbol>\n');

  // 先根据 回车 拆成数组,每一项都包含了一个图标的信息
  const IconsStringArr = symbolStr.split('\n').filter(Boolean);

  const fileNameRegExp = /id="你的js的svg的id前缀-([^]*?)"/g;
  const viewBoxRegExp = /viewBox="([^]*?)"/g;
  const pathRegExp = /<path([^]*?)path>/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) => (
  <LeeIcon
    {...props}
    svgProps={{
      viewBox: '<%= viewBox %>',
      ...props.svgProps,
    }}
  >
  <%= svgPath %>
  </LeeIcon>
);

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();

四、生产Tree Shake目录

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('<svg>')[1]
    .split('</svg>')[0]
    .replace(/<\/symbol>/g, '</symbol>\n');

  // 先根据 回车 拆成数组,每一项都包含了一个图标的信息
  const IconsStringArr = symbolStr.split('\n').filter(Boolean);

  const fileNameRegExp = /id="你的js的svg的id前缀-([^]*?)"/g;
  const viewBoxRegExp = /viewBox="([^]*?)"/g;
  const pathRegExp = /<path([^]*?)path>/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) => (
  <LeeIcon
    {...props}
    svgProps={{
      viewBox: '<%= viewBox %>',
      ...props.svgProps,
    }}
  >
  <%= svgPath %>
  </LeeIcon>
);

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();
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值