JSCommon系列 - 前端常用数据处理工具库

224 阅读7分钟

JSCommon 前端常用数据处理工具库

JSCommon 介绍

JavaScript/TypeScript 的简单工具集合,为前端应用提供你所需要的全部工具函数

开始使用:

npm install @wolforest/jscommon

项目地址: github.com/wolforest/j…

JSCommon 数据处理工具库概览

Lodash:一致性、模块化、高性能的 JavaScript 实用工具库

Lodash 是当前最流行的 JavaScript 工具库之一,它通过提供大量实用的工具函数,帮助开发者更轻松地处理数组、数字、对象、字符串等数据。

为什么选择 Lodash?

  1. 可靠性
    • 经过充分测试
    • 社区活跃,持续维护
    • 在 GitHub 上拥有超过 50k 的 star
  2. 性能优化
    • 优化的内部实现
    • 支持按需引入
    • 链式调用优化
  3. 一致性
    • 跨浏览器兼容
    • 统一的 API 设计
    • 可预测的函数行为

安装和使用

# npm 安装
npm install lodash

# yarn 安装
yarn add lodash

引入方式:

// 全量引入
import _ from 'lodash';

// 按需引入(推荐)
import get from 'lodash/get';
import debounce from 'lodash/debounce';

核心功能详解

1. 数组操作
// 数组分块
_.chunk(['a', 'b', 'c', 'd'], 2);
// => [['a', 'b'], ['c', 'd']]

// 数组去重
_.uniq([2, 1, 2, 3, 1]);
// => [2, 1, 3]

// 数组扁平化
_.flatten([1, [2, [3, [4]], 5]]);
// => [1, 2, [3, [4]], 5]

// 深度扁平化
_.flattenDeep([1, [2, [3, [4]], 5]]);
// => [1, 2, 3, 4, 5]
2. 对象操作
const obj = { a: [{ b: { c: 3 } }] };

// 安全获取深层属性
_.get(obj, 'a[0].b.c', 'default');
// => 3

// 属性不存在时返回默认值
_.get(obj, 'a[0].b.d', 'default');
// => 'default'
3. 函数优化
// 创建防抖函数
const debouncedSave = _.debounce(function(text) {
    console.log('保存:', text);
}, 1000);

// 创建节流函数
const throttledScroll = _.throttle(function() {
    console.log('滚动事件处理');
}, 100);
4. 字符串处理
// 首字母大写
_.capitalize('hello world');
// => 'Hello world'

// 驼峰转换
_.camelCase('hello-world');
// => 'helloWorld'

最佳实践

  1. 按需引入
// 好的做法
import get from 'lodash/get';
import debounce from 'lodash/debounce';

// 避免这样做
import _ from 'lodash';
  1. 使用类型检查
// 配合 TypeScript 使用
import { get } from 'lodash';
const value = get<string>(obj, 'path', 'default');

Immer:不可变数据处理利器

Immer 是一个能让你以更直观的方式处理不可变数据的 JavaScript 库。它的核心理念是通过修改当前状态的副本(draft)来生成下一个不可变状态,这使得复杂的状态更新变得简单直观。

为什么选择 Immer?

1. 主要优势
  • 直观的可变式写法
  • 自动处理不可变性
  • 更少的样板代码
  • 优秀的性能表现
  • 结构共享优化
  • TypeScript 友好
2. 适用场景
  • React 状态管理
  • Redux reducer 编写
  • 复杂数据结构更新
  • 嵌套对象的处理

基础使用

import produce from 'immer';

// 基础示例
const baseState = {
  users: [{id: 1, name: 'John'}],
  settings: {
    theme: 'light'
  }
};

const nextState = produce(baseState, draft => {
  // 直接修改 draft
  draft.users.push({id: 2, name: 'Jane'});
  draft.settings.theme = 'dark';
});

// baseState 保持不变
console.log(baseState.users.length); // 1
// nextState 包含新的更改
console.log(nextState.users.length); // 2

核心功能详解

1. 对象操作
const baseState = {
  name: 'John',
  age: 30,
  address: {
    city: 'New York',
    country: 'USA'
  }
};

const nextState = produce(baseState, draft => {
  // 修改属性
  draft.name = 'Jane';
  // 修改嵌套属性
  draft.address.city = 'Boston';
  // 添加新属性
  draft.email = 'jane@example.com';
  // 删除属性
  delete draft.age;
});
2. 数组操作
const baseState = {
  todos: [
    { id: 1, text: '学习 React', done: false },
    { id: 2, text: '学习 Immer', done: false }
  ]
};

