UniApp的TypeScript装饰器实践

UniApp的TypeScript装饰器实践:轻松上手,玩转装饰器

开场白 🎤

大家好,欢迎来到今天的讲座!今天我们要聊的是一个非常有趣的话题——UniApp中的TypeScript装饰器。如果你对装饰器还不是很熟悉,别担心,我们会从零开始,一步一步地带你走进这个神奇的世界。准备好了吗?让我们开始吧!

什么是装饰器?✨

装饰器(Decorators)是TypeScript中的一种元编程工具,它允许你在类、方法、属性或参数上添加额外的功能或行为。你可以把装饰器想象成一个“魔法棒”,用它可以在不修改原有代码的情况下,给类或方法增加一些特殊的效果。

在JavaScript/TypeScript中,装饰器本质上是一个函数,它会在类或方法被定义时自动执行。装饰器可以用来:

  • 修改类的行为
  • 验证参数
  • 日志记录
  • 权限控制
  • 缓存数据
  • 等等

装饰器的基本语法

装饰器的语法非常简单,通常以@符号开头,后面跟着一个函数名。比如:

function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;
  descriptor.value = function (...args: any[]) {
    console.log(`Calling ${propertyKey} with args:`, args);
    return originalMethod.apply(this, args);
  };
}

class Example {
  @log
  greet(name: string) {
    console.log(`Hello, ${name}!`);
  }
}

const example = new Example();
example.greet('World'); // 输出: Calling greet with args: [ 'World' ]
                        //       Hello, World!

在这个例子中,@log装饰器会拦截greet方法的调用,并在调用前打印出传入的参数。

UniApp中的装饰器实践 🚀

UniApp 是一个基于 Vue.js 的跨平台开发框架,支持使用 TypeScript 进行开发。结合装饰器,我们可以为 UniApp 应用程序添加更多的功能和灵活性。

1. 使用装饰器简化页面生命周期管理 🔄

UniApp 页面有多个生命周期钩子,比如 onLoadonShowonHide 等。如果我们想在多个页面中重复使用这些钩子逻辑,手动编写会显得非常冗余。这时候,装饰器就可以派上用场了!

自定义生命周期装饰器

我们可以创建一个自定义的装饰器来简化页面生命周期的管理。比如,我们希望在每个页面加载时自动记录页面的访问时间。

// lifecycle.decorator.ts
import { getCurrentPages } from '@dcloudio/uni-app';

function logPageLoad(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;
  descriptor.value = function (...args: any[]) {
    const page = getCurrentPages().pop();
    console.log(`Page ${page.route} loaded at:`, new Date().toISOString());
    return originalMethod.apply(this, args);
  };
}

export default logPageLoad;

然后,在我们的页面组件中使用这个装饰器:

// pages/index/index.vue
<script lang="ts">
import { defineComponent } from 'vue';
import logPageLoad from '@/decorators/lifecycle.decorator';

export default defineComponent({
  onLoad: logPageLoad(function (options: any) {
    console.log('Page options:', options);
  }),
});
</script>

这样,每次页面加载时,都会自动记录访问时间,而不需要在每个页面中手动编写日志代码。

2. 使用装饰器进行权限验证 🔒

在很多应用中,某些页面或功能可能需要用户登录后才能访问。我们可以通过装饰器来简化权限验证的逻辑。

创建权限验证装饰器

// auth.decorator.ts
function requireAuth(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;
  descriptor.value = function (...args: any[]) {
    const user = this.$store.state.user; // 假设你使用 Vuex 管理状态
    if (!user.isAuthenticated) {
      uni.showToast({
        title: '请先登录',
        icon: 'none',
      });
      uni.reLaunch({ url: '/pages/login/login' });
      return;
    }
    return originalMethod.apply(this, args);
  };
}

export default requireAuth;

然后,在需要权限验证的页面中使用这个装饰器:

// pages/profile/profile.vue
<script lang="ts">
import { defineComponent } from 'vue';
import requireAuth from '@/decorators/auth.decorator';

