前端 utils 工具函数规范:拆分 / 命名 / 复用全指南,避开全局污染等高频坑|编码语法规范篇

【前端工具函数规范】+【中后台前端开发】:从抽离逻辑到目录规范,彻底搞懂utils工具函数的落地写法,避开全局污染、命名混乱等高频坑!

📑 文章目录

  • 一、开篇:为什么要单独讲「工具函数」?
  • 二、核心概念:什么是「工具函数」?
  • [三、怎么拆分:按领域划分 utils](#三、怎么拆分:按领域划分 utils)
    • [3.1 推荐目录结构(以 Vue 为例)](#3.1 推荐目录结构(以 Vue 为例))
    • [3.2 拆分原则(三个问题)](#3.2 拆分原则(三个问题))
    • [3.3 完整示例:format.js](#3.3 完整示例:format.js)
  • 四、命名规范:一眼能看懂用途
    • [4.1 基本规则](#4.1 基本规则)
    • [4.2 常用动词前缀](#4.2 常用动词前缀)
    • [4.3 命名示例](#4.3 命名示例)
  • 五、怎么复用:入口统一、按需引入
    • [5.1 统一入口 index.js](#5.1 统一入口 index.js)
    • [5.2 使用方式(两种)](#5.2 使用方式(两种))
    • [5.3 避免的用法](#5.3 避免的用法)
  • 六、避免全局污染:作用域与模块化
    • [6.1 什么是全局污染?](#6.1 什么是全局污染?)
    • [6.2 正确做法:ES Module](#6.2 正确做法:ES Module)
    • [6.3 旧项目怎么办?](#6.3 旧项目怎么办?)
  • [七、完整实战:从零搭一套 utils](#七、完整实战:从零搭一套 utils)
    • [7.1 format.js](#7.1 format.js)
    • [7.2 validate.js](#7.2 validate.js)
    • [7.3 storage.js](#7.3 storage.js)
    • [7.4 index.js(统一导出)](#7.4 index.js(统一导出))
    • [7.5 在 Vue 组件中使用](#7.5 在 Vue 组件中使用)
  • 八、常见踩坑总结
  • 九、小结
  • [🔍 系列模块导航](#🔍 系列模块导航)

同学们好,我是 Eugene(尤金),一名多年中后台前端开发工程师。

(Eugene 发音 /juːˈdʒiːn/,大家怎么顺口怎么叫就好)

很多前端开发者都会遇到一个瓶颈:

代码能跑,但不够规范;功能能实现,但维护起来特别痛苦;一个人写没问题,一到团队协作就各种混乱、踩坑、返工。

想写出干净、优雅、可维护 的专业代码,靠的不是天赋,而是体系化的规范 + 真实实战经验

这一系列《前端规范实战》,我会用大白话 + 真实业务场景,不讲玄学、不堆理论,只分享能直接落地的规范、标准与避坑指南。

帮你从「会写代码」真正升级为「会写优质、可维护、团队级别的代码」。


一、开篇:为什么要单独讲「工具函数」?

很多人学 JavaScript 会用 Array.mapString.split,但一提到「写自己的工具函数」就懵了------什么时候该写?写在哪?怎么命名、怎么拆分、怎么复用?

这篇文章的目的很简单:

让你在日常开发中,清楚地知道:什么时候抽工具函数、抽出来后放哪、叫什么名字、怎么用,顺带帮你避开「全局污染」「命名冲突」这些坑。

适用读者:

  • 会写 JS,但对「函数组织」概念不太清晰的
  • 从零开始学前端,想建立良好习惯的
  • 有经验但想统一自己团队/项目规范的

下面按:为什么要拆 → 怎么拆 → 怎么命名 → 怎么复用 → 怎么避免全局污染 来写。

[⬆ 返回目录](#⬆ 返回目录)


二、核心概念:什么是「工具函数」?

工具函数(utility function) 指的是:只做一件事、与业务逻辑解耦、可在多处复用的纯函数

常见特征:

特征 说明
单一职责 每个函数只负责一件事
无副作用 不直接改 DOM、不发请求,输入相同则输出相同
可复用 多个页面、组件都能用
可测试 输入输出明确,容易写单测

示例:符合 vs 不符合

js 复制代码
// ❌ 不是好的工具函数:掺杂业务逻辑、改全局状态
function handleUserSubmit(userId, formData) {
  document.getElementById('submitBtn').disabled = true;  // 副作用
  const res = await fetch(`/api/user/${userId}`);         // 业务耦合
  return res.json();
}

// ✅ 符合工具函数:纯逻辑、无副作用
function formatDate(timestamp, format = 'YYYY-MM-DD') {
  const date = new Date(timestamp);
  const year = date.getFullYear();
  const month = String(date.getMonth() + 1).padStart(2, '0');
  const day = String(date.getDate()).padStart(2, '0');
  return format.replace('YYYY', year).replace('MM', month).replace('DD', day);
}

[⬆ 返回目录](#⬆ 返回目录)


三、怎么拆分:按领域划分 utils

不要把所有工具函数丢进一个巨大的 utils.js,而是按功能领域拆成多个模块。

3.1 推荐目录结构(以 Vue 为例)

复制代码
src/
├── utils/
│   ├── index.js          # 统一导出入口
│   ├── format.js         # 格式化:日期、金额、手机号等
│   ├── validate.js       # 校验:手机、邮箱、身份证等
│   ├── storage.js        # 本地存储封装
│   ├── request.js        # 请求封装(可单独成 api 目录)
│   └── string.js         # 字符串:截断、脱敏等

思路:

  • 每个文件只处理一类问题
  • 业务扩展时,优先在对应文件里加函数,不够再新建文件

[⬆ 返回目录](#⬆ 返回目录)

3.2 拆分原则(三个问题)

拆之前可以问自己:

  1. 这个逻辑会不会在多个地方用到?会 → 抽成工具函数。
  2. 它属于哪一类?(格式化 / 校验 / 存储 / 请求 / 字符串等)按类别放进对应文件。
  3. 它是否依赖具体业务?依赖业务 → 放业务模块;不依赖 → 放 utils。

[⬆ 返回目录](#⬆ 返回目录)

3.3 完整示例:format.js

js 复制代码
/**
 * 格式化相关工具函数
 * @file format.js
 */

/**
 * 格式化日期
 * @param {number|string|Date} timestamp - 时间戳或日期对象
 * @param {string} format - 输出格式,如 'YYYY-MM-DD'、'YYYY/MM/DD HH:mm'
 * @returns {string}
 */
export function formatDate(timestamp, format = 'YYYY-MM-DD') {
  const date = timestamp instanceof Date ? timestamp : new Date(timestamp);
  if (isNaN(date.getTime())) return '';

  const pad = (n) => String(n).padStart(2, '0');
  const map = {
    YYYY: date.getFullYear(),
    MM: pad(date.getMonth() + 1),
    DD: pad(date.getDate()),
    HH: pad(date.getHours()),
    mm: pad(date.getMinutes()),
    ss: pad(date.getSeconds()),
  };

  return format.replace(/YYYY|MM|DD|HH|mm|ss/g, (key) => map[key] || key);
}

/**
 * 格式化金额(千分位)
 * @param {number} num - 数字
 * @param {number} decimals - 小数位数
 * @returns {string}
 */
export function formatMoney(num, decimals = 2) {
  const fixed = Number(num).toFixed(decimals);
  const [int, dec] = fixed.split('.');
  const intWithComma = int.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
  return dec ? `${intWithComma}.${dec}` : intWithComma;
}

[⬆ 返回目录](#⬆ 返回目录)


四、命名规范:一眼能看懂用途

4.1 基本规则

规则 说明 示例
动词开头 表示「做什么」 formatDatevalidateEmail
见名知意 名字要能反映功能 formatMoney 而不是 fm
避免否定 少用 isNotXxx,多用肯定 isEmpty 而不是 isNotEmpty(或 hasContent

[⬆ 返回目录](#⬆ 返回目录)

4.2 常用动词前缀

复制代码
format  - 格式化:formatDate, formatPhone
validate/check - 校验:validateEmail, checkIdCard
get     - 获取:getStorage, getQuery
set     - 设置:setStorage
parse   - 解析:parseQuery, parseJSON
debounce/throttle - 节流防抖

[⬆ 返回目录](#⬆ 返回目录)

4.3 命名示例

js 复制代码
// ✅ 清晰
function formatPhone(phone) { /* 格式化手机号 */ }
function validateEmail(email) { /* 校验邮箱 */ }
function getStorage(key) { /* 获取本地存储 */ }

// ❌ 模糊
function phone(p) { /* 做什么不清楚 */ }
function check(e) { /* 检查什么不清楚 */ }
function util1(x) { /* 完全无意义 */ }

[⬆ 返回目录](#⬆ 返回目录)


五、怎么复用:入口统一、按需引入

5.1 统一入口 index.js

把所有 utils 在一个入口里导出,方便统一管理和按需引入:

js 复制代码
/**
 * utils 统一导出
 * 使用方式:import { formatDate, validateEmail } from '@/utils'
 */

// 格式化
export { formatDate, formatMoney } from './format';

// 校验
export { validateEmail, validatePhone } from './validate';

// 存储
export { getStorage, setStorage, removeStorage } from './storage';

[⬆ 返回目录](#⬆ 返回目录)

5.2 使用方式(两种)

js 复制代码
// 方式一:按需引入(推荐,利于 tree-shaking)
import { formatDate, validateEmail } from '@/utils';

// 方式二:引入整个模块(适合一次性用很多)
import * as formatUtils from '@/utils/format';
formatUtils.formatDate(1234567890);

[⬆ 返回目录](#⬆ 返回目录)

5.3 避免的用法

js 复制代码
// ❌ 全局挂在 window 上,容易污染和冲突
window.formatDate = formatDate;

// ❌ 在 main.js 里全量挂到 Vue 原型
Vue.prototype.$formatDate = formatDate;  // 现代项目不推荐

[⬆ 返回目录](#⬆ 返回目录)


六、避免全局污染:作用域与模块化

6.1 什么是全局污染?

把函数挂在 window 上,会让所有脚本都能访问,容易:

  • 命名冲突(多个库都用 format
  • 难以追踪来源
  • 不利于打包和按需加载
js 复制代码
// ❌ 全局污染
window.myFormat = function() { /* ... */ };

[⬆ 返回目录](#⬆ 返回目录)

6.2 正确做法:ES Module

export 明确导出,用 import 明确引入,所有函数都在模块作用域内:

js 复制代码
// format.js
export function formatDate(timestamp) { /* ... */ }

// 组件中
import { formatDate } from '@/utils/format';

[⬆ 返回目录](#⬆ 返回目录)

6.3 旧项目怎么办?

老项目可能是 IIFE + 全局变量,可以逐步改成模块:

js 复制代码
// 旧写法
(function(global) {
  global.myUtils = {
    formatDate: function(timestamp) { /* ... */ }
  };
})(window);

// 新写法:用模块
// format.js
export function formatDate(timestamp) { /* ... */ }

[⬆ 返回目录](#⬆ 返回目录)


七、完整实战:从零搭一套 utils

下面是一套可直接复用的工具函数示例,覆盖格式化、校验、存储三个常见场景。

7.1 format.js

js 复制代码
/**
 * 格式化工具
 */

export function formatDate(timestamp, format = 'YYYY-MM-DD') {
  const date = timestamp instanceof Date ? timestamp : new Date(timestamp);
  if (isNaN(date.getTime())) return '';
  const pad = (n) => String(n).padStart(2, '0');
  const map = {
    YYYY: date.getFullYear(),
    MM: pad(date.getMonth() + 1),
    DD: pad(date.getDate()),
    HH: pad(date.getHours()),
    mm: pad(date.getMinutes()),
    ss: pad(date.getSeconds()),
  };
  return format.replace(/YYYY|MM|DD|HH|mm|ss/g, (key) => map[key] || key);
}

export function formatPhone(phone) {
  const str = String(phone).replace(/\D/g, '');
  if (str.length !== 11) return phone;
  return str.replace(/(\d{3})(\d{4})(\d{4})/, '$1-$2-$3');
}

[⬆ 返回目录](#⬆ 返回目录)

7.2 validate.js

js 复制代码
/**
 * 校验工具
 */

export function validateEmail(email) {
  return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email || '');
}

export function validatePhone(phone) {
  return /^1[3-9]\d{9}$/.test(String(phone).replace(/\s/g, ''));
}

[⬆ 返回目录](#⬆ 返回目录)

7.3 storage.js

js 复制代码
/**
 * 本地存储封装(自动 JSON 序列化)
 */

const PREFIX = 'myapp_';

export function getStorage(key, defaultValue = null) {
  try {
    const val = localStorage.getItem(PREFIX + key);
    return val ? JSON.parse(val) : defaultValue;
  } catch {
    return defaultValue;
  }
}

export function setStorage(key, value) {
  try {
    localStorage.setItem(PREFIX + key, JSON.stringify(value));
  } catch (e) {
    console.warn('setStorage error:', e);
  }
}

[⬆ 返回目录](#⬆ 返回目录)

7.4 index.js(统一导出)

js 复制代码
export { formatDate, formatPhone } from './format';
export { validateEmail, validatePhone } from './validate';
export { getStorage, setStorage } from './storage';

[⬆ 返回目录](#⬆ 返回目录)

7.5 在 Vue 组件中使用

html 复制代码
<template>
  <div>
    <p>注册时间:{{ displayDate }}</p>
    <p>手机号:{{ displayPhone }}</p>
  </div>
</template>

<script>
import { formatDate, formatPhone, validateEmail } from '@/utils';

export default {
  data() {
    return {
      user: {
        registerTime: 1700000000000,
        phone: '13800138000',
        email: 'test@example.com',
      },
    };
  },
  computed: {
    displayDate() {
      return formatDate(this.user.registerTime, 'YYYY年MM月DD日');
    },
    displayPhone() {
      return formatPhone(this.user.phone);
    },
  },
  methods: {
    submit() {
      if (!validateEmail(this.user.email)) {
        this.$message.error('邮箱格式不正确');
        return;
      }
      // 提交逻辑...
    },
  },
};
</script>

[⬆ 返回目录](#⬆ 返回目录)


八、常见踩坑总结

原因 建议
一个 utils.js 几千行 没按领域拆分 按 format、validate、storage 等拆分
命名看不懂 缩写过度、语义不清 formatDate 这类动词+名词
到处 window.xxx 不了解模块化 使用 ES Module 导出/引入
组件里写死逻辑 不会抽函数 抽成纯函数,再放到 utils
工具函数里发请求 业务和工具混在一起 工具只做纯逻辑,请求放 api 层

[⬆ 返回目录](#⬆ 返回目录)


九、小结

  • 为什么拆:逻辑复用、易测试、易维护。
  • 怎么拆:按 format、validate、storage 等领域分文件。
  • 怎么命名:动词开头、见名知意,少用缩写。
  • 怎么复用 :统一在 index.js 导出,按需 import
  • 怎么避免污染 :用 ES Module,不挂 window

养成「能复用就抽、抽了就放对位置」的习惯,对团队协作和长期维护都很重要。

[⬆ 返回目录](#⬆ 返回目录)

🔍 系列模块导航

📝 编码语法规范

这是前端规范实战系列中第二个模块,当编码语法规范模块更新完成之后会附上此模块的跳转链接,方便同学们阅读学习。

更新中,敬请期待~

👉 跟着系列慢慢学,把技术功底扎扎实实地打牢~

📚 系列总览

前端规范实战系列目前正在持续更新中,当该系列完结之后我会整理出一篇《前端规范实战系列全系列目录导航》,届时会附上文章简介以及跳转链接,方便同学们按顺序体系化的学习~

更新中,敬请期待~

[⬆ 返回目录](#⬆ 返回目录)


技术成长,从来不是比谁写得快,而是比谁写得稳、规范、可维护

哪怕每次只吃透一条规范,长期下来,差距会非常明显。

后续我会持续更新前端规范、工程化、可维护代码相关实战干货,帮你告别面条代码、维护噩梦,在开发与面试中更有底气。

觉得有用欢迎 点赞 + 收藏 + 关注,不错过每一篇实战内容。

我是 Eugene,与你一起写规范、写优质代码,我们下篇干货见~

相关推荐
C澒1 小时前
微前端容器标准化 —— 公共能力篇:通用请求
前端·架构
古城小栈1 小时前
Go 底层代码的完整分类
开发语言·后端·golang
llxxyy卢2 小时前
web部分中等题目
android·前端
耳冉鹅2 小时前
Go无锁共享内存环形缓冲区设计
开发语言·golang
若惜2 小时前
selenium自动化测试web自动化测试 框架封装Pom
前端·python·selenium
计算机安禾2 小时前
【C语言程序设计】第36篇:二进制文件的读写
c语言·开发语言·c++·算法·github·visual studio code·visual studio
子非鱼@Itfuture2 小时前
try-catch和try-with-resources区别是什么?try{}catch(){}和try(){}catch(){}有什么好处?
java·开发语言
Amumu121382 小时前
Js:内置对象
开发语言·前端·javascript
广州华水科技2 小时前
2026年单北斗GNSS变形监测系统推荐,助力精准监控与智慧城市建设
前端