const nextState = produce(baseState, draft => {
  // 添加元素
  draft.todos.push({ id: 3, text: '学习 Redux', done: false });
  
  // 删除元素
  draft.todos.splice(0, 1);
  
  // 修改元素
  const todo = draft.todos.find(todo => todo.id === 2);
  if (todo) {
    todo.done = true;
  }
});

实际应用场景

React 状态管理
import { useState } from 'react';
import produce from 'immer';

function TodoList() {
  const [todos, setTodos] = useState([
    { id: 1, text: '学习 React', done: false }
  ]);

  const addTodo = text => {
    setTodos(produce(draft => {
      draft.push({ id: Date.now(), text, done: false });
    }));
  };

  const toggleTodo = id => {
    setTodos(produce(draft => {
      const todo = draft.find(todo => todo.id === id);
      if (todo) {
        todo.done = !todo.done;
      }
    }));
  };
}

Ramda:函数式编程工具库

Ramda 是一个专注于函数式编程范式的 JavaScript 工具库。与 Lodash 等库不同,Ramda 的特点是所有函数都是自动柯里化的,且遵循数据不可变原则。

为什么选择 Ramda?

1. 函数式编程特性
  • 自动柯里化(Currying)
  • 数据不可变性(Immutability)
  • 函数组合(Composition)
  • Point-free 编程风格
  • 参数顺序优化(data-last)
2. 相比其他库的优势
  • 专注于函数式编程
  • 纯函数设计
  • 更好的函数组合支持
  • 更适合函数式编程范式

安装和使用

# npm 安装
npm install ramda

# yarn 安装
yarn add ramda

引入方式:

// 全量引入
import * as R from 'ramda';

// 按需引入(推荐)
import { pipe, map, filter } from 'ramda';

核心概念详解

1. 柯里化(Currying)
// 基础柯里化示例
const add = R.add;
const add5 = add(5);
add5(3); // => 8

// 多参数柯里化
const multiply = R.multiply;
const double = multiply(2);
double(4); // => 8
2. 函数组合
const calculateFinalScore = R.pipe(
  R.prop('scores'),           // 获取分数数组
  R.sum,                      // 求和
  R.divide(R.__, 100),        // 除以100
  R.multiply(10)              // 乘以10
);

const student = { scores: [70, 80, 90] };
calculateFinalScore(student); // => 24
3. 数据处理
// 过滤和映射
const numbers = [1, 2, 3, 4, 5];
const isEven = x => x % 2 === 0;
const double = x => x * 2;

const processNumbers = R.pipe(
  R.filter(isEven),
  R.map(double)
);

processNumbers(numbers); // => [4, 8]

最佳实践

Point-free 风格
// 不好的写法
const getTotal = data => R.sum(R.map(R.prop('amount'), data));

// 好的写法(Point-free)
const getTotal = R.pipe(
  R.map(R.prop('amount')),
  R.sum
);

Big.js:JavaScript 精确数学计算库

Big.js 是一个用于任意精度十进制算术的 JavaScript 库。它解决了 JavaScript 处理浮点数计算时的精度问题,特别适合金融计算等对精度要求较高的场景。

基础使用

基本运算
import Big from 'big.js'

// 加法
const sum = new Big('0.1')
  .plus('0.2')
  .toString() // '0.3'

// 减法
const difference = new Big('0.3')
  .minus('0.1')
  .toString() // '0.2'

// 乘法
const product = new Big('0.1')
  .times('0.2')
  .toString() // '0.02'

// 除法
const quotient = new Big('0.3')
  .div('0.1')
  .toString() // '3'
精度控制
import Big from 'big.js'

// 设置全局配置
Big.DP = 20  // 小数位数
Big.RM = 1   // 舍入模式 (0-3)

// 四舍五入到指定小数位
const num = new Big('1.23456789')
console.log(num.round(2).toString())  // '1.23'

高级特性

金融计算
class FinancialCalculator {
  // 计算复利
  static calculateCompoundInterest(
    principal: string,
    rate: string,
    years: number,
    compoundingFrequency: number = 1
  ): string {
    const p = new Big(principal)
    const r = new Big(rate).div(100)
    const n = new Big(compoundingFrequency)
    const t = new Big(years)
    
    // A = P(1 + r/n)^(nt)
    return p.times(
      new Big(1)
        .plus(r.div(n))
        .pow(n.times(t))
    ).round(2).toString()
  }
}

Number-Precision:JavaScript 精确数值计算库

number-precision 是一个轻量级的精确数值计算库,用于解决 JavaScript 中浮点数计算的精度问题。