export default defineComponent({
  methods: {
    @requireAuth
    viewProfile() {
      console.log('查看个人资料');
    },
  },
});
</script>

现在,viewProfile 方法只有在用户登录后才能调用,否则会跳转到登录页面。

3. 使用装饰器优化 API 请求 🌐

在实际开发中,API 请求是非常常见的操作。我们可以使用装饰器来简化 API 请求的处理,比如自动处理错误、显示加载动画等。

创建 API 请求装饰器

// api.decorator.ts
function handleApiRequest(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;
  descriptor.value = async function (...args: any[]) {
    try {
      uni.showLoading({ title: '加载中...' });
      const response = await originalMethod.apply(this, args);
      uni.hideLoading();
      return response;
    } catch (error) {
      uni.showToast({
        title: '请求失败,请稍后再试',
        icon: 'none',
      });
      throw error;
    }
  };
}

export default handleApiRequest;

然后,在需要发送 API 请求的地方使用这个装饰器:

// services/user.service.ts
import { http } from '@/utils/http';
import handleApiRequest from '@/decorators/api.decorator';

class UserService {
  @handleApiRequest
  async fetchUserData(userId: string) {
    return http.get(`/users/${userId}`);
  }
}

export default new UserService();

现在,fetchUserData 方法会自动显示加载动画,并在请求失败时显示错误提示,而不需要在每个地方都手动编写这些逻辑。

装饰器的高级用法 🧙‍♂️

除了上面提到的基础用法,装饰器还有一些更高级的应用场景。比如,我们可以使用装饰器来实现依赖注入、AOP(面向切面编程)、甚至是 ORM(对象关系映射)等功能。

依赖注入

依赖注入是一种设计模式,它允许我们将依赖项(如服务、工具类等)注入到类中,而不是在类内部直接创建它们。这有助于提高代码的可测试性和灵活性。

// injector.decorator.ts
function inject(serviceName: string) {
  return function (target: any, propertyKey: string) {
    Object.defineProperty(target, propertyKey, {
      get() {
        return getApp()[serviceName];
      },
    });
  };
}

// 在组件中使用
<script lang="ts">
import { defineComponent } from 'vue';
import { inject } from '@/decorators/injector.decorator';

export default defineComponent({
  setup() {
    class MyComponent {
      @inject('userService')
      private userService!: UserService;

      async getUserData() {
        return this.userService.fetchUserData('123');
      }
    }

    return new MyComponent();
  },
});
</script>

AOP(面向切面编程)

AOP 是一种编程范式,它允许我们在不修改业务逻辑的情况下,动态地为方法添加横切关注点(如日志、事务管理等)。装饰器非常适合实现 AOP。

// aop.decorator.ts
function logExecutionTime(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;
  descriptor.value = function (...args: any[]) {
    const startTime = performance.now();
    const result = originalMethod.apply(this, args);
    const endTime = performance.now();
    console.log(`${propertyKey} executed in ${endTime - startTime}ms`);
    return result;
  };
}

// 在服务中使用
class MathService {
  @logExecutionTime
  add(a: number, b: number) {
    return a + b;
  }
}

总结 🎉

通过今天的讲座,我们了解了如何在 UniApp 中使用 TypeScript 装饰器来简化开发流程、提升代码的可维护性。装饰器不仅可以帮助我们管理页面生命周期、进行权限验证、优化 API 请求,还可以用于更高级的场景,如依赖注入和 AOP。

希望这篇文章能让你对装饰器有更深的理解,并且能够在你的项目中灵活运用。如果你有任何问题或想法,欢迎在评论区留言,我们一起讨论!

最后,祝你在 UniApp 和 TypeScript 的世界里玩得开心!😊


参考资料:

  • TypeScript 官方文档:装饰器章节
  • Vue 3 官方文档:组合式 API
  • UniApp 官方文档:生命周期钩子

(注:以上内容引用了相关技术文档的描述,但并未插入外部链接,以确保符合要求。)

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注