微信小程序app.js里居然可以写自定义方法?用起来简直不要太方便

还在每个Page里重复编写相同的工具函数?快来解锁App.js的隐藏用法,告别代码冗余,实现真正的全局调用!

1. 前言:被我们忽略的App.js

各位小程序开发者,相信大家对app.js都不陌生。它是小程序的入口文件,我们通常会在里面调用App()函数来注册小程序,并设置一些全局生命周期回调全局数据

// app.js 最常见的写法
App({
  onLaunch() {
    console.log('小程序初始化完成');
  },
  onShow() {
    console.log('小程序显示');
  },
  globalData: {
    userInfo: null,
    systemInfo: null
  }
});

但是,你有没有想过,App()实例本身也是一个对象?既然是一个对象,那么我们是不是可以给它挂载自定义属性和方法呢?

答案是:当然可以! 而且这是一个非常实用且强大的技巧,可以极大地提升我们代码的复用性和可维护性。

2. 为什么要在App.js中写自定义方法?

在项目开发中,我们经常会遇到一些需要在多个页面甚至整个项目中使用的功能,例如:

  • 数据格式化:格式化时间、格式化价格

  • 工具函数:防抖、节流、深拷贝

  • 通用网络请求:带特定逻辑的请求封装

  • 用户信息管理:获取、更新、检查用户信息

  • 通用弹窗或提示:统一风格的模态框

如果每个页面都复制粘贴一份相同的代码,会导致:

  1. 代码冗余:项目体积变大,同样的代码重复出现。

  2. 维护困难:一旦需要修改函数逻辑,就得找到所有使用的地方逐个修改,极易出错。

而将这类方法定义在app.jsApp实例上,就可以实现一次定义,全局调用,完美解决上述问题。

3. 如何定义与使用自定义方法?

3.1 定义全局自定义方法

app.js中,我们可以直接在App({})的参数对象里添加自定义方法,与onLaunchglobalData平级。

// app.js

App({
  // 1. 小程序生命周期函数
  onLaunch(options) {},
  onShow(options) {},

  // 2. 全局数据
  globalData: {
    userInfo: null,
    baseURL: 'https://api.example.com'
  },

  // 3. 自定义全局方法 - 工具类
  /**
   * 简单版时间格式化工具
   * @param {Date} date - 日期对象
   * @param {string} format - 格式,默认 'YYYY-MM-DD'
   * @returns {string} 格式化后的时间字符串
   */
  formatTime(date, format = 'YYYY-MM-DD') {
    const year = date.getFullYear();
    const month = (date.getMonth() + 1).toString().padStart(2, '0');
    const day = date.getDate().toString().padStart(2, '0');

    if (format === 'YYYY-MM-DD') {
      return `${year}-${month}-${day}`;
    }
    // 可以扩展其他格式
    return `${year}-${month}-${day}`;
  },

  /**
   * 防抖函数
   * @param {Function} fn - 需要防抖的函数
   * @param {number} delay - 延迟时间(ms)
   * @returns {Function} 包装后的防抖函数
   */
  debounce(fn, delay = 500) {
    let timer = null;
    return function (...args) {
      clearTimeout(timer);
      timer = setTimeout(() => {
        fn.apply(this, args);
      }, delay);
    };
  },

  // 4. 自定义全局方法 - 业务相关
  /**
   * 检查用户登录状态
   * @returns {boolean} 是否已登录
   */
  checkLogin() {
    const token = wx.getStorageSync('token');
    const userInfo = this.globalData.userInfo;
    return !!(token || userInfo);
  },

  /**
   * 带统一错误处理的请求
   * @param {string} url - 请求地址
   * @param {Object} data - 请求参数
   * @param {string} method - 请求方法
   * @returns {Promise} - 返回Promise对象
   */
  async apiRequest(url, data = {}, method = 'GET') {
    const { baseURL } = this.globalData;
    const token = wx.getStorageSync('token');

    try {
      const res = await wx.request({
        url: `${baseURL}${url}`,
        data,
        method,
        header: {
          'Authorization': token ? `Bearer ${token}` : '',
          'Content-Type': 'application/json'
        }
      });
      // 这里可以加入对res.data的业务状态码判断
      if (res.statusCode === 200) {
        return res.data;
      } else {
        throw new Error(`请求错误 [${res.statusCode}]`);
      }
    } catch (error) {
      console.error('API请求失败:', error);
      wx.showToast({
        title: '网络请求失败,请重试',
        icon: 'none'
      });
      throw error; // 将错误继续抛出,以便页面能具体处理
    }
  }
});

