常用准标准库

什么是准标准库?

准标准库是指虽然不是 JavaScript 官方标准,但在社区中被广泛使用、几乎成为事实标准的工具库和实用函数。这些库已经成为现代 JavaScript 开发不可或缺的一部分。

Lodash - JavaScript 实用工具库

Lodash 是最受欢迎的 JavaScript 实用工具库,提供了模块化、高性能的工具函数。

核心功能

// 数组操作
import { chunk, uniq, sortBy } from 'lodash';

const numbers = [1, 2, 3, 4, 5];
console.log(chunk(numbers, 2)); // [[1,2], [3,4], [5]]
console.log(uniq([1, 2, 2, 3])); // [1, 2, 3]
console.log(sortBy(users, 'age')); // 按年龄排序

// 对象操作
import { get, set, merge, omit } from 'lodash';

const user = { profile: { name: '张三' } };
console.log(get(user, 'profile.name')); // '张三'
console.log(get(user, 'profile.email', 'default@email.com')); // 默认值

const newUser = set({}, 'profile.age', 25);
const merged = merge({}, defaults, options);
const cleaned = omit(user, ['password', 'token']);

// 函数式编程
import { debounce, throttle, memoize } from 'lodash';

const debouncedSearch = debounce((query) => {
    fetchSearchResults(query);
}, 300);

const throttledScroll = throttle(() => {
    handleScroll();
}, 100);

const expensive = memoize((n) => {
    return complexCalculation(n);
});

Day.js - 轻量级日期库

Day.js 是 Moment.js 的轻量级替代品,API 相似但体积只有 2KB。

import dayjs from 'dayjs';
import relativeTime from 'dayjs/plugin/relativeTime';
import utc from 'dayjs/plugin/utc';

dayjs.extend(relativeTime);
dayjs.extend(utc);

// 基本使用
const now = dayjs();
console.log(now.format('YYYY-MM-DD HH:mm:ss')); // '2024-01-26 15:30:00'

// 操作日期
const tomorrow = dayjs().add(1, 'day');
const lastMonth = dayjs().subtract(1, 'month');

// 相对时间
console.log(dayjs('2024-01-01').fromNow()); // '25 days ago'

// 时区处理
const utcTime = dayjs.utc();
const localTime = dayjs.utc().local();

// 解析和格式化
const date = dayjs('2024-01-26', 'YYYY-MM-DD');
console.log(date.format('MMMM D, YYYY')); // 'January 26, 2026' (英文)
console.log(date.locale('zh-cn').format('YYYY年MM月DD日')); // 中文

clsx / classnames - CSS 类名处理

动态构建 CSS 类名字符串的工具库,特别适用于 React/Vue 等框架。

import clsx from 'clsx';
import { twMerge } from 'tailwind-merge';

// 基本使用
const className = clsx('foo', 'bar', 'baz');
// 'foo bar baz'

// 条件类名
const className = clsx({
    active: isActive,
    disabled: isDisabled,
    hidden: isHidden
});

// 数组和对象
const className = clsx('base-class', {
    'modifier-a': conditionA,
    'modifier-b': conditionB
}, 'another-class');

// 与 Tailwind CSS 结合使用 (推荐 twMerge)
const className = twMerge(
    clsx('px-4 py-2', {
        'bg-blue-500': isPrimary,
        'bg-gray-500': !isPrimary
    }),
    existingClassName // 自动处理冲突
);

Zod - 运行时类型验证

Zod 是 TypeScript-first 的模式声明和验证库,可以无缝集成到 TypeScript。

import { z } from 'zod';

// 定义模式
const UserSchema = z.object({
    name: z.string().min(2).max(50),
    age: z.number().min(0).max(150),
    email: z.string().email(),
    role: z.enum(['admin', 'user', 'guest']),
    preferences: z.object({
        notifications: z.boolean(),
        theme: z.enum(['light', 'dark', 'auto'])
    }).optional()
});

// 推断 TypeScript 类型
type User = z.infer;

// 验证数据
try {
    const result = UserSchema.parse({
        name: '张三',
        age: 25,
        email: 'test@example.com',
        role: 'user'
    });
    console.log('验证成功:', result);
} catch (error) {
    console.error('验证失败:', error.errors);
}

// 安全解析 (不抛出异常)
const result = UserSchema.safeParse(inputData);
if (result.success) {
    console.log(result.data);
} else {
    console.log(result.error);
}

// API 响应验证
const APIResponseSchema = z.object({
    success: z.boolean(),
    data: z.array(UserSchema),
    pagination: z.object({
        total: z.number(),
        page: z.number()
    })
});

Dexie.js - IndexedDB 封装

Dexie.js 是 IndexedDB 的简洁封装,让浏览器数据库操作变得简单。

import Dexie from 'dexie';

