ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于 Carbon 构建自己的图标库:从 `@carbon/icons` 到 `@carbon/icon-helpers` 的完整实战指南

基于 Carbon 构建自己的图标库:从 `@carbon/icons` 到 `@carbon/icon-helpers` 的完整实战指南 基于 Carbon 构建自己的图标库从carbon/icons到carbon/icon-helpers的完整实战指南【免费下载链接】carbonA design system built by IBM项目地址: https://gitcode.com/GitHub_Trending/carbo/carbon本指南基于 Carbon Design System 仓库中的官方文档 docs/guides/building-an-icon-library.md 展开面向希望为自己的框架React、Vue 或其他构建图标库的开发者。你将掌握如何以carbon/icons为数据源产出 SVG 模块、如何组织es/lib/umd多格式输出、如何编写tasks/build.js构建脚本以及如何使用carbon/icon-helpers处理 SVG 容器的可访问性属性与浏览器兼容性问题最终产出一个与 Carbon 生态保持一致的、支持 tree-shaking 的图标库。简介为什么要有这样一份指南Carbon 是 IBM 开源的设计系统其图标体系规模庞大仅packages/icons的src/svg目录就存放了数千个源 SVG 文件横跨 16/20/24/32 等多套尺寸。面对如此体量的图标集如果每个框架React、Vue、Web Components……都各自重复实现一套图标库必然导致构建逻辑、导入路径、可访问性处理参差不齐。因此Carbon 提供了一份可复用的图标库构建参考——即本文讲解的指南。它的目标读者不是终端用户而是想要在 Carbon 生态内或借鉴 Carbon 模式为特定框架封装图标库的开发者。按照指南产出的成品可以直接参考仓库中现成的carbon/icons-reactpackages/icons-react等包。在开始之前指南要求你先在 Carbon Elements 项目上创建 issue 并与核心贡献者确认方向确保你的工作落在生态中合适的位置避免重复造轮子。数据源carbon/icons包构建图标库的核心数据源是carbon/iconspackages/icons包。根据 packages/icons/package.json 中的description它是用于 Carbon Design System 数字与软件产品的图标。该包除了持有全部源 SVG 资源位于 packages/icons/src/svg外还对外导出以下产物优化后的 SVG 资源由源文件处理后生成的、适合直接使用的 SVG图标描述符icon descriptors以 JavaScript 对象编码 SVG 信息的结构是构建各框架图标库的基础数据meta.json元数据文件对图标库构建者尤为关键它记录了每个图标的元信息名称、尺寸、输出路径等是tasks/build.js遍历图标清单的依据。关于图标资源的详细说明可进一步阅读 docs/guides/icons.md。一个典型的源 SVG 长这样packages/icons/src/svg/caret--down.svg?xml version1.0 encodingutf-8? svg version1.1 idicon xmlnshttp://www.w3.org/2000/svg ... width8px height4px viewBox0 0 8 4 ... polygon points8,0 4,4 0,0 / /svg图标库构建工具要做的就是把这类 SVG 中的路径信息提取为 JavaScript 描述符再按目标框架生成模块文件。设计理念图标库架构的五条原则指南提出在设计图标库架构时应遵循以下设计目标这也是carbon/icons-react、carbon/icons-vue等包共同遵循的同时支持直接导入与可 tree-shaking 的入口。面对 IBM Design Language 图标集如此大的规模每个库都应提供两种导入方式直接路径/es/icon-name/16.js按需加载单个图标入口文件/es/index.js集中导出全部图标。这意味着不能提供一个通用的Icon nameicon-name组件因为那样会破坏对carbon/icons描述符的 tree-shaking可以支持Icon icon{descriptor} /这样的写法但那会迫使使用者同时安装两个依赖与只安装你的图标库的目标相悖。最小化运行时逻辑。优先在构建期根据 SVG 数据静态生成标记markup而不是在运行时用数据动态拼接 SVG。这样浏览器端不需要额外的解析开销。用carbon/icon-helpers校验svg容器属性。其中getAttributes帮助函数会为svg容器提供正确的属性集合详见下文。保持导入路径与carbon/icons包一致。这些信息通过carbon/icons/meta.json提供。不要自创随意的直接导入路径应在各包之间保持一致方便使用者记忆与迁移。不依赖通用渲染组件如前所述保证每个图标都以独立模块存在。项目结构一个图标库包的布局按照指南在克隆 Carbon Elements 仓库见根目录 README.md并遵循贡献指南之后你可以在packages目录下新建一个文件夹运行yarn init -y生成package.json并编辑其中的name字段。构建完成后一个图标库包应呈现如下结构icon-library-package-name ├── README.md ├── es # ESM 输出目录 ├── examples # 展示用法的示例目录 ├── lib # CommonJS 模块输出目录 ├── package.json ├── src ├── tasks │ └── build.js # 生成图标库的构建任务 └── umd # UMD 模块输出目录对照仓库实况carbon/icon-helpers的 package.json 中clean脚本为rimraf es lib umd其构建产物目录与指南描述完全一致carbon/icons-react的 package.json 同样声明了esESM与libCommonJS输出并在files字段中只发布编译产物。package.json中的脚本指南建议每个图标库都暴露以下两个脚本{ ...: ..., scripts: { build: yarn clean node tasks/build.js, clean: rimraf es lib umd } }clean任务让该包能融入工作区根目录的全局yarn cleanbuild任务应在发布前运行同时暴露build也能让工作区根通过yarn build一次性构建所有包的资源。仓库中的实际实现完全印证了这一点packages/icons/package.jsonbuild: yarn clean node tasks/build.js、clean: rimraf es lib metadata.json svg多清除了构建中间产物metadata.json与svg另有prepublishOnly: yarn build确保发布前必先构建packages/icon-helpers/package.jsonbuild: yarn clean carbon-cli bundle src/index.ts --name CarbonIconHelpers --dtspackages/icons-react/package.jsonbuild: yarn clean node tasks/build.js、clean: rimraf es lib。可以看到carbon/icons-react的build正是clean 后执行tasks/build.js这一模式的直接落地。核心构建脚本拆解tasks/build.js指南强调tasks/build.js的结构被刻意设计得通用以便你按需实现自己的逻辑。其顶层模式如下use strict; const path require(path); const build require(../src/build); build({ cwd: path.resolve(__dirname, ../) }).catch((error) { console.error(error); });即入口脚本只负责解析包根目录cwd并调用真正的build函数。build函数本身应具有如下形态use strict; // 用于向控制台输出日志支持 reporter.info / reporter.success / reporter.error 等 const { reporter } require(carbon/cli-reporter); // 来自图标构建过程的元信息 const meta require(carbon/icons/meta.json); const fs require(fs-extra); const path require(path); async function build({ cwd }) { const { build: tsdown } await import(tsdown); // 定义 ESM 与 CJS 的打包入口 const ESM_DIR path.join(cwd, es); const BUNDLE_TARGETS [ { format: commonjs, directory: lib, }, ]; reporter.info(Building ESM and CJS sources...); // 使用 Promise.all 的扇出模式并行执行任务 await Promise.all( // meta 是一个映射提供当前待构建图标的哈希信息 meta.map(async (info) { // 通常实现会提供从源文件创建源码模块的方法并写入 ESM 路径 const source createModuleFromInfo(info); const jsFilepath path.join(cwd, info.outputOptions.file); await fs.ensureDir(path.dirname(jsFilepath)); await fs.writeFile(jsFilepath, source); // 之后用 tsdown 把 ESM 模块编译为 CJS await Promise.all( BUNDLE_TARGETS.map(async ({ format, directory }) { await tsdown({ entry: [jsFilepath], format: [format], outDir: path.dirname( jsFilepath.replace(/\/es\//, /${directory}/) ), clean: false, dts: false, external: [carbon/icon-helpers, prop-types, react], }); }) ); }) ); // 之后应尝试在 es/index.js 下构造全量图标入口。 // 鉴于库的规模这个入口必须可 tree-shaking reporter.info(Building ESM and CJS entrypoints...); // 可以复用 BUNDLE_TARGETS 生成 CJS 入口 // 构造完所有模块文件后可在此生成 examples 目录中的示例信息 reporter.success(Done! ); } module.exports build;从源码可以提炼出几个关键点供实现时参考数据源是metacarbon/icons/meta.json是一个数组/映射每一项info含outputOptions.file等字段直接告诉你每个图标应输出到哪个相对路径——这正是导入路径与carbon/icons保持一致这条原则的落地方式。扇出并行外层Promise.all让所有图标并行生成 ESM 源模块内层Promise.all再并行编译 CJS充分利用多核与 I/O 并发。tsdown 完成格式编译tsdown的entry指向刚写入的 ESM 文件format: [commonjs]输出到lib目录clean: false防止清掉刚生成的内容dts: false表示这里不生成类型声明external把carbon/icon-helpers、prop-types、react等运行时依赖标记为外部引用避免打进产物。入口必须可 tree-shakinges/index.js用具名导出named exports汇总所有图标这样打包器才能按需摇树。可参考carbon/icons-react的 READMEpackages/icons-react/README.md中的用法import { Add } from carbon/icons-react。值得一提的是指南中的这段构建逻辑是针对 React 类库的示例external中包含react与prop-typesVue 或其他框架只需替换相应 external 依赖与模块模板即可。深入carbon/icon-helpersSVG 容器的属性与序列化carbon/icon-helperspackages/icon-helpers提供了一系列方法用于把carbon/icons的图标描述符转换为 DOM 节点或字符串并获取整个svg容器的正确属性。其入口在 packages/icon-helpers/src/index.ts对外导出defaultAttributes、getAttributes、formatAttributes、toString、toSVG五个成员。在图标模块源码中这样引入import { getAttributes } from carbon/icon-helpers;getAttributes可访问性与 IE11 兼容指南指出getAttributes帮助设置可访问性属性如aria-label以及 IE11 浏览器怪癖如focusable属性。其完整实现在 packages/icon-helpers/src/getAttributes.tsexport const defaultAttributes { // focusable 是字符串属性因此这里不使用布尔值 focusable: false, preserveAspectRatio: xMidYMid meet, }; export default function getAttributes({ width, height, viewBox 0 0 ${width} ${height}, ...attributes }: Recordstring, unknown {}): Recordstring, unknown { const { tabindex, ...rest } attributes; const iconAttributes: Recordstring, unknown { ...defaultAttributes, ...rest, width, height, viewBox, }; if (iconAttributes[aria-label] || iconAttributes[aria-labelledby]) { iconAttributes.role img; if (tabindex ! undefined tabindex ! null) { iconAttributes.focusable true; iconAttributes.tabindex tabindex; } } else { iconAttributes[aria-hidden] true; } return iconAttributes; }理解它的行为规则可以直接参考配套单元测试 packages/icon-helpers/src/tests/getAttributes-test.js默认装饰性图标未提供任何无障碍信息时输出aria-hiddentrue、focusablefalse、preserveAspectRatioxMidYMid meet且不设置role。这符合图标默认是装饰性内容的定位——carbon/icons-react的 README 也明确说明默认设置aria-hiddentrue。提供aria-label或aria-labelledby时role被设为img同时移除aria-hidden让屏幕阅读器可以读出图标含义。tabindex的微妙规则只有当同时存在aria-label/aria-labelledby和tabindex时才输出focusabletrue并透传tabindex如果只有tabindex而没有无障碍标签则不会透传——因为 SVG 应当先有 aria 标签才可聚焦。测试用例should set focusable%s when using %s用五组组合验证了这一行为。viewBox默认由width/height推导0 0 ${width} ${height}因此图标库只需传入尺寸即可。toSVG与toString描述符的两种序列化形式图标描述符的类型定义在 packages/icon-helpers/src/types.tsexport default interface IconDescriptor { elem?: string; // 元素名默认 svg attrs?: Recordstring, string; // 元素属性 content?: ArrayIconDescriptor; // 子元素递归描述符 }toSVGpackages/icon-helpers/src/toSVG.ts把描述符递归转换为 DOM 节点创建 SVG 命名空间下的元素根节点用getAttributes处理属性子节点直接用原始attrs然后递归追加content子节点。此函数依赖浏览器 DOMdocument.createElementNS适用于 React/Vue 等运行时渲染场景。toStringpackages/icon-helpers/src/toString.ts把描述符递归转换为 SVG 字符串配合导出的formatAttributes将属性对象拼成keyvalue形式。它不依赖 DOM适用于服务端渲染或模板字符串场景。指南同时指出如果你认为把getAttributes的逻辑内嵌进你自己的图标库更合适完全可以重新实现——因为carbon/icon-helpers定位是参考实现 共享工具而非强制依赖。落地参照carbon/icons-react是怎么做的carbon/icons-reactpackages/icons-react就是本指南模式在 React 生态中的实际产物可作为对照样板依赖上声明carbon/icon-helpers^10.82.0、prop-types并以react 16作为 peer dependency通过main/module字段分别指向lib/index.jsCommonJS与es/index.jsESM并标记sideEffects: false以配合 tree-shaking使用方式为具名导入import { Add } from carbon/icons-react默认 16px支持size属性切换 16/20/24/32px图标默认作为装饰性内容aria-hidden传入aria-label/aria-labelledby后由getAttributes逻辑自动切换为可读模式双色调图标如WarningFilled通过data-icon-pathinner-path属性选择器控制内层路径填充色。如果你的目标是 Vue 或其他框架完全可以仿照这一套carbon/icons数据源 tasks/build.js生成模块 carbon/icon-helpers处理属性 双入口 tree-shaking的组合拳快速搭建出与 Carbon 风格一致、对使用者友好的图标库。【免费下载链接】carbonA design system built by IBM项目地址: https://gitcode.com/GitHub_Trending/carbo/carbon创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表