Ramda logo

Ramda

专为函数式编程设计的 JavaScript 实用工具库,以自动柯里化、数据最后(data-last)参数顺序和不可变数据为核心原则。

工具函数函数式编程柯里化不可变数据

Ramda 是从零设计的函数式编程工具库,所有函数默认自动柯里化、数据参数放在最后、不修改用户数据。通过 pipe/compose 构建声明式数据管道,提供 Lens(透镜)、Transducers(转换器)等高级功能。npm 周下载量约 1350 万,GitHub 约 24.1k Stars,在 React/Redux 生态中尤为流行。

Ramda

专为函数式编程设计的 JavaScript 实用工具库,以自动柯里化、数据最后(data-last)参数顺序和不可变数据为核心原则。

技术简介说明

Ramda 由 Scott Sauyet 和 Buzz Mosessian 于 2015 年发起,是一个从零设计的函数式编程工具库。与 Lodash 和 Underscore 这类多范式工具库不同,Ramda 将函数式编程理念贯穿到每一个 API 设计中:所有函数默认自动柯里化(auto-curried),数据参数始终放在最后(data-last),且所有函数都不会修改用户数据(纯函数、无副作用)。

Ramda 的核心价值在于它让 JavaScript 开发者能够以接近 Haskell、 Elm 等纯函数式语言的风格编写代码。通过柯里化和数据最后的参数顺序,开发者可以轻松使用函数组合(R.composeR.pipe)、偏应用(partial application)和 point-free 风格,构建出清晰的数据转换管道。这种风格在 Redux 生态(reselectcreateSlice 等)中尤为常见。

截至 2026 年,Ramda 的 npm 周下载量约 1350 万,GitHub 拥有约 24.1k 颗星。社区正在积极推动向 1.0 版本的演进("Road to 1.0" 讨论进行中),TypeScript 类型支持也在通过独立的 types-ramda 项目持续完善。

基本信息

  • 官网:https://ramdajs.com
  • GitHub:https://github.com/ramda/ramda
  • License:MIT
  • 最新版本:0.32.0(2025 年 10 月发布)
  • 主要维护者/公司:Scott Sauyet(核心维护者)、社区驱动
  • GitHub Stars:~24.1k
  • npm 周下载量:~1350 万
  • TypeScript 类型:@types/ramda v0.32.0(2026 年 6 月更新)/ types-ramda

快速上手

安装

# npm
npm install ramda
 
# yarn
yarn add ramda
 
# pnpm
pnpm add ramda
 
# CDN(浏览器直接使用)
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/ramda.min.js"></script>
 
# ES Module CDN
<script type="module">
  import * as R from 'https://cdn.jsdelivr.net/npm/[email protected]/es/index.js';
</script>

基础配置

// CommonJS
const R = require('ramda');
 
// ES Module(推荐)
import * as R from 'ramda';
 
// 按需引入(配合 tree-shaking)
import { pipe, map, filter, add } from 'ramda';

TypeScript 配置:

# 安装类型定义
npm install -D @types/ramda
# 或使用官方 types-ramda 包(更精确)
npm install -D types-ramda
// tsconfig.json
{
  "compilerOptions": {
    // 如果使用 types-ramda
    "types": ["types-ramda"]
  }
}

最小示例

import * as R from 'ramda';
 
// 自动柯里化 — 所有函数天然支持偏应用
const double = R.multiply(2);
double(10); // 20
 
// 数据最后(data-last)— 利于函数组合
const getNames = R.map(R.prop('name'));
const users = [{ name: 'Alice' }, { name: 'Bob' }];
getNames(users); // ['Alice', 'Bob']
 
// 函数管道 — 声明式的数据转换
const processUsers = R.pipe(
  R.filter(R.propEq('active', true)),
  R.map(R.prop('name')),
  R.join(', ')
);
 
const result = processUsers(users); // 'Alice, Bob'
 
// 不可变操作 — 不修改原始数据
const addItem = R.append({ id: 3, name: 'Charlie' });
const newList = addItem(users); // 返回新数组,users 不变
 