3.2 在页面/组件中调用全局方法

在任何页面(Page)或组件(Component)中,我们都可以通过getApp()方法获取到App实例,从而调用我们定义在它上面的方法。

重要: 不要在app.js中使用getApp(),因为此时App可能还未初始化完毕。

在Page页面中调用:

// pages/index/index.js
const app = getApp(); // 获取App实例

Page({
  data: {
    formattedDate: ''
  },

  onLoad() {
    // 1. 调用工具方法格式化时间
    const today = new Date();
    const dateStr = app.formatTime(today); // 调用app.js中的formatTime方法
    this.setData({ formattedDate: dateStr });

    // 2. 检查登录状态
    if (app.checkLogin()) { // 调用app.js中的checkLogin方法
      this.fetchData();
    } else {
      wx.navigateTo({ url: '/pages/login/login' });
    }

    // 3. 使用防抖函数包装事件处理函数
    this.debouncedTap = app.debounce(this.onTap, 1000);
  },

  // 一个按钮点击事件
  onTap() {
    console.log('按钮被点击,防抖处理');
  },

  // 在WXML中绑定的函数应该是 debouncedTap
  async fetchData() {
    try {
      // 4. 使用封装的网络请求
      const goodsList = await app.apiRequest('/goods/list', { page: 1 });
      console.log('请求到的数据:', goodsList);
    } catch (error) {
      // 错误已被app.apiRequest统一处理,这里可以做更细致的页面逻辑处理
    }
  }
});

在Component组件中调用:

// components/my-component/my-component.js
const app = getApp(); // 同样,先获取App实例

Component({
  methods: {
    onButtonClick() {
      if (app.checkLogin()) { // 调用全局方法
        // 执行逻辑
      } else {
        wx.showModal({
          title: '提示',
          content: '请先登录',
        });
      }
    }
  }
});

4. 注意事项

  1. 避免过度使用:不要将所有的函数都塞进app.js。只将那些真正全局通用的方法放在这里,否则会使app.js变得臃肿难以维护。项目较大时,可以考虑使用小程序插件独立的JS模块(Module)来管理工具函数。

  2. this的指向问题:在app.js的自定义方法内部,this指向的是App实例。这意味着你可以在方法内通过this.globalData访问到全局数据。但在页面或组件中调用这些方法时,要确保方法的this指向正确,如果需要进行复杂绑定,可能需要使用callapply或箭头函数。

  3. 执行时机:确保在调用getApp()时,app.js已经初始化完成。在页面onLoad及之后的生命周期中调用是安全的。

  4. 单例模式:整个小程序中只有一个App实例,getApp()在任何地方获取到的都是同一个实例。

5. 总结

app.js中编写自定义全局方法是一个简单却极其有效的开发技巧。它充分利用了小程序框架的特性,帮助我们:

  • 减少代码重复,提升开发效率。

  • 集中管理逻辑,降低维护成本。

  • 轻松实现跨页面/组件调用,让项目结构更清晰。

下次当你发现某个函数在两个以上的地方被使用时,不妨考虑一下:“是时候把它提升到app.js里了!”。

希望这个小技巧能对你的开发工作有所帮助!如果你有更多有趣的用法或心得体会,欢迎在评论区留言讨论~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jiaberrrr

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值