还在每个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中写自定义方法?
在项目开发中,我们经常会遇到一些需要在多个页面甚至整个项目中使用的功能,例如:
-
数据格式化:格式化时间、格式化价格
-
工具函数:防抖、节流、深拷贝
-
通用网络请求:带特定逻辑的请求封装
-
用户信息管理:获取、更新、检查用户信息
-
通用弹窗或提示:统一风格的模态框
如果每个页面都复制粘贴一份相同的代码,会导致:
-
代码冗余:项目体积变大,同样的代码重复出现。
-
维护困难:一旦需要修改函数逻辑,就得找到所有使用的地方逐个修改,极易出错。
而将这类方法定义在app.js
的App实例
上,就可以实现一次定义,全局调用,完美解决上述问题。
3. 如何定义与使用自定义方法?
3.1 定义全局自定义方法
在app.js
中,我们可以直接在App({})
的参数对象里添加自定义方法,与onLaunch
, globalData
平级。
// 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. 注意事项
-
避免过度使用:不要将所有的函数都塞进
app.js
。只将那些真正全局通用的方法放在这里,否则会使app.js
变得臃肿难以维护。项目较大时,可以考虑使用小程序插件或独立的JS模块(Module)来管理工具函数。 -
this
的指向问题:在app.js
的自定义方法内部,this
指向的是App实例
。这意味着你可以在方法内通过this.globalData
访问到全局数据。但在页面或组件中调用这些方法时,要确保方法的this
指向正确,如果需要进行复杂绑定,可能需要使用call
、apply
或箭头函数。 -
执行时机:确保在调用
getApp()
时,app.js
已经初始化完成。在页面onLoad
及之后的生命周期中调用是安全的。 -
单例模式:整个小程序中只有一个
App
实例,getApp()
在任何地方获取到的都是同一个实例。
5. 总结
在app.js
中编写自定义全局方法是一个简单却极其有效的开发技巧。它充分利用了小程序框架的特性,帮助我们:
-
减少代码重复,提升开发效率。
-
集中管理逻辑,降低维护成本。
-
轻松实现跨页面/组件调用,让项目结构更清晰。
下次当你发现某个函数在两个以上的地方被使用时,不妨考虑一下:“是时候把它提升到app.js
里了!”。
希望这个小技巧能对你的开发工作有所帮助!如果你有更多有趣的用法或心得体会,欢迎在评论区留言讨论~