// 模式匹配
const grade = R.cond([
  [R.gte(R.__, 90), R.always('A')],
  [R.gte(R.__, 80), R.always('B')],
  [R.gte(R.__, 70), R.always('C')],
  [R.T, R.always('F')]
]);
grade(85); // 'B'

核心概念与架构

三大设计原则

Ramda 的每个 API 都严格遵循三条核心原则,这是它与 Lodash/Underscore 的根本区别:

  1. 自动柯里化(Auto-currying):每个函数都自动支持柯里化,传入部分参数返回新函数,直到参数齐全才执行。
  2. 数据最后(Data-last):被操作的数据参数放在最后,使得函数可以自然地作为管道中的一个步骤组合。
  3. 无副作用(No mutation):所有函数都是纯函数,不修改输入数据,始终返回新值。

函数组合(Composition)

Ramda 的核心操作是函数组合:

// R.compose — 从右向左组合
const transform = R.compose(
  R.join(', '),
  R.map(R.toUpper),
  R.filter(R.startsWith('A'))
);
transform(['Alice', 'Bob', 'Anna']); // 'ALICE, ANNA'
 
// R.pipe — 从左向右组合(更易读)
const transform2 = R.pipe(
  R.filter(R.startsWith('A')),
  R.map(R.toUpper),
  R.join(', ')
);

Point-free 风格

Ramda 鼓励 point-free(无点)风格——函数定义不显式引用参数:

// 有点风格
const getActiveNames = (users) => R.map(R.prop('name'), R.filter(R.propEq('active', true), users));
 
// Point-free 风格 ✅
const getActiveNames = R.pipe(
  R.filter(R.propEq('active', true)),
  R.map(R.prop('name'))
);

占位符(Placeholder)

使用 R.__ 占位符支持灵活的偏应用:

const subtractFrom10 = R.subtract(10);  // 固定第一个参数
const subtract10 = R.subtract(R.__, 10); // 固定第二个参数
 
subtractFrom10(3); // 7 (10 - 3)
subtract10(15);    // 5 (15 - 10)

核心特性

  • 自动柯里化:所有 250+ 个函数默认自动柯里化,无需额外配置,是函数式编程的基石。
  • 数据最后参数顺序:所有函数的被操作数据参数放在最后,天然适合 pipe/compose 组合。
  • 不可变数据:所有函数都是纯函数,不修改输入数据,避免副作用导致的 bug。
  • 函数管道R.pipeR.compose 提供强大的函数组合能力,构建声明式数据管道。
  • Lens(透镜)R.lensR.viewR.setR.over 提供不可变的数据访问和更新机制,优雅处理嵌套对象。
  • 模式匹配R.condR.whenR.ifElseR.match 提供声明式的条件分支逻辑。
  • 关系代数R.innerJoinR.groupByR.sortByR.unionR.intersection 等集合操作。
  • Transducers(转换器)R.intoR.transduce 支持高效的惰性管道,避免中间数组创建。
  • Fantasy Land 兼容:与 Fantasy Land 规范兼容,可与其他函数式库(如 folktalesanctuary)无缝集成。
  • 无副作用设计:整个库的设计理念确保函数不会修改外部状态,代码行为可预测。

生态图

核心依赖

Ramda 本身零外部依赖,完全自包含。

生态插件与扩展

  • ramda-adjunct:社区维护的扩展工具集,补充 Ramda 缺失的实用方法(如 RA.isNotNilRA.renameKeys 等)。
  • ramda-fantasy:Fantasy Land 兼容类型(Maybe、Either、Future 等),与 Ramda 无缝集成。
  • ramda-sandbox:在线实验环境,用于测试 Ramda 代码片段。
  • eslint-plugin-ramda:ESLint 插件,帮助避免常见的 Ramda 使用陷阱。
  • types-ramda:官方维护的 TypeScript 类型定义,比 @types/ramda 更精确,支持柯里化类型推导。

