当前位置:   article > 正文

HOW - SVG 图标组件封装(Lucide React)

HOW - SVG 图标组件封装(Lucide React)

HOW - 图形格式SVG及其应用(图标库) 中我们介绍过 “动态 Fetch CDN SVG 图标”,这种在开发者将需要用到 图标 依次上传到 CDN 再在项目通过请求引入使用的场景。

但其实,更常见的做法还是统一维护一组 SVG 图标,然后将其封装成组件,直接在项目中通过 Import 的形式使用。

Lucide React

lucide-react 是一个为 React 提供图标的库,它是基于 Lucide 项目构建的。Lucide 是一个开源图标库,具有灵活、可定制和轻量的特点。lucide-react 允许在 React 项目中轻松地使用这些图标。

以下是 lucide-react 的工作原理和一些关键点:

主要原理

  1. 基于 SVG 的图标:
    Lucide 图标库中的所有图标都是基于 SVG(可缩放矢量图形)的。这意味着图标可以在任何大小下保持清晰和不失真,非常适合现代响应式设计。

  2. React 组件:
    lucide-react 将这些 SVG 图标封装成 React 组件。每个图标都可以作为一个独立的 React 组件使用,这使得它们的集成非常方便,并且能够利用 React 的声明式特性和组件化优势。

  3. 灵活可定制:
    由于这些图标是基于 SVG 的,开发者可以轻松地通过 React 的属性来定制图标的大小、颜色、样式等。lucide-react 组件通常接受标准的 SVG 属性,如 width, height, stroke, fill 等,使得定制变得非常简单。

安装和使用

安装 lucide-react:

你可以通过 npm 或 yarn 安装 lucide-react

npm install lucide-react
  • 1

yarn add lucide-react
  • 1

使用 lucide-react:

安装之后,你可以在你的 React 项目中导入和使用图标。例如:

import React from 'react';
import { ArrowRight, Home } from 'lucide-react';

function App() {
  return (
    <div>
      <h1>My App</h1>
      <ArrowRight size={24} color="red" />
      <Home size={32} color="blue" />
    </div>
  );
}

export default App;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

在这个例子中,ArrowRightHome 图标作为 React 组件使用。你可以通过传递属性来定制它们的大小和颜色。

工作原理详解

  1. SVG 图标文件:
    Lucide 项目包含了一系列的 SVG 图标文件,每个图标都是一个独立的 SVG 文件。

  2. React 组件封装:
    lucide-react 库会将这些 SVG 文件转换为 React 组件。这个转换过程通常会在构建阶段完成,通过脚本将每个 SVG 文件转换为一个对应的 React 组件。

  3. 组件导出:
    转换后的 React 组件会被导出,并作为 lucide-react 库的一部分发布到 npm 上。开发者可以在他们的 React 项目中导入这些组件并使用。

React 组件封装:SVG 文件转换为 React 组件

两个源码方法:createLucideIcon、Icon

去翻阅源码实现,可以发现有如下两个重要的方法:

// https://github.com/lucide-icons/lucide/blob/main/packages/lucide-react/src/createLucideIcon.ts
import { createElement, forwardRef } from 'react';
import { mergeClasses, toKebabCase } from '@lucide/shared';
import { IconNode, LucideProps } from './types';
import Icon from './Icon';

/**
 * Create a Lucide icon component
 * @param {string} iconName
 * @param {array} iconNode
 * @returns {ForwardRefExoticComponent} LucideIcon
 */
const createLucideIcon = (iconName: string, iconNode: IconNode) => {
  const Component = forwardRef<SVGSVGElement, LucideProps>(({ className, ...props }, ref) =>
    createElement(Icon, {
      ref,
      iconNode,
      className: mergeClasses(`lucide-${toKebabCase(iconName)}`, className),
      ...props,
    }),
  );

  Component.displayName = `${iconName}`;

  return Component;
};

export default createLucideIcon;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
// https://github.com/lucide-icons/lucide/blob/main/packages/lucide-react/src/Icon.ts
import { createElement, forwardRef } from 'react';
import defaultAttributes from './defaultAttributes';
import { IconNode, LucideProps } from './types';
import { mergeClasses } from '@lucide/shared';

interface IconComponentProps extends LucideProps {
  iconNode: IconNode;
}