// 定义数据库
const db = new Dexie('MyDatabase');
db.version(1).stores({
    todos: '++id, text, completed, date',
    users: '++id, &email, name',
    posts: '++id, userId, title, date'
});

// 添加数据
await db.todos.add({
    text: '学习 JavaScript',
    completed: false,
    date: new Date()
});

// 查询数据
const allTodos = await db.todos.toArray();
const activeTodos = await db.todos
    .where('completed')
    .equals(false)
    .toArray();

// 分页查询
const page = await db.todos
    .where('date')
    .below(new Date())
    .reverse()
    .offset(0)
    .limit(10)
    .toArray();

// 更新数据
await db.todos.update(id, { completed: true });

// 删除数据
await db.todos.delete(id);
await db.todos.where('completed').equals(true).delete();

// 批量操作
await db.transaction('rw', db.todos, db.users, async () => {
    await db.todos.bulkAdd(todos);
    await db.todos.where('completed').equals(true).modify({ archived: true });
});

Nanoid - 唯一 ID 生成

Nanoid 是一个更小、更安全、更友好的唯一字符串 ID 生成器。

import { nanoid } from 'nanoid';

// 默认 21 字符 ID
const id = nanoid(); // 'V1StGXR8_Z5jdHi6B-myT'

// 自定义长度
const shortId = nanoid(10); // 'IRLo-Vl1dQ'

// 自定义字母表
const customId = nanoid(12, '0123456789ABCDEF');

// URL 安全
const urlId = nanoid(); // 自动使用 URL 安全字符

// 比较优势
// UUID: '550e8400-e29b-41d4-a716-446655440000' (36 字符)
// NanoID: 'V1StGXR8_Z5jdHi6B-myT' (21 字符)
// 更短、更快、更安全

// 使用场景
const todoId = nanoid();
const fileId = nanoid(16);
const sessionToken = nanoid(32);

Turndown - HTML 转 Markdown

Turndown 是一个将 HTML 转换为 Markdown 的工具库。

import TurndownService from 'turndown';

const turndownService = new TurndownService();

// 基本转换
const markdown = turndownService.turndown(`
    

标题

这是一段加粗文本

  • 列表项 1
  • 列表项 2
`); // 结果: // # 标题 // 这是一段**加粗**文本 // * 列表项 1 // * 列表项 2 // 自定义规则 turndownService.addRule('strikethrough', { filter: ['s', 'del'], replacement: (content) => `~~${content}~~` }); turndownService.addRule('highlight', { filter: (node, options) => { return node.nodeName === 'SPAN' && node.hasAttribute('data-highlight'); }, replacement: (content, node) => `==${content}==` });

DOMPurify - XSS 防护

DOMPurify 是一个快速的、只针对 XSS 的 DOM-only 清理器。

import DOMPurify from 'dompurify';

// 基本使用
const dirty = '<img src=x onerror=alert(1)//>';
const clean = DOMPurify.sanitize(dirty);
// 结果: '<img src=x>'

// 允许特定标签
const clean = DOMPurify.sanitize(dirty, {
    ALLOWED_TAGS: ['p', 'b', 'i', 'u', 'strong', 'em', 'a'],
    ALLOWED_ATTR: ['href', 'title', 'target']
});

// 清理 HTML 片段
const cleanHTML = DOMPurify.sanitize(userInputHTML);

// 使用钩子
DOMPurify.addHook('uponSanitizeAttribute', (node, data) => {
    // 自定义属性处理逻辑
    if (data.attrName === 'target') {
        data.attrValue = '_blank';
    }
});

// 实际应用示例
function renderUserContent(content) {
    return DOMPurify.sanitize(content, {
        ALLOWED_TAGS: ['p', 'br', 'strong', 'em', 'a', 'ul', 'ol', 'li'],
        ALLOWED_ATTR: ['href', 'target'],
        ALLOW_DATA_ATTR: false
    });
}

Copy-to-clipboard - 剪贴板操作

现代剪贴板 API 的简单封装。

import { clipboard } from '@nolebase/vitepress-plugin-inline-copy-to-clipboard';

// 复制文本
async function copyToClipboard(text) {
    try {
        await navigator.clipboard.writeText(text);
        console.log('复制成功!');
    } catch (err) {
        console.error('复制失败:', err);
        // 降级方案
        const textArea = document.createElement('textarea');
        textArea.value = text;
        document.body.appendChild(textArea);
        textArea.select();
        document.execCommand('copy');
        document.body.removeChild(textArea);
    }
}

// 现代浏览器内置 API (推荐)
async function copyText(text) {
    await navigator.clipboard.writeText(text);
}

async function pasteText() {
    const text = await navigator.clipboard.readText();
    return text;
}

选择合适的库

选择库的原则: