所有的工具函数使用背景都是 vue 框架(vue2 vue3)+ ts 项目中使用部分需要自行调整
1. 防抖函数
使用场景:按钮点击(请求接口) 输入框输入(模糊搜索)计算鼠标的位置
之前有写过一篇关于 vue 中使用节流防抖
/**
* 防抖函数
* @param fn 回调函数
* @param timeout 延迟时间
* @param immediate 是否立即执行
*/
export const debounce = <T extends FunctionArgs>(
fn: T,
timeout: MaybeRef<number> = 200,
immediate = false
) => {
let timmer: TimeoutHandle;
const wait = unref(timeout);
return () => {
timmer && clearTimeout(timmer);
if (immediate) {
if (!timmer) {
fn();
}
timmer = setTimeout(() => (timmer = null), wait);
} else {
timmer = setTimeout(fn, wait);
}
};
};
2. 节流函数
/**
*
* @param fn 回调函数
* @param timeout 延迟时间
*/
export const throttle = (fn, timeout = 200) => {
let lastExecutedTime = 0;
return function (...args) {
const currentTime = Date.now();
if (currentTime - lastExecutedTime > timeout) {
fn.apply(this, args);
lastExecutedTime = currentTime;
}
};
};
3. 检查当前设备类型
/**
* 检测当前设备是否为移动设备
*
* 通过检查用户代理(User Agent)字符串判断当前设备类型
*
* @returns {boolean} 如果是移动设备返回true,桌面设备返回false
*
* @example
* if (deviceDetection()) {
* // 移动设备逻辑
* } else {
* // 桌面设备逻辑
* }
*/
export const deviceDetection = (): boolean => {
const userAgent: string = navigator.userAgent.toLowerCase();
// 定义移动设备的特征标识
const mobileIdentifiers = [
/iphone os/i, // iOS设备
/midp/i, // 早期Java手机平台
/rv:1.2.3.4/i, // 特定UC浏览器版本
/ucweb/i, // UC浏览器
/android/i, // 安卓设备
/windows ce/i, // Windows CE设备
/windows mobile/i // Windows Mobile设备
];
// 检查是否匹配任一移动设备标识
return mobileIdentifiers.some(pattern => pattern.test(userAgent));
};
## 3. 获取当前浏览器信息
```js
/**
* 获取当前浏览器信息,包括浏览器类型和版本号
*
* @returns {BrowserInter} 包含浏览器类型和版本的对象
* @property {string} browser - 浏览器类型 (msie|firefox|chrome|opera|safari)
* @property {string} version - 浏览器版本号
*
* @example
* const { browser, version } = getBrowserInfo();
* console.log(`当前浏览器: ${browser} ${version}`);
*/
export const getBrowserInfo = (): BrowserInter => {
const userAgent = navigator.userAgent.toLowerCase();
const regExp = /(msie|firefox|chrome|opera|version).*?([\d.]+)/;
const match = userAgent.match(regExp);
if (!match || match.length < 3) {
return {
browser: 'unknown',
version: '0.0'
};
}
// 特殊处理Safari浏览器(Safari使用'version'而不是'safari'标识)
const browser = match[1] === 'version' ? 'safari' : match[1];
return {
browser,
version: match[2]
};
};
4. js 操作 Class 类名相关
/**
* 检查DOM元素是否包含指定的类名
*
* @param {RefType<any>} ele - 要检查的DOM元素
* @param {string} cls - 要检查的类名
* @returns {boolean} 如果元素包含指定类名则返回true,否则返回false
*
* @example
* if (hasClass(element, 'active')) {
* // 元素包含'active'类的处理逻辑
* }
*/
export const hasClass = (ele: RefType<any>, cls: string): boolean => {
if (!ele || !ele.className || typeof ele.className !== 'string') return false;
return new RegExp("(\\s|^)" + cls + "(\\s|$)").test(ele.className);
};
/**
* 为DOM元素添加一个或两个类名
*
* @param {RefType<any>} ele - 要添加类名的DOM元素
* @param {string} cls - 要添加的主要类名
* @param {string} [extracls] - 可选的额外类名
* @returns {void}
*
* @example
* addClass(element, 'active');
* addClass(element, 'primary', 'large');
*/
export const addClass = (
ele: RefType<any>,
cls: string,
extracls?: string
): void => {
if (!ele || !cls) return;
if (!hasClass(ele, cls)) {
ele.className = ele.className ? `${ele.className} ${cls}` : cls;
}
if (extracls && !hasClass(ele, extracls)) {
ele.className = `${ele.className} ${extracls}`;
}
};
/**
* 从DOM元素移除一个或两个类名
*
* @param {RefType<any>} ele - 要移除类名的DOM元素
* @param {string} cls - 要移除的主要类名
* @param {string} [extracls] - 可选的额外要移除的类名
* @returns {void}
*
* @example
* removeClass(element, 'active');
* removeClass(element, 'primary', 'large');
*/
export const removeClass = (
ele: RefType<any>,
cls: string,
extracls?: string
): void => {
if (!ele || !ele.className || !cls) return;
if (hasClass(ele, cls)) {
const reg = new RegExp("(\\s|^)" + cls + "(\\s|$)");
ele.className = ele.className.replace(reg, " ").trim();
}
if (extracls && hasClass(ele, extracls)) {
const regs = new RegExp("(\\s|^)" + extracls + "(\\s|$)");
ele.className = ele.className.replace(regs, " ").trim();
}
};
/**
* 根据条件切换DOM元素的类名
*
* @param {boolean} flag - 决定是否添加类名的标志,true添加,false移除
* @param {string} clsName - 要切换的类名
* @param {RefType<any>} [target] - 目标DOM元素,默认为document.body
* @returns {void}
*
* @example
* // 根据darkMode变量切换body上的dark-theme类
* toggleClass(darkMode, 'dark-theme');
* // 切换特定元素的visible类
* toggleClass(isVisible, 'visible', menuElement);
*/
export const toggleClass = (
flag: boolean,
clsName: string,
target?: RefType<any>
): void => {
if (!clsName) return;
const targetEl = target || document.body;
if (!targetEl) return;
// 首先移除类名
let { className } = targetEl;
const reg = new RegExp("(\\s|^)" + clsName + "(\\s|$)");
className = className.replace(reg, " ").trim();
// 根据flag决定是否重新添加
targetEl.className = flag ? `${className} ${clsName}` : className;
};
5. requestAnimationFrame 实现的节流函数
/**
* 创建一个使用requestAnimationFrame实现的节流函数
*
* 该函数确保回调在每个动画帧内最多执行一次,适用于滚动、调整大小等高频事件处理
*
* @template T - 任意函数类型
* @param {T} fn - 需要节流的原始函数
* @returns {T} 节流处理后的函数
*
* @example
* // 创建一个节流的滚动处理函数
* const throttledScroll = useRafThrottle(() => {
* console.log(window.scrollY);
* });
*
* // 添加滚动事件监听
* window.addEventListener('scroll', throttledScroll);
*/
import type { FunctionArgs } from "@vueuse/core";
export function useRafThrottle<T extends FunctionArgs>(fn: T): T {
let locked = false;
// @ts-ignore
return function (...args) {
if (locked) return;
locked = true;
window.requestAnimationFrame(() => {
fn.apply(this, args);
locked = false;
});
};
}
6. 监听元素变化(大小)
/**
* ResizeObserver的回调处理函数
* 处理元素大小变化事件,并触发所有注册的监听函数
*
* @param {ResizeObserverEntry[]} entries - ResizeObserver提供的条目数组
* @private
*/
const resizeHandler = function (entries: ResizeObserverEntry[]): void {
for (const entry of entries) {
const listeners = entry.target.__resizeListeners__ || [];
if (listeners.length) {
listeners.forEach(fn => {
fn();
});
}
}
};
7. 为 DOM 元素添加/移除大小变化监听器
/**
* 为DOM元素添加大小变化监听器
*
* 使用ResizeObserver监听元素大小变化,当元素大小变化时触发回调函数
*
* @param {HTMLElement} element - 要监听大小变化的DOM元素
* @param {Function} fn - 元素大小变化时调用的回调函数
* @returns {void}
*
* @example
* // 监听容器大小变化并更新图表
* addResizeListener(chartContainer, () => {
* chart.resize();
* });
*/
export const addResizeListener = function (element: any, fn: () => void): void {
if (isServer || !element) return;
// 初始化元素上的监听器数组
if (!element.__resizeListeners__) {
element.__resizeListeners__ = [];
// 创建ResizeObserver实例并开始观察元素
element.__ro__ = new ResizeObserver(resizeHandler);
element.__ro__.observe(element);
}
// 避免重复添加相同的监听器
if (element.__resizeListeners__.indexOf(fn) === -1) {
element.__resizeListeners__.push(fn);
}
};
/**
* 移除DOM元素上的大小变化监听器
*
* 如果移除后元素上没有其他监听器,则停止对该元素的观察
*
* @param {HTMLElement} element - 要移除监听器的DOM元素
* @param {Function} fn - 要移除的特定监听器函数
* @returns {void}
*
* @example
* // 移除之前添加的监听器
* removeResizeListener(chartContainer, updateChart);
*/
export const removeResizeListener = function (element: any, fn: () => void): void {
if (isServer || !element || !element.__resizeListeners__) return;
// 查找并移除特定监听器
const index = element.__resizeListeners__.indexOf(fn);
if (index !== -1) {
element.__resizeListeners__.splice(index, 1);
}
// 如果没有剩余监听器,停止观察并清理
if (!element.__resizeListeners__.length && element.__ro__) {
element.__ro__.disconnect();
element.__ro__ = null;
}
};
8. 本地存储 sessionStorage localStorage 使用封装
/**
* Web存储代理接口
* 定义了存储操作的标准方法集
*/
interface ProxyStorage {
/**
* 获取存储项
* @param {string} key - 存储项的键
* @returns {any} 存储的值
*/
getItem(key: string): any;
/**
* 设置存储项
* @param {string} key - 存储项的键
* @param {string} value - 要存储的值
*/
setItem(key: string, value: string): void;
/**
* 移除存储项
* @param {string} key - 要移除的存储项的键
*/
removeItem(key: string): void;
/**
* 清除所有存储项
*/
clear(): void;
}
/**
* sessionStorage操作代理类
* 提供对sessionStorage的操作封装,支持存取JSON对象
*/
class SessionStorageProxy implements ProxyStorage {
/**
* 被代理的存储对象
* @protected
*/
protected storage: ProxyStorage;
/**
* 构造函数
* @param {ProxyStorage} storageModel - 被代理的存储对象
*/
constructor(storageModel: ProxyStorage) {
this.storage = storageModel;
}
/**
* 存储数据,自动将对象转换为JSON字符串
* @param {string} key - 存储项的键
* @param {any} value - 要存储的值,可以是任意类型
*/
public setItem(key: string, value: any): void {
if (key === undefined || key === null || key === '') {
throw new Error('Storage key cannot be empty');
}
try {
const valueStr = typeof value === 'object'
? JSON.stringify(value)
: String(value);
this.storage.setItem(key, valueStr);
} catch (error) {
console.error('Failed to store item:', error);
}
}
/**
* 获取数据,自动将JSON字符串转换回对象
* @param {string} key - 存储项的键
* @returns {any} 解析后的存储值,如果项不存在或解析失败则返回null
*/
public getItem(key: string): any {
if (key === undefined || key === null || key === '') {
return null;
}
try {
const value = this.storage.getItem(key);
if (value === null || value === undefined) {
return null;
}
return JSON.parse(value);
} catch (error) {
console.warn(`Error parsing stored item ${key}:`, error);
return this.storage.getItem(key);
}
}
/**
* 移除指定的存储项
* @param {string} key - 要移除的存储项的键
*/
public removeItem(key: string): void {
if (key) {
this.storage.removeItem(key);
}
}
/**
* 清除所有存储项
*/
public clear(): void {
this.storage.clear();
}
}
/**
* localStorage操作代理类
* 提供对localStorage的操作封装,继承自SessionStorageProxy
*/
class LocalStorageProxy extends SessionStorageProxy implements ProxyStorage {
/**
* 构造函数
* @param {ProxyStorage} localStorage - 被代理的localStorage对象
*/
constructor(localStorage: ProxyStorage) {
super(localStorage);
}
}
/**
* sessionStorage实例
* 用于操作浏览器的会话存储
* 页面会话结束时数据会被清除
*
* @example
* // 存储数据
* storageSession.setItem('user', { name: 'John', age: 30 });
*
* // 获取数据
* const user = storageSession.getItem('user');
*
* // 删除数据
* storageSession.removeItem('user');
*/
export const storageSession = new SessionStorageProxy(sessionStorage);
/**
* localStorage实例
* 用于操作浏览器的本地存储
* 数据永久保存,除非被显式清除
*
* @example
* // 存储数据
* storageLocal.setItem('theme', 'dark');
*
* // 获取数据
* const theme = storageLocal.getItem('theme');
*
* // 删除数据
* storageLocal.removeItem('theme');
*/
export const storageLocal = new LocalStorageProxy(localStorage);
9. js-cookie 使用封装
import Cookies from "js-cookie";
import useUserStoreHook from "/@/store/modules/user";
/**
* 认证令牌的Cookie键名
* 用于存储和获取认证令牌
*/
const TokenKey = "Admin-Token";
/**
* 获取当前用户的认证令牌
*
* 从Cookie中读取存储的令牌
*
* @returns {string | undefined} 用户认证令牌,如果未登录则返回undefined
*
* @example
* const token = getToken();
* if (token) {
* // 用户已登录
* } else {
* // 用户未登录,重定向到登录页
* }
*/
export function getToken(): string | undefined {
return Cookies.get(TokenKey);
}
/**
* 设置用户认证令牌
*
* 将认证令牌存储到Cookie中,同时更新Vuex存储的状态
*
* @param {object} data - 包含令牌信息的对象
* @param {string} data.token - 用户认证令牌
* @returns {void}
*
* @example
* // 登录成功后设置令牌
* setToken({ token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...' });
*/
export function setToken(data: { token: string }): void {
if (!data || typeof data !== 'object') {
console.error('Invalid token data provided');
return;
}
const { token: accessToken } = data;
if (!accessToken) {
console.error('No access token provided');
return;
}
// store 里面保存 token 的方法 更新Vuex存储的令牌状态
useUserStoreHook().SET_TOKEN(accessToken);
// 存储令牌到Cookie
// 注意:这里没有设置过期时间,使用默认的会话Cookie
Cookies.set(TokenKey, accessToken);
}
/**
* 移除用户认证令牌
*
* 从Cookie中删除存储的令牌,通常在用户登出时调用
*
* @returns {void}
*
* @example
* // 用户登出
* removeToken();
* router.push('/login');
*/
export function removeToken(): void {
Cookies.remove(TokenKey);
}
10. 获取当前环境变量
/**
* 环境变量工具模块
* 用于访问和加载Vite应用的环境变量
*/
/**
* 加载应用的环境变量
*
* 从Vite的import.meta.env中获取当前应用的所有环境变量。
* 环境变量通常在.env文件中定义,并在构建时由Vite处理。
*
* @returns {ViteEnv} 包含所有环境变量的对象
*
* @example
* // 获取所有环境变量
* const env = loadEnv();
* console.log('当前模式:', env.MODE);
* console.log('API基础URL:', env.VITE_API_BASE_URL);
*
* // 也可以直接解构需要的环境变量
* const { VITE_APP_TITLE, VITE_API_BASE_URL } = loadEnv();
*/
export const loadEnv = (): ViteEnv => {
try {
// 确保我们在浏览器环境中
if (typeof import.meta.env === 'undefined') {
console.warn('环境变量不可用,可能不在Vite环境中运行');
return {} as ViteEnv;
}
return import.meta.env;
} catch (error) {
console.error('加载环境变量时发生错误:', error);
return {} as ViteEnv;
}
};
第一篇先更新 10 个 后续继续更新第二篇 第三篇…敬请期待~ 如果感觉有所帮助 欢迎点赞收藏推荐 如有不恰当的地方或者更好的建议 也欢迎留言交流~~ 感谢阅读 再见~