/**
 * Lucide icon component
 *
 * @component Icon
 * @param {object} props
 * @param {string} props.color - The color of the icon
 * @param {number} props.size - The size of the icon
 * @param {number} props.strokeWidth - The stroke width of the icon
 * @param {boolean} props.absoluteStrokeWidth - Whether to use absolute stroke width
 * @param {string} props.className - The class name of the icon
 * @param {IconNode} props.children - The children of the icon
 * @param {IconNode} props.iconNode - The icon node of the icon
 *
 * @returns {ForwardRefExoticComponent} LucideIcon
 */
const Icon = forwardRef<SVGSVGElement, IconComponentProps>(
  (
    {
      color = 'currentColor',
      size = 24,
      strokeWidth = 2,
      absoluteStrokeWidth,
      className = '',
      children,
      iconNode,
      ...rest
    },
    ref,
  ) => {
    return createElement(
      'svg',
      {
        ref,
        ...defaultAttributes,
        width: size,
        height: size,
        stroke: color,
        strokeWidth: absoluteStrokeWidth ? (Number(strokeWidth) * 24) / Number(size) : strokeWidth,
        className: mergeClasses('lucide', className),
        ...rest,
      },
      [
        ...iconNode.map(([tag, attrs]) => createElement(tag, attrs)),
        ...(Array.isArray(children) ? children : [children]),
      ],
    );
  },
);

export default Icon;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60

从这两个方法可以看出一些端倪,即基于 React 的 createElement 来转换成 React 组件。

模拟构建过程

下面介绍一般如何去构建一个图标转换过程并导出供使用者使用。

1. 源码结构

通常,目录结构会包含以下部分:

  • src/icons: 包含所有 SVG 图标文件的目录。
  • src/components: 存放将 SVG 图标转换为 React 组件的代码。
  • scripts: 包含用于自动化转换和构建的脚本。
  • index.js: 入口文件,导出所有图标组件。

2. SVG 图标转换为 React 组件

核心过程是将每个 SVG 图标文件转换为一个 React 组件。这个转换过程通常包括以下步骤:

a. 读取 SVG 文件

使用 Node.js 的文件系统模块(fs)读取存放在 src/icons 目录中的所有 SVG 文件。

const fs = require('fs');
const path = require('path');

const iconsDir = path.resolve(__dirname, 'src/icons');
const iconFiles = fs.readdirSync(iconsDir);
  • 1
  • 2
  • 3
  • 4
  • 5
b. 生成 React 组件代码

遍历每个 SVG 文件,生成对应的 React 组件代码。使用模板字符串创建 React 组件。

iconFiles.forEach(file => {
  const iconName = path.basename(file, '.svg');
  const svgContent = fs.readFileSync(path.join(iconsDir, file), 'utf8');

  const componentCode = `
import React from 'react';

const ${iconName} = (props) => (
  <svg {...props}>
    ${svgContent}
  </svg>
);

export default ${iconName};
  `;

  fs.writeFileSync(path.join(__dirname, `src/components/${iconName}.js`), componentCode);
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
c. 生成索引文件

为了方便导入所有图标组件,生成一个索引文件(index.js),导出所有组件。

const indexContent = iconFiles.map(file => {
  const iconName = path.basename(file, '.svg');
  return `export { default as ${iconName} } from './components/${iconName}';`;
}).join('\n');

fs.writeFileSync(path.join(__dirname, 'src/index.js'), indexContent);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

3. 发布到 npm

通过 package.json 配置,指定入口文件为生成的 src/index.js,并发布到 npm 上。

package.json:

{
  "name": "lucide-react",
  "version": "1.0.0",
  "main": "src/index.js",
  "dependencies": {
    "react": "^17.0.0"
  },
  "scripts": {
    "build": "node scripts/build.js"
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

4. 在项目中使用

开发者在项目中通过 npm 安装 lucide-react,并使用导出的图标组件。

npm install lucide-react
  • 1

然后在 React 项目中导入和使用图标组件:

import React from 'react';
import { ArrowRight, Home } from 'lucide-react';

function App() {
  return (
    <div>
      <h1>My App</h1>
      <ArrowRight size={24} color="red" />
      <Home size={32} color="blue" />
    </div>
  );
}

export default App;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Li_阴宅/article/detail/856160
推荐阅读
相关标签
  

闽ICP备14008679号