在 HOW - 图形格式SVG及其应用(图标库) 中我们介绍过 "动态 Fetch CDN SVG 图标",这种在开发者将需要用到 图标 依次上传到 CDN 再在项目通过请求引入使用的场景。
但其实,更常见的做法还是统一维护一组 SVG 图标,然后将其封装成组件,直接在项目中通过 Import 的形式使用。
Lucide React
lucide-react
是一个为 React 提供图标的库,它是基于 Lucide 项目构建的。Lucide 是一个开源图标库,具有灵活、可定制和轻量的特点。lucide-react
允许在 React 项目中轻松地使用这些图标。
以下是 lucide-react
的工作原理和一些关键点:
主要原理
-
基于 SVG 的图标 :
Lucide 图标库中的所有图标都是基于 SVG(可缩放矢量图形)的。这意味着图标可以在任何大小下保持清晰和不失真,非常适合现代响应式设计。
-
React 组件 :
lucide-react
将这些 SVG 图标封装成 React 组件。每个图标都可以作为一个独立的 React 组件使用,这使得它们的集成非常方便,并且能够利用 React 的声明式特性和组件化优势。 -
灵活可定制 :
由于这些图标是基于 SVG 的,开发者可以轻松地通过 React 的属性来定制图标的大小、颜色、样式等。
lucide-react
组件通常接受标准的 SVG 属性,如width
,height
,stroke
,fill
等,使得定制变得非常简单。
安装和使用
安装 lucide-react
:
你可以通过 npm 或 yarn 安装 lucide-react
:
bash
npm install lucide-react
或
bash
yarn add lucide-react
使用 lucide-react
:
安装之后,你可以在你的 React 项目中导入和使用图标。例如:
jsx
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;
在这个例子中,ArrowRight
和 Home
图标作为 React 组件使用。你可以通过传递属性来定制它们的大小和颜色。
工作原理详解
-
SVG 图标文件 :
Lucide 项目包含了一系列的 SVG 图标文件,每个图标都是一个独立的 SVG 文件。
-
React 组件封装 :
lucide-react
库会将这些 SVG 文件转换为 React 组件。这个转换过程通常会在构建阶段完成,通过脚本将每个 SVG 文件转换为一个对应的 React 组件。 -
组件导出 :
转换后的 React 组件会被导出,并作为
lucide-react
库的一部分发布到 npm 上。开发者可以在他们的 React 项目中导入这些组件并使用。
React 组件封装:SVG 文件转换为 React 组件
两个源码方法:createLucideIcon、Icon
去翻阅源码实现,可以发现有如下两个重要的方法:
typescript
// 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;
typescript
// 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;
从这两个方法可以看出一些端倪,即基于 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 文件。
js
const fs = require('fs');
const path = require('path');
const iconsDir = path.resolve(__dirname, 'src/icons');
const iconFiles = fs.readdirSync(iconsDir);
b. 生成 React 组件代码
遍历每个 SVG 文件,生成对应的 React 组件代码。使用模板字符串创建 React 组件。
js
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);
});
c. 生成索引文件
为了方便导入所有图标组件,生成一个索引文件(index.js
),导出所有组件。
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);
3. 发布到 npm
通过 package.json
配置,指定入口文件为生成的 src/index.js
,并发布到 npm 上。
package.json:
json
{
"name": "lucide-react",
"version": "1.0.0",
"main": "src/index.js",
"dependencies": {
"react": "^17.0.0"
},
"scripts": {
"build": "node scripts/build.js"
}
}
4. 在项目中使用
开发者在项目中通过 npm 安装 lucide-react
,并使用导出的图标组件。
bash
npm install lucide-react
然后在 React 项目中导入和使用图标组件:
jsx
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;