常用准标准库

什么是准标准库?

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

Fetch API - 现代网络请求

Fetch API 是现代浏览器提供的原生网络请求接口,Promise-based,简洁强大。

// 基本 GET 请求
async function getData() {
    try {
        const response = await fetch('/api/users');
        if (!response.ok) {
            throw new Error(`HTTP error! status: ${response.status}`);
        }
        const data = await response.json();
        return data;
    } catch (error) {
        console.error('Fetch error:', error);
        throw error;
    }
}

// POST 请求
async function postData(url, data) {
    const response = await fetch(url, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
        },
        body: JSON.stringify(data),
    });
    return response.json();
}

// PUT 请求
async function updateData(url, data) {
    const response = await fetch(url, {
        method: 'PUT',
        headers: {
            'Content-Type': 'application/json',
        },
        body: JSON.stringify(data),
    });
    return response.json();
}

// DELETE 请求
async function deleteData(url) {
    const response = await fetch(url, {
        method: 'DELETE',
    });
    return response.status === 204;
}

Fetch 高级用法

// 添加查询参数
function fetchWithParams(url, params) {
    const queryString = new URLSearchParams(params).toString();
    const fullUrl = queryString ? `${url}?${queryString}` : url;
    return fetch(fullUrl);
}

// 使用示例
fetchWithParams('/api/users', { page: 1, limit: 10 });

// 设置超时
function fetchWithTimeout(url, options = {}, timeout = 5000) {
    return Promise.race([
        fetch(url, options),
        new Promise((_, reject) =>
            setTimeout(() => reject(new Error('Request timeout')), timeout)
        )
    ]);
}

// 使用示例
try {
    const data = await fetchWithTimeout('/api/data', {}, 3000);
} catch (error) {
    console.error('请求超时:', error);
}

// 请求取消 (AbortController)
function fetchWithCancel(url) {
    const controller = new AbortController();
    const signal = controller.signal;

    const promise = fetch(url, { signal });
    return {
        promise,
        cancel: () => controller.abort()
    };
}

// 使用示例
const { promise, cancel } = fetchWithCancel('/api/data');
setTimeout(() => cancel(), 1000); // 1秒后取消

Fetch 最佳实践

// 封装 Fetch API
class ApiClient {
    constructor(baseURL, options = {}) {
        this.baseURL = baseURL;
        this.defaultOptions = {
            headers: {
                'Content-Type': 'application/json',
            },
            ...options
        };
    }

    async request(endpoint, options = {}) {
        const url = `${this.baseURL}${endpoint}`;
        const config = { ...this.defaultOptions, ...options };

        try {
            const response = await fetch(url, config);

            if (!response.ok) {
                const error = new Error(`HTTP ${response.status}: ${response.statusText}`);
                error.status = response.status;
                throw error;
            }

            const contentType = response.headers.get('content-type');
            if (contentType && contentType.includes('application/json')) {
                return response.json();
            }
            return response.text();
        } catch (error) {
            console.error('API request failed:', error);
            throw error;
        }
    }

    get(endpoint, params = {}) {
        const queryString = new URLSearchParams(params).toString();
        const url = queryString ? `${endpoint}?${queryString}` : endpoint;
        return this.request(url, { method: 'GET' });
    }

    post(endpoint, data) {
        return this.request(endpoint, {
            method: 'POST',
            body: JSON.stringify(data)
        });
    }

    put(endpoint, data) {
        return this.request(endpoint, {
            method: 'PUT',
            body: JSON.stringify(data)
        });
    }

    delete(endpoint) {
        return this.request(endpoint, { method: 'DELETE' });
    }
}

// 使用示例
const api = new ApiClient('https://api.example.com');

const users = await api.get('/users');
const user = await api.get('/users/1');
const newUser = await api.post('/users', { name: '张三' });
const updatedUser = await api.put('/users/1', { name: '李四' });
await api.delete('/users/1');

Axios - HTTP 客户端

Axios 是基于 Promise 的 HTTP 客户端,支持浏览器和 Node.js,功能比 Fetch 更丰富。

// 安装
npm install axios

// 基本使用
import axios from 'axios';

// GET 请求
axios.get('/api/users')
    .then(response => console.log(response.data))
    .catch(error => console.error(error));

// POST 请求
axios.post('/api/users', {
    name: '张三',
    email: 'zhangsan@example.com'
})
.then(response => console.log(response.data));

// PUT 请求
axios.put('/api/users/1', { name: '李四' });

// DELETE 请求
axios.delete('/api/users/1');

Axios 高级功能

// 并发请求
function getMultipleData() {
    return axios.all([
        axios.get('/api/users'),
        axios.get('/api/posts'),
        axios.get('/api/comments')
    ]).then(axios.spread((users, posts, comments) => {
        return { users: users.data, posts: posts.data, comments: comments.data };
    }));
}

// 请求拦截器
axios.interceptors.request.use(
    config => {
        // 在发送请求之前做些什么
        const token = localStorage.getItem('token');
        if (token) {
            config.headers.Authorization = `Bearer ${token}`;
        }
        return config;
    },
    error => {
        // 对请求错误做些什么
        return Promise.reject(error);
    }
);

// 响应拦截器
axios.interceptors.response.use(
    response => {
        // 对响应数据做点什么
        return response;
    },
    error => {
        // 对响应错误做点什么
        if (error.response?.status === 401) {
            // 处理未授权
            window.location.href = '/login';
        }
        return Promise.reject(error);
    }
);

// 超时设置
axios.get('/api/data', { timeout: 5000 });

// 取消请求
const CancelToken = axios.CancelToken;
const source = CancelToken.source();

axios.get('/api/data', {
    cancelToken: source.token
});

source.cancel('Operation canceled by the user.');

Axios 实例封装

// 创建 Axios 实例
const api = axios.create({
    baseURL: 'https://api.example.com',
    timeout: 10000,
    headers: {
        'Content-Type': 'application/json',
        'X-Custom-Header': 'value'
    }
});

// 请求拦截器
api.interceptors.request.use(
    config => {
        const token = localStorage.getItem('token');
        if (token) {
            config.headers.Authorization = `Bearer ${token}`;
        }
        return config;
    }
);

// 响应拦截器
api.interceptors.response.use(
    response => response,
    error => {
        if (error.response?.status === 401) {
            window.location.href = '/login';
        }
        return Promise.reject(error);
    }
);

// 封装 API 方法
export const userApi = {
    getUsers: (params) => api.get('/users', { params }),
    getUser: (id) => api.get(`/users/${id}`),
    createUser: (data) => api.post('/users', data),
    updateUser: (id, data) => api.put(`/users/${id}`, data),
    deleteUser: (id) => api.delete(`/users/${id}`)
};

// 使用
import { userApi } from './api';

const users = await userApi.getUsers({ page: 1, limit: 10 });
const user = await userApi.getUser(1);
await userApi.deleteUser(1);

Fetch vs Axios

// Fetch - 原生,轻量
// ✅ 浏览器原生支持
// ✅ 无需额外依赖
// ❌ 需要手动处理很多细节
// ❌ 不支持请求取消 (需要 AbortController)
// ❌ 不支持请求/响应拦截器

fetch('/api/users')
    .then(response => {
        if (!response.ok) throw new Error('Error');
        return response.json();
    })
    .then(data => console.log(data));

// Axios - 功能丰富
// ✅ 自动 JSON 转换
// ✅ 请求/响应拦截器
// ✅ 请求取消
// ✅ 自动超时
// ✅ 更好的错误处理
// ✅ 并发请求支持
// ❌ 需要额外依赖 (~13KB)

axios.get('/api/users')
    .then(response => console.log(response.data))
    .catch(error => console.error(error));

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);
});

Lodash 深入使用

// 深度克隆
import { cloneDeep, merge } from 'lodash';

const original = {
    name: '张三',
    profile: {
        age: 25,
        address: { city: '北京' }
    }
};

const cloned = cloneDeep(original);
cloned.profile.address.city = '上海';
console.log(original.profile.address.city); // '北京' (原对象不变)

// 深度合并
const defaults = {
    theme: 'light',
    language: 'en',
    features: {
        notifications: true
    }
};

const options = {
    language: 'zh',
    features: {
        darkMode: true
    }
};

const merged = merge({}, defaults, options);
// { theme: 'light', language: 'zh', features: { notifications: true, darkMode: true } }

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日')); // 中文

date-fns - 现代日期库

date-fns 是现代 JavaScript 日期处理库,模块化、不可变、Tree-shakable。

// 安装
npm install date-fns

// 基本使用
import {
    format,
    addDays,
    subDays,
    differenceInDays,
    isAfter,
    isBefore,
    startOfDay,
    endOfDay,
    eachDayOfInterval,
    parseISO
} from 'date-fns';

const now = new Date();

// 格式化日期
console.log(format(now, 'yyyy-MM-dd')); // '2024-01-26'
console.log(format(now, 'yyyy年MM月dd日')); // '2024年01月26日'
console.log(format(now, 'yyyy-MM-dd HH:mm:ss')); // '2024-01-26 15:30:00'

// 日期操作
const tomorrow = addDays(now, 1);
const yesterday = subDays(now, 1);
const nextWeek = addDays(now, 7);

// 日期比较
const date1 = new Date('2024-01-01');
const date2 = new Date('2024-01-26');

console.log(isAfter(date2, date1)); // true
console.log(isBefore(date1, date2)); // true

// 日期差
const daysDiff = differenceInDays(date2, date1); // 25

date-fns 高级用法

// 时区处理 (需要额外安装)
import { zonedTimeToUtc, utcToZonedTime, format } from 'date-fns-tz';
import 'date-fns-tz/locale/zh-CN';

const timeZone = 'Asia/Shanghai';
const now = new Date();

// UTC 转换为时区
const zonedDate = utcToZonedTime(now, timeZone);

// 格式化时区日期
console.log(format(zonedDate, 'yyyy-MM-dd HH:mm:ss', { timeZone }));

// 国际化 (i18n)
import { format, zhCN } from 'date-fns/locale';
import { differenceInDays } from 'date-fns';

console.log(format(now, 'PPP', { locale: zhCN })); // '2024年1月26日'

// 相对时间
import { formatDistanceToNow, formatDistance } from 'date-fns';

const pastDate = new Date('2024-01-20');
console.log(formatDistanceToNow(pastDate, { locale: zhCN })); // '大约6天前'

// 日期解析
import { parse, parseISO, isValid } from 'date-fns';

const parsedDate = parse('2024-01-26', 'yyyy-MM-dd', new Date());
const isoDate = parseISO('2024-01-26T15:30:00');

if (isValid(parsedDate)) {
    console.log('日期有效');
}

// 业务日历
import {
    isWeekend,
    isMonday,
    isFriday,
    getWeek,
    getMonth,
    getYear,
    startOfMonth,
    endOfMonth
} from 'date-fns';

console.log(isWeekend(now)); // 是否周末
console.log(isMonday(now)); // 是否周一
console.log(getWeek(now)); // 周数
console.log(startOfMonth(now)); // 月初
console.log(endOfMonth(now)); // 月末

date-fns vs Day.js

// Day.js - 简单易用,类似 Moment.js
import dayjs from 'dayjs';

// 链式调用
const result = dayjs()
    .add(1, 'day')
    .subtract(1, 'month')
    .format('YYYY-MM-DD');

// ✅ API 简洁,易于理解
// ✅ 体积小 (~2KB)
// ✅ 链式调用
// ❌ 不是模块化的

// date-fns - 模块化,函数式
import { format, addDays, subMonths } from 'date-fns';

// 函数式调用
const result2 = format(
    subMonths(addDays(new Date(), 1), 1),
    'yyyy-MM-dd'
);

// ✅ 完全模块化
// ✅ 函数式,可组合
// ✅ 不可变
// ✅ Tree-shakable
// ❌ API 相对复杂
// ❌ 需要多个 import

Zepto - 轻量级 jQuery 替代

Zepto 是轻量级的现代浏览器库,API 与 jQuery 兼容,但体积更小 (~10KB)。

// 安装
npm install zepto

// 基本 DOM 操作
import $ from 'zepto';

// 选择元素
const $header = $('header');
const $items = $('.item');

// 修改内容
$('

新段落

').appendTo('#container'); $('.content').html('

标题

'); $('.title').text('新标题'); // 样式操作 $('.box').css('background', 'red'); $('.box').addClass('active'); $('.box').removeClass('inactive'); $('.box').toggleClass('highlight'); // 事件处理 $('.button').on('click', function() { console.log('按钮被点击'); }); $('.input').on('input', function(e) { console.log(e.target.value); }); // 动画 $('.box').animate({ left: '100px', opacity: 0.5 }, 300);

Zepto vs jQuery

// jQuery - 功能丰富,但体积大 (~34KB)
// ✅ 功能全面
// ✅ 广泛使用
// ✅ 文档完善
// ❌ 体积大
// ❌ 性能相对较低

// Zepto - 轻量级,现代浏览器
// ✅ 体积小 (~10KB)
// ✅ 性能好
// ✅ API 与 jQuery 兼容
// ✅ 移动端友好
// ❌ 不支持 IE
// ❌ 功能相对较少

// 选择合适的库
// - 老项目、需要 IE 支持: jQuery
// - 现代浏览器、移动端: Zepto 或 原生 API
// - 新项目: 推荐使用原生 API 或框架

选择合适的库