前端常用工具库
前端常用工具库
轮子别自己造, 这是我整理的前端常用库地图: 每个库解决什么痛、什么时候用、什么时候别用。Java 世界里有 Hutool 和 Commons, 前端对应的就是这一批。写熟了 05 篇那条 CRUD 链路再看这篇, 会知道每个库都往哪个位置装。
一张地图先对号:
代码块收起展开
数据处理 → Lodash-es 日期处理 → Day.js
HTTP 请求 → Axios 组合式工具 → VueUse
UI 组件 → Element Plus 图表 → ECharts
表单校验 → VeeValidate 拖拽排序 → SortableJS
虚拟滚动 → vue-virtual-scroller 富文本 → TipTapLodash-es: 工具函数百宝箱
解决的痛: 深拷贝、防抖节流、分组排序这些高频操作, 原生 JS 要么没有要么写着啰嗦。装 lodash-es 版本 (支持 tree-shaking, 打包时只带用到的函数, 别装老的 lodash):
代码块收起展开
import {
cloneDeep, // 深拷贝, 前端的 BeanUtils 加强版
debounce, // 防抖: 停止触发 N 毫秒后才执行 (搜索框标配)
throttle, // 节流: N 毫秒内最多执行一次 (滚动事件标配)
groupBy, // 分组, 相当于 Stream 的 Collectors.groupingBy
orderBy, // 多字段排序
uniq, // 去重
pick, omit // 挑选/排除对象属性
} from 'lodash-es'
const search = debounce(keyword => fetchList(keyword), 500) // 用户停手 500ms 才真发请求
const grouped = groupBy(users, 'role') // { admin: [...], user: [...] }
const sorted = orderBy(users, ['age', 'name'], ['desc', 'asc'])
const safe = omit(user, ['password']) // 传给日志前把敏感字段摘掉什么时候别用: 原生已经够用的场景 (arr.map、?. 可选链、structuredClone), 别为一个函数引一个库。
Day.js: 日期处理
原生 Date 的 API 是出了名的难用, Day.js 用 2KB 的体积给了一套人话 API (前端的 java.time 平替):
代码块收起展开
import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime' // 插件机制: 核心极小, 功能按需挂
import 'dayjs/locale/zh-cn'
dayjs.extend(relativeTime)
dayjs.locale('zh-cn')
dayjs().format('YYYY-MM-DD HH:mm:ss') // 格式化
dayjs('2026-01-15').add(7, 'day') // 日期运算
dayjs().startOf('day') // 今天 00:00:00
dayjs('2026-01-20').diff('2026-01-15', 'day') // 差值: 5
dayjs(post.createdAt).fromNow() // "3 小时前", 评论区标配一个用它练手的真实场景, 倒计时组件 (注意 01 篇讲过的定时器清理):
代码块收起展开
<script setup>
import { onMounted, onUnmounted, ref } from 'vue'
import dayjs from 'dayjs'
const props = defineProps({ endTime: String })
const countdown = ref('')
let timer = null
function update() {
const diff = dayjs(props.endTime).diff(dayjs(), 'second')
if (diff <= 0) {
countdown.value = '已结束'
clearInterval(timer)
return
}
const d = Math.floor(diff / 86400)
const h = Math.floor((diff % 86400) / 3600)
const m = Math.floor((diff % 3600) / 60)
countdown.value = `${d}天 ${h}小时 ${m}分 ${diff % 60}秒`
}
onMounted(() => {
update()
timer = setInterval(update, 1000)
})
onUnmounted(() => clearInterval(timer)) // 忘了这行就是内存泄漏
</script>
<template>
<div>{{ countdown }}</div>
</template>Axios: HTTP 请求
fetch 的进化版: 自动 JSON 转换、超时、拦截器、请求取消都是开箱即有。它在项目里的正确姿势是封装成统一请求层 (拦截器统一挂 token、统一处理 401、统一错误提示), 完整封装和分层思路在 05 篇写透了, 这里不重复。只记一条使用侧的样子:
代码块收起展开
// 业务代码里永远只碰封装后的 api 模块, 不直接 import axios
import { userApi } from '@/api/user'
const users = await userApi.getList({ page: 1, size: 10 })VueUse: 组合式函数军火库
Vue 生态最值得装的库。浏览器各种能力 (窗口尺寸、剪贴板、网络状态、localStorage) 的响应式封装, 全是 02 篇讲的 useXxx 组合式函数形态:
代码块收起展开
<script setup>
import { useClipboard, useLocalStorage, useOnline, useWindowSize } from '@vueuse/core'
const token = useLocalStorage('token', '') // 响应式 localStorage: 赋值自动存, 多标签页还同步
const { width } = useWindowSize() // 窗口宽度, 变了自动更新
const online = useOnline() // 断网检测
const { copy, copied } = useClipboard() // 一键复制 + 复制成功状态
</script>
<template>
<button @click="copy('要复制的内容')">{{ copied ? '已复制' : '复制' }}</button>
<p v-if="!online">网络断了</p>
</template>自己动手写 useXxx 之前先翻它的文档, 大概率已经有了, 而且人家处理好了清理和边界。
Element Plus: 按需导入
快速上手篇用的全量引入省事但打包全带上了。正式项目配自动按需导入, 用到哪个组件打包哪个:
npm install -D unplugin-vue-components unplugin-auto-import// vite.config.js
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
export default {
plugins: [
AutoImport({ resolvers: [ElementPlusResolver()] }), // ElMessage 这类函数自动引入
Components({ resolvers: [ElementPlusResolver()] }) // 模板里的 el-xxx 自动引入
]
}配完之后模板里直接写 <el-button>, 连 import 都不用写, 构建时插件自动补。第一次见到”没 import 也能用”别慌, 是这两个插件在背后干活。
ECharts: 图表
数据可视化的国民库。要点就一个模式: 拿个 div 当画布, init 后 setOption:
代码块收起展开
<script setup>
import { onMounted, onUnmounted, ref } from 'vue'
import * as echarts from 'echarts'
const chartRef = ref(null)
let chart = null
onMounted(() => {
chart = echarts.init(chartRef.value) // 必须等 DOM 挂载后才能 init
chart.setOption({
xAxis: { data: ['1月', '2月', '3月'] },
yAxis: {},
series: [{ type: 'bar', data: [5, 20, 36] }]
})
window.addEventListener('resize', chart.resize)
})
onUnmounted(() => {
window.removeEventListener('resize', chart.resize)
chart?.dispose() // 图表实例不销毁也是内存泄漏
})
</script>
<template>
<div ref="chartRef" style="width: 600px; height: 400px;"></div>
</template>ECharts 很重, 一定走路由懒加载或 defineAsyncComponent (02 篇), 别打进首屏。
VeeValidate + Yup: 脱离组件库的表单校验
用了 Element Plus 就用它自带的 rules (05 篇)。但不用组件库的场景 (自定义 UI、移动端), VeeValidate 配 Yup 声明式校验很舒服:
代码块收起展开
import * as yup from 'yup'
const schema = yup.object({
email: yup.string().required('邮箱不能为空').email('邮箱格式不正确'),
age: yup.number().min(18, '未满 18')
})schema 定义和后端的 Bean Validation 注解 (@NotNull/@Email) 思路完全一致, 声明规则, 框架负责执行。
挑库的原则
装库之前过四关: 最近还在维护吗 (GitHub 最后提交时间); 体积多大 (bundlephobia.com 查, 前端每 KB 都算流量); 支不支持按需引入; 原生是不是已经能做了。库解决的是效率, 不能解决理解, 每个引进来的库至少要知道它替我干了什么脏活, 不然出了 bug 连排查的入口都找不到。