精度问题本质

JavaScript 中的数字采用 IEEE 754 双精度浮点数标准,这导致了一些著名的精度问题:

0.1 + 0.2 = 0.30000000000000004
1.0 - 0.9 = 0.09999999999999998
0.105.toFixed(2) = '0.11'

这些问题源于二进制无法精确表示某些十进制小数,就像十进制无法精确表示 1/3 一样。

快速使用

import NP from 'number-precision'

// 去除浮点数计算误差
NP.strip(0.09999999999999998) // = 0.1

// 加法运算
NP.plus(0.1, 0.2)            // = 0.3
NP.plus(2.3, 2.4)            // = 4.7

// 减法运算
NP.minus(1.0, 0.9)           // = 0.1

// 乘法运算
NP.times(3, 0.3)             // = 0.9

实践应用

金融计算
class FinancialCalc {
  // 计算利息
  static calculateInterest(
    principal: number,
    rate: number,
    years: number
  ): number {
    return NP.times(
      principal,
      rate / 100,
      years
    );
  }
}

@wolforest/jscommon 数据处理架构设计与实现

@wolforest/jscommon 是一个统一的 JavaScript/TypeScript 工具库,它集成了上述多个常用的数据处理库,并提供了一致的 API 接口。通过这种方式,开发者可以使用一个依赖获得多个库的能力,同时享受粗颗粒度的 Tree-shaking 支持。

架构设计

@wolforest/jscommon 采用模块化设计,按功能领域划分不同的工具类:

  1. lang 模块:核心语言增强

    • ArrayUtil:数组操作工具
    • ObjectUtil:对象操作工具
    • StringUtil:字符串操作工具
    • NumberUtil:数字操作工具
    • DecimalUtil:精确数值计算工具
    • DateUtil:日期处理工具
    • FunctionUtil:函数工具
    • TypeUtil:类型判断工具
    • JSONUtil:JSON 处理工具
  2. storage 模块:存储相关工具

  3. net 模块:网络请求工具

  4. style 模块:样式处理工具

  5. debug 模块:调试工具

编码实现

以 DecimalUtil 为例,它封装了 Big.js 库,提供精确数值计算能力:

import Big from 'big.js';

export class DecimalUtil {
  // 创建 Big 实例
  static of(value: number | string): Big {
    return new Big(value);
  }

  // 精确加法
  static add(...numbers: (number | string)[]): string | number {
    return numbers.reduce((sum, num) => new Big(sum).plus(num).toString());
  }

  // 精确减法
  static subtract(minuend: number | string, subtrahend: number | string): string {
    return new Big(minuend).minus(subtrahend).toString();
  }

  // 精确乘法
  static multiply(...numbers: (number | string)[]): string | number {
    return numbers.reduce((product, num) => new Big(product).times(num).toString());
  }

  // 精确除法
  static divide(dividend: number | string, divisor: number | string, precision: number = 10): string {
    if (new Big(divisor).eq(0)) {
      throw new Error('Division by zero');
    }
    return new Big(dividend).div(divisor).toFixed(precision);
  }
}

ObjectUtil 则封装了 Lodash 的对象操作能力:

import {
  assign, merge, get, pick, omit, cloneDeep, isEqual
  // ...其他 lodash 函数
} from 'lodash-es';

export class ObjectUtil {
  static assign = assign;
  static merge = merge;
  static get = get;
  static pick = pick;
  static omit = omit;
  static cloneDeep = cloneDeep;
  static isEqual = isEqual;
  // ...其他方法
}

使用示例

import { ObjectUtil, DecimalUtil } from '@wolforest/jscommon';

// 对象操作
const obj = { a: { b: { c: 1 } } };
const value = ObjectUtil.get(obj, 'a.b.c', 0); // 1

// 精确数值计算
const price = DecimalUtil.multiply('10.5', '2'); // "21"
const total = DecimalUtil.add('99.9', '0.1'); // "100"

通过这种设计,@wolforest/jscommon 实现了:

  1. 统一的 API 入口:所有工具函数通过统一的命名空间访问
  2. 粗颗粒度的 Tree-shaking 支持:按需引入,减少打包体积
  3. 类型安全:完整的 TypeScript 类型定义
  4. 功能分类清晰:按功能模块组织代码
  5. 扩展性强:可以方便地添加新的工具函数和模块

这种架构使得开发者可以用统一的方式访问多个库的能力,简化了项目依赖管理,提高了开发效率。


项目地址: github.com/wolforest/j…

感谢阅读到最后,期待你的 github 🌟 鼓励!