前端开发中的装饰器模式——给函数和组件“加壳”的艺术

装饰器模式是前端开发中最常用的设计模式之一。从高阶函数到 React 高阶组件,从性能监控到权限控制,它无处不在。比如大家熟悉的React.memo,它背后的设计思想就是装饰器模式。接下来我们从性能监控、重试机制、权限控制三个真实场景出发,给出装饰器模式的完整实现。

一、从一个性能监控需求出发

我们在做性能优化时,想知道某个异步请求到底耗时多少,最直接的做法是在函数里写计时:

javascript 复制代码
async function fetchUsers() {
  const start = Date.now();
  const res = await fetch('/api/users');
  const data = await res.json();
  console.log(`耗时: ${Date.now() - start}ms`);
  return data;
}

问题很明显:监控逻辑和业务逻辑混在一起 。如果还有 fetchOrders、fetchProducts,每个函数都要重复写一遍计时代码。这就是装饰器模式要解决的问题------把横切关注点(日志、性能、重试、权限)从核心业务逻辑中剥离出来。

二、装饰器模式的核心思想

在不修改原函数或组件代码的前提下,动态地给其添加额外的职责或行为。

在前端开发中,它有两种最常见的落地形式:

  • 高阶函数(HOF):装饰函数,给函数叠加日志、重试、节流等能力。
  • 高阶组件(HOC):装饰 React 组件,给组件叠加权限、加载状态等能力。

三、高阶函数装饰器------给函数"加壳"

1. 性能监控

用装饰器把计时代码抽离出来,原函数完全不需要改动:

javascript 复制代码
const withPerformance = (fn, label) => async (...args) => {
  const start = performance.now();
  try {
    return await fn(...args);
  } finally {
    console.log(`[${label}] 耗时: ${(performance.now() - start).toFixed(2)}ms`);
  }
};

// 原函数保持纯净
async function fetchUsers() {
  return fetch('/api/users').then(r => r.json());
}

// 装饰后自动获得监控能力
const monitoredFetch = withPerformance(fetchUsers, '获取用户列表');
monitoredFetch(); // 自动打印耗时

好处:核心逻辑和监控逻辑完全分离。想给哪个函数加监控,套上装饰器就行,原函数零侵入。

2. 重试机制

调用第三方接口时,网络抖动可能导致偶发失败。用装饰器给任何函数加上重试能力:

javascript 复制代码
const withRetry = (fn, retries = 3, delay = 1000) => async (...args) => {
  for (let i = 0; i <= retries; i++) {
    try {
      return await fn(...args);
    } catch (err) {
      if (i === retries) throw err;
      await new Promise(r => setTimeout(r, delay));
    }
  }
};

const resilientFetch = withRetry(fetchUsers, 3);

后续重试策略变化(比如改成指数退避),只需改这一个装饰器,所有使用它的函数自动生效。

3. 防抖与节流

React 的搜索框联想功能中,我们通常在自定义 Hook 中封装防抖逻辑,但其底层仍是高阶函数装饰器思想:

javascript 复制代码
function debounce(fn, delay = 300) {
  let timer = null;
  return function (...args) {
    clearTimeout(timer);
    timer = setTimeout(() => fn.apply(this, args), delay);
  };
}

const handleSearch = debounce((keyword) => {
  fetch(`/api/search?q=${keyword}`);
}, 300);

四、React 高阶组件装饰器------给组件"加壳"

1. 权限控制

后台管理系统中,很多页面需要判断角色权限。把这些逻辑从页面组件中抽出来:

tsx 复制代码
const withAuth = (Component) => (props) => {
  const { isAuthenticated, role } = useAuth();

  if (!isAuthenticated) return <Navigate to="/login" replace />;
  if (props.requiredRole && role !== props.requiredRole) {
    return <Result status="403" title="无权限" />;
  }

  return <Component {...props} />;
};

// 使用:任何需要权限的页面,套一层即可
const AdminDashboard = withAuth(Dashboard);
<AdminDashboard requiredRole="admin" />;

权限逻辑只存在于 withAuth 中。某天权限规则变了,只改一个地方,所有页面自动同步。

2. 加载状态管理

数据还在请求时,展示骨架屏或 Spin:

tsx 复制代码
const withLoading = (Component) => ({ loading, ...props }) => {
  if (loading) return <Spin size="large" />;
  return <Component {...props} />;
};

const UserListWithLoading = withLoading(UserList);

五、总结

装饰器模式的核心是在不修改原代码的前提下,动态地给函数或组件添加额外职责 。它是前端开发中最实用的模式之一,其核心价值在于让核心逻辑和附加能力完全解耦 。React.memo 就是内置的组件装饰器。当下次发现自己在多个函数或组件里写相似的日志、权限、重试逻辑时,停下来想一想------是不是该给它们"加个壳"了?

相关推荐
时间的情敌14 小时前
Cesium 流动管线、管道动画、管线流向仿真,管网、油气、水利孪生高频需求
前端·交互·cesium
yuyuxun114 小时前
协同过滤的校园二手物品智能推荐系统的设计与实现 毕业设计-附源码40082
javascript·vue.js·spring boot·python·数据分析·django·课程设计
wangxiaoxuan21715 小时前
SLAM 的基本结构:前端、后端、回环与地图优化
前端·机器人·科沃斯八界
总要学点什么17 小时前
第105篇:三维特效-流动图片墙体效果——让箭头沿墙长滚,它俩竟是同一份代码
开发语言·javascript·ecmascript·gis·webgl·材质·cesium
Agent世界18 小时前
用 CDP 做发布自动化:四道机器闸门与产物验收
前端·javascript
FungLeo18 小时前
成为全栈·Vue 管理后台篇·编辑器图片上传:从文件选择到文章附件关联
前端·vue.js·编辑器·markdown·文件上传·formdata·附件管理
总要学点什么19 小时前
第104篇:三维特效-流动墙体效果——让贴图沿墙流动,它却只肯走一条对角线
开发语言·javascript·ecmascript·gis·webgl·材质·cesium
某哈压力大19 小时前
SSH 突然 Connection reset?不是密钥坏了,是 OpenSSL 把 sshd 干趴了
前端·node.js·命令行
程序员大阳19 小时前
副队长ES6+教程(3)--解构赋值
前端·javascript·es6·赋值·解构
神膘护体小月半19 小时前
css - 元素背景图由 hover 触发,初次加载时闪动的解决办法
前端·javascript·css