相关项目

  • Sanctuary:类型安全的函数式编程库,受 Ramda 启发但更强调类型正确性。
  • Folktale:JavaScript 函数式编程标准库,提供 Maybe、Result 等代数数据类型。
  • Immutable.js:不可变数据结构库,可与 Ramda 配合使用处理大型数据集。

社区生态

Ramda 在函数式编程社区中有稳定的影响力,在 React + Redux 生态中尤其流行。O'Reilly 和 Coursera 在 2026 年仍在发布 Ramda 教程课程,说明其教育价值持续被认可。

适用场景

  • React/Redux 状态管理:在 Redux reducer 中处理不可变状态更新、selector 计算,Ramda 的纯函数特性完美契合。
  • 复杂数据转换管道:需要多步骤的数据过滤、映射、聚合操作时,R.pipe 提供清晰的声明式管道。
  • 函数式编程实践:学习和实践函数式编程概念(柯里化、组合、函子、monad)的理想工具库。
  • 不可变数据处理:需要确保数据不被意外修改的场景,如事件溯源(Event Sourcing)、CQRS 架构。
  • 嵌套对象操作:使用 Lens(透镜)优雅处理深层嵌套对象的读取和更新,避免展开运算符嵌套地狱。
  • 领域驱动设计(DDD):函数式管道适合表达业务流程中的数据转换规则。
  • 数据科学与 ETL:使用 Transducers 高效处理大数据集的转换和聚合,避免中间数组的内存开销。

开发与工程化

开发流程

# 克隆仓库
git clone https://github.com/ramda/ramda.git
cd ramda
 
# 安装依赖
npm install
 
# 运行测试
npm test
 
# 构建(生成 ESM、CJS、UMD 格式)
npm run build
 
# 生成文档
npm run docs

源码结构

ramda/
├── source/           # 各方法的独立源码(ES Module)
│   ├── pipe.js
│   ├── compose.js
│   ├── map.js
│   └── ...
├── es/               # ES Module 构建输出
├── dist/             # UMD 构建输出
├── package.json
└── test/             # 测试用例

工程化最佳实践

  • 按需引入:使用具名导入配合 tree-shaking:
    import { pipe, map, filter, prop } from 'ramda';
  • TypeScript 优先:使用 types-ramda 获得更精确的柯里化类型推导。
  • ESM 构建:在 Vite、Rollup 等现代构建工具中,Ramda 的 ESM 输出可以被有效 tree-shaking。
  • 预构建别名:如果只需要少量方法,可以配置构建工具直接解析到 ramda/es/ 下的单独文件。

CI/CD 集成

  • 在 CI 中配置 npm test 和类型检查。
  • Ramda 作为纯函数库,测试覆盖率很高(>95%),可直接集成到质量门禁中。
  • 使用 eslint-plugin-ramda 在 lint 阶段检测常见使用错误。

性能与安全

性能特点

  • 一般性能:由于自动柯里化和函数包装开销,Ramda 在简单操作(如 mapfilter)上比原生方法和 Lodash 慢 2-5 倍。
  • Transducers 优势:对于多步骤管道操作,使用 R.transduce 可以避免中间数组创建,整体性能优于逐步骤操作。
  • Lens 开销:Lens 操作在频繁调用场景下有一定性能开销,不适合在渲染循环或高频事件处理中使用。

基准测试参考

操作RamdaLodash原生 JS
map(1000 元素)~3.2ms~1.1ms~0.8ms
filter(1000 元素)~2.8ms~0.9ms~0.7ms
pipe(5 步管道)~4.5ms~3.0ms(无管道)~5.0ms(手写循环)
Transduce(5 步管道)~2.0ms

注:数据为近似值,具体取决于运行环境和数据复杂度。

安全注意事项

  • Ramda 的纯函数设计天然避免了大部分安全问题(无原型污染、无 eval 使用)。
  • 未发现已公开的 CVE 漏洞。
  • 使用 R.mergeDeepLeftR.mergeDeepRight 等深度合并方法时,注意处理包含 __proto__ 等原型链属性的恶意输入。

优化建议

  • 对高频调用的操作,考虑使用原生方法或 Lodash 替代。
  • 使用 R.transduce 替代多步 R.pipe,减少中间数组的内存分配。
  • 避免在 React 渲染循环或 requestAnimationFrame 回调中使用 Ramda 方法,改用原生代码。
  • 对于大型不可变数据结构操作,考虑结合 Immutable.js 使用。

技术对比

特性RamdaLodashUnderscore.js
设计哲学纯函数式实用主义(多范式)实用主义
自动柯里化✅ 所有函数❌(lodash/fp 支持)
参数顺序数据最后数据优先数据优先
不可变性强制可选(lodash/fp)不保证
纯函数强制部分部分
方法数量250+300+100+
包体积 (gzip)~12 KB(按需引入更小)~33 KB~16 KB
性能中等(curry 开销)中等
函数组合pipe/compose(核心特性)flow/flowRight(lodash/fp)compose
Lens(透镜)✅ 内置
Transducers✅ 内置
TypeScript 支持@types/ramda / types-ramda内置@types/underscore
Fantasy Land✅ 兼容
学习曲线高(需要 FP 基础)
npm 周下载量~1350 万~1.2 亿~2300 万

选型建议

  • 需要函数式编程范式、不可变数据、柯里化 → Ramda
  • 需要全面的工具集合和高性能 → Lodash
  • 维护遗留项目或仅需少量方法 → Underscore.js
  • 使用 TypeScript 且重视类型推导 → Lodash(内置类型)或 Ramda + types-ramda

最佳实践

生产环境建议

  1. 拥抱 Point-free 但保持可读性

    // 可读的 point-free ✅
    const getActiveUserEmails = R.pipe(
      R.filter(R.propEq('active', true)),
      R.map(R.prop('email')),
      R.reject(R.isNil)
    );
     
    // 过度 point-free ❌ — 难以理解
    const bad = R.pipe(R.filter(R.pipe(R.prop('active'), R.equals(true))), R.map(R.prop('email')));
  2. 使用 Lens 处理嵌套对象

    const nameLens = R.lensProp('name');
    const addressLens = R.lensProp('address');
    const cityLens = R.lensPath(['address', 'city']);
     
    // 深层更新 — 比展开运算符清晰得多
    const updated = R.set(cityLens, 'Shanghai', user);
  3. Transducers 处理大数据集

    // 避免创建中间数组
    const xf = R.compose(
      R.map(R.prop('name')),
      R.filter(R.startsWith('A'))
    );
    const result = R.into([], xf, largeUserList);
  4. 条件逻辑使用 cond

    const categorize = R.cond([
      [R.propEq('role', 'admin'), R.always('administrator')],
      [R.propEq('role', 'editor'), R.always('content_manager')],
      [R.T, R.always('viewer')]
    ]);
  5. 与 React 结合使用

    import * as R from 'ramda';
     
    // selector — 在 useSelector 中使用
    const selectActiveUsers = R.pipe(
      R.prop('users'),
      R.filter(R.propEq('active', true)),
      R.sortBy(R.prop('name'))
    );
     
    // 不可变状态更新 — 在 reducer 中使用
    const reducer = (state, action) => R.mergeRight(state, { items: action.payload });

常见陷阱

  • 柯里化性能开销:对于高频调用(如渲染循环中)的简单操作,避免使用 Ramda 的柯里化函数,改用原生方法。
  • 类型推导不完整@types/ramda 对复杂柯里化的类型推导不够精确,建议使用 types-ramda 或显式标注类型。
  • 过度 point-free:过度追求 point-free 风格会降低代码可读性。在管道步骤超过 3-4 步时,考虑添加注释或适当使用具名参数。
  • R.__ 占位符的混淆:过度使用占位符会使参数绑定难以追踪,优先使用固定参数位置的函数。

技术局限与边界

已知限制

  1. 性能开销:自动柯里化和函数包装带来了不可避免的性能开销。在简单操作上比 Lodash 慢 2-5 倍,比原生方法慢 3-10 倍。

  2. 学习曲线陡峭:需要掌握函数式编程基础概念(柯里化、组合、函子、monad 等)才能有效使用。对团队新人有较高的入门成本。

  3. TypeScript 类型推导不完善:虽然 types-ramda 有所改善,但复杂柯里化场景的类型推导仍有局限,有时需要手动标注类型。

  4. 尚未达到 1.0:截至 2026 年仍为 0.x 版本,可能存在轻微的 API 变动(虽然 0.30+ 已经相对稳定)。

  5. 社区规模有限:相比 Lodash 的 1.2 亿周下载量,Ramda 的 1350 万下载量意味着遇到问题时可参考的资源更少。

  6. 浏览器调试:柯里化函数在调试器中的堆栈追踪较复杂,不如普通函数直观。

不适用场景

  • 性能极度敏感的实时渲染或游戏逻辑(使用原生方法或专门的性能优化库)。
  • 团队成员对函数式编程不熟悉的短期项目(学习成本可能超过收益)。
  • 需要大量异步操作的场景(Ramda 不提供内置的异步工具,需结合 Task/Future 等外部类型)。
  • 需要与 TypeScript 严格模式完美集成的项目(类型推导的局限可能增加开发摩擦)。

迁移注意事项

  • 从 Lodash 迁移到 Ramda 时,注意参数顺序的变化(数据从第一个变为最后一个)。
  • Ramda 的 R.equals() 与 Lodash 的 _.isEqual() 行为不完全一致(如对 NaN 的处理)。
  • 迁移到原生 JS 时:R.map(fn, arr)arr.map(fn)R.filter(fn, arr)arr.filter(fn) 等。
  • 渐进式迁移:可以从 Ramda 管道逐步替换为 Array.prototype 链式调用,再按需提取为独立函数。

学习资源

2026 年现状

最新版本

  • 当前版本:0.32.0(2025 年 10 月发布)。
  • TypeScript 类型@types/ramda v0.32.0(2026 年 6 月更新)。
  • 上次重大更新:0.30.0(2024 年 5 月),移除了多个长期弃用的函数。

发展趋势

  • Road to 1.0:社区正在积极推进 1.0 版本发布(Discussion #3397),预计将聚焦于 API 稳定性和 TypeScript 类型改进。
  • TypeScript 优先types-ramda 项目独立维护,正在提供更精确的柯里化类型推导,目标是在 1.0 时提供一流的 TypeScript 支持。
  • 教育领域持续增长:O'Reilly 和 Coursera 在 2026 年发布了新的 Ramda 课程,说明其在函数式编程教育领域仍有活力。

社区活跃度

  • GitHub 有约 121 个 open issues 和 26 个 open PRs,社区讨论活跃。
  • npm 周下载量约 1350 万,保持稳定。
  • 在 React/Redux 生态中仍是函数式编程的首选工具库。
  • 相比 Lodash 下载量下降明显(Lodash ~1.2 亿/周),但目标用户群不同。

未来路线图

  • 1.0 版本:预计将包含 API 冻结、TypeScript 类型内置支持和更清晰的模块边界。
  • 移除遗留方法:0.30+ 已开始移除了废弃函数,后续版本将继续清理。
  • 更好的 ESM 支持:优化 tree-shaking 和按需引入体验。

选型建议(2026)

  • 新项目:如果团队熟悉函数式编程,Ramda 是优秀的选择;如果团队 FP 经验有限,考虑 Lodash 或原生 JS。
  • React/Redux 项目:Ramda 在状态管理和 selector 场景中有独特优势。
  • TypeScript 项目:配合 types-ramda 使用,但需注意复杂柯里化场景的类型标注。
  • 性能敏感项目:评估 Ramda 的性能开销是否可接受,高频操作考虑使用原生方法。