我开源了一个 React 组件库,沉淀了多个高频组件和实用 Hooks

我开源了一个 React 组件库,沉淀了多个高频组件和实用 Hooks

大家好,我是木木剑光。

在业务开发过程中,我们经常会遇到一些重复性的需求和场景。比如瀑布流布局、无限滚动加载、竞态请求处理等等。每次都要重新写一遍类似的逻辑,不仅浪费时间,还容易出现 bug。

为了解决这个问题,我将团队在研发过程中沉淀下来的一些通用组件和 Hooks 整理成了一个开源组件库 ------ @mmjg/ui-components。今天想和大家分享一下这个库的设计思路和核心功能。

项目背景

在实际的业务场景中,我们发现有几类需求特别高频:

  1. 瀑布流布局:图片展示、商品列表等场景,需要高性能的虚拟滚动和响应式布局
  2. 3D 交互效果:一些创新性的 UI 交互,比如 3D 旋转菜单
  3. 流光效果:loading 状态的视觉优化
  4. 通用 Hooks:无限加载、元素尺寸监听、竞态请求处理等

这些需求看似简单,但要做得好、做得通用,还是有不少细节需要考虑的。比如瀑布流组件,需要考虑虚拟滚动性能、响应式布局、动态高度计算、滚动位置恢复等等。

核心组件介绍

1. Masonry 瀑布流组件

瀑布流布局在前端开发中非常常见,但要实现一个高性能、功能完善的瀑布流组件并不容易。我们的 Masonry 组件具有以下特性:

核心功能:

  • 虚拟滚动优化:只渲染可视区域内的元素,大幅提升性能
  • 响应式布局:支持断点配置,根据容器宽度自动调整列数
  • 无限加载:内置加载更多机制,支持阈值配置
  • 动态高度计算:自动计算每个 item 的高度,支持最小高度和额外高度配置
  • 滚动控制:支持滚动到顶部、滚动到指定元素等 API

关键实现原理:

虚拟滚动的核心思路是:计算可视区域的范围,只渲染这个范围内的元素。我们通过 useScrollViewArea hook 来实现:

typescript 复制代码
const [viewRange, reCalcViewRange] = useScrollViewArea({
    container: () => containerRef.current!,
    overscanHeight: 3000, // 预渲染区域
    loadMoreThreshold,
    onLoadMore,
});

这样,即使有成千上万条数据,也能保持流畅的滚动性能。

使用示例:

typescript 复制代码
import { Masonry } from '@mmjg/ui-components';

<Masonry
    items={images}
    columnCount={4}
    gap={8}
    itemRender={({ item, columnWidth, height }) => (
        <img
            src={item.src}
            style={{ width: columnWidth, height }}
        />
    )}
    onLoadMore={(check) => {
        // 加载更多数据
        loadMoreData().then(() => {
            check(); // 检查是否需要继续加载
        });
    }}
/>

这是一个创新的 3D 旋转菜单组件,支持鼠标拖拽交互。它的实现原理是通过 CSS 3D transform 和自定义 hook 实现的。

核心思路:

  1. 使用 rotateYtranslateZ 将菜单项分布在 3D 空间中
  2. 通过 useRotateAnimation hook 管理旋转动画
  3. 通过 useMouseDragOffset hook 处理鼠标拖拽交互
typescript 复制代码
export function Menu3D(props: Menu3DProps) {
    const { menus, rotateRadius = 200 } = props;
    const itemRotateDeg = 360 / menus.length;

    return (
        <div className={styles["component-menu3d"]}>
            <div className={styles["component-menu3d-rotate"]}>
                {menus.map((menu, index) => (
                    <div
                        key={menu.id}
                        style={{
                            transform: `rotateY(${itemRotateDeg * index}deg)
                                       translateZ(${rotateRadius}px)`
                        }}
                        onClick={() => onMenuClick(menu.id)}
                    >
                        {/* 菜单内容 */}
                    </div>
                ))}
            </div>
        </div>
    );
}

3. Shimmer 流光效果组件

这是一个简单但实用的流光效果组件,支持文本和容器两种模式。

typescript 复制代码
<Shimmer type="text" duration={2}>
    加载中...
</Shimmer>

<Shimmer type="container" width={10} color="#ffffff">
    <div style={{ width: 200, height: 100 }}>
        内容区域
    </div>
</Shimmer>

实用 Hooks 集合

除了 UI 组件,我们还沉淀了一些高频使用的 Hooks。

useAutoLoadMore:自动加载更多

这是一个解决无限加载场景痛点的 hook。

问题场景: 分页的 pageNum 是一个定值,但窗口的大小用户可以自由拖拽,因此可能出现窗口被拖拽得较大的情况下,滚动条消失,导致用户无法触发加载更多。

解决方案: 监听滚动容器大小变化,当滚动容器滚动条消失时,如果还有更多则触发加载更多。

typescript 复制代码
useAutoLoadMore(containerRef.current, {
    hasMore: hasMoreData,
    onLoadMore: (check) => {
        loadMoreData().then(() => {
            check(); // 检查是否需要继续加载
        });
    }
});

useBoxSizeObserver:监听元素尺寸

基于 ResizeObserver 实现的 hook,可以方便地监听元素尺寸变化。

typescript 复制代码
const [ref, boxSize] = useBoxSizeObserver();

return (
    <div ref={ref}>
        宽度: {boxSize.width}, 高度: {boxSize.height}
    </div>
);

UniqueRequest:竞态请求处理

这是一个通用模块,用于解决前端开发中常见的竞态问题。

问题场景: 在搜索场景中,用户快速输入时可能会发起多个请求,后面的请求可能先返回,导致显示的结果不是最新的。

解决方案: 使用 AbortController 取消之前的请求,只保留最新的请求结果。

typescript 复制代码
const uniqueRequest = new UniqueRequest();

// 在搜索场景中使用
const handleSearch = async (keyword: string) => {
    try {
        const result = await uniqueRequest.request(
            searchAPI,
            { keyword }
        );
        setSearchResult(result);
    } catch (error) {
        if (isAbortError(error)) {
            // 请求被取消,不做处理
            return;
        }
        // 处理其他错误
    }
};

技术栈和工程化

这个组件库基于以下技术栈:

  • React 18+:使用稳定的 React 特性
  • TypeScript:完整的类型支持
  • Rolldown:使用下一代打包工具,构建速度更快
  • Less:样式解决方案

在工程化方面,我们做了以下考虑:

  1. 类型安全:所有组件和 hooks 都有完整的 TypeScript 类型定义
  2. Tree Shaking:支持按需引入,减少打包体积
  3. 开发体验:支持 development 和 production 两种模式,开发时直接引用源码,生产环境使用构建产物

使用方式

组件库已经发布到 npm,可以直接安装使用:

bash 复制代码
pnpm add @mmjg/ui-components

更多详尽的使用指南请查看我们的官方文档

总结

@mmjg/ui-components 是我们在实际业务开发中沉淀下来的组件库,它解决了我们遇到的一些常见问题。虽然目前功能还不够完善,但我们会持续迭代和优化。

开源这个项目的目的,一方面是希望能帮助到有同样需求的开发者,另一方面也希望能收到社区的反馈,让这个库变得更好。

如果你对这个项目感兴趣,欢迎查看我们的文档源码,也欢迎提出 Issue 和 PR。


相关链接:

相关推荐
kyriewen1 小时前
DeepSeek API 高峰时段涨价 2 倍,便宜大碗的时代要结束了?
前端·ai编程·deepseek
Moment2 小时前
牛逼,NextJs 从 16.3 开始全面拥抱 Agent Native 🥰🥰🥰
前端·后端·面试
沸点小助手2 小时前
6月沸点活动获奖名单公示|本周互动话题上新🎊
前端·后端
Csvn2 小时前
React 19 `use()` 来了:以后数据加载可以不用 useEffect?
前端·react.js
没落英雄2 小时前
从零开始搭建一个 AI Agent —— LangChain + TypeScript 实战手记
前端·人工智能·架构
远航_2 小时前
git submodule
前端·后端·github
摸着石头过河的石头2 小时前
从 Webpack 到 RSBuild:前端构建工具的进化之路
前端
疯狂的魔鬼2 小时前
告别 boolean 地狱:一个文件上传组件的状态机实践
前端·设计
竹林8182 小时前
Solana DApp 开发踩坑实录:从零用 @solana/web3.js 实现链上数据查询与交易签名
前端·javascript