getApp()
getApp() 函数是用来获取 app 实例的函数,一般情况下没啥问题,但是在几个特殊的场景下它会给你带来意想不到的 bug。
在 app.js 中的 onLaunch 回调函数中使用
// app.js
App({
onLaunch() {
console.info(getApp(), '!!');
}
});
你会发现这个时候会输出 undefined ,这其实可以理解,毕竟这个时候还在初始化阶段,app 还没出生。如果代码仅仅是这么简单的话,那你可以很容易的发现这个问题,一旦你在其中调用了一个方法,而这个方法又不小心在获取 app 实例来获取变量,那意外就产生了。
So~ 如果没有同步要求,可以在 onLaunch 中调用函数可以包一层 setTimout 避免踩坑:
// app.js
App({
onLaunch() {
setTimeout(() => {
// ... 调用其他函数
});
}
});
将 getApp() 赋值给一个变量
我们创建一个 a.js 文件:
// a.js
const app = getApp();
export function checkVersion() {
console.log('checked!');
console.info('app', app, '!!');
}
上面的文件一般情况下不会遇到什么问题,但是一旦你在 app.js 中引入,那惊喜就产生了。
// app.js
import { checkVersion } from 'a';
App({
onLaunch() {
console.log('I\'m Fine!');
},
onShow() {
checkVersion();
}
});
这个时候你会发现 app 变量是 undefined,这种错误你可能很难发觉,特别是封装的一个通用库,平时好好的,但是突然在 app.js 中使用了一下,就哎嘿了。
So~ 为了避免这种问题,尽量减少公用 app 实例共享,而是在方法中直接使用 getApp() 来获取实例对象。如果要使用全局变量,可以单独一个 js 文件来单独存储变量会更好,比如:
// globalStore.js
export default {
userInfo: null,
isIos: false,
isLaunched: false,
};
// app.js
import store from 'globalStore';
App({
onLaunch() {
store.isLaunched = true;
},
onShow() {
const { isLaunched } = getApp().store,
console.log(isLaunched);
},
store,
});
这样既可以通过导入模块的方式获取全局变量,又可以兼容通过 getApp().store 来获取全局变量。
但是原则上我还是建议通过导入模块的方式来读写全局变量,毕竟在某些情况下 getApp() 返回了 undefined 。
在页面入口文件顶部定义变量
在页面入口文件定义变量很常见,但是你一定要注意的是,页面的入口文件只会执行一次,并不是每个页面实例独立的,比如下面的代码:
// pages/page/index.js
import { getDetailInfo } from 'api';
let ajaxLock = false;
Page({
onLoad() {
this.getRemoteData();
},
async getRemoteData() {
if (ajaxLock) {
return;
}
ajaxLock = true;
try {
await getDetailInfo();
} catch(err) {
// ... 处理错误
} finally {
ajaxLock = false;
}
},
});
页面逻辑比较简单,一进入页面就请求远程数据,目测也没啥问题,但是一旦同时打开多个该页面,你会发现只有第一个页面请求了数据,后面的页面没有请求,因为这几个页面都共享了 ajaxLock 这个变量,因此在页面顶部声明的变量,一定要注意使用场景。
页面入口文件中 data 中直接赋值全局变量
直接上代码:
// pages/page/index.js
Page({
data: {
isIos: getApp().store.isIos,
},
});
// app.js
App({
onLaunch() {
this.getSysInfo();
},
getSysInfo() {
return new Promise((resolve, reject) => {
wx.getSystemInfoAsync({
success: resolve,
fail: reject,
});
}).then((res) => {
const { store } = getApp();
store.isIos = res.platform.toLowerCase() === 'ios';
});
},
store: {
isIos: false,
},
});
上面代码的主要逻辑就是需要知道当前设备是不是 ios ,代码在模拟器上跑着似乎很稳定,但是一旦上了真机,就时好时坏了,因为 isIos 这个变量不是同步获取状态的,一旦赋值在页面入口函数执行完之后,那么状态的展现就会不正确。
因此对于某些状态不是同步赋值,千万不要直接通过在初始化的时候直接给 data 赋值的方式去操作,最好放到 onLoad 回调函数中去赋值状态。
你不知道的 wx.createSelectorQuery() and wx.createIntersectionObserver();
这两个函数也是比较常用的,wx.createSelectorQuery 主要用来查询某个元素,wx.createIntersectionObserver 用在需要处理元素是否在可视区。这两个函数的问题目测纯纯的可爱,我也是在实现某个特殊需求的时候才发现,也真是后知后觉,不明觉厉,细思极恐...
我们来复现问题,页面入口函数这么写:
// pages/page/index.js
let index = 0
Page({
data: {
tag: 0
},
onLoad() {
if (index++ < 2) {
wx.navigateTo({
url: '/pages/page/index'
});
}
this.setData({
tag: index
},() => {
setTimeout(() => {
const { tag } = this.data;
const query = wx.createSelectorQuery();
// const query = this.createSelectorQuery();
query.select(`.c-${ tag }`).boundingClientRect();
query.exec((res) => {
console.log(tag, res);
});
}, 2000);
});
}
});
<!-- 模板文件 -->
<view class="c-{{tag}}">demo</view>
我模拟了同时打开多个页面的情况,在开发者工具中你就会发现,前两个页面的结果居然是 null !!!当时我就觉得世界有点儿崩塌。因此我怀疑毕竟 wx.createSelectorQuery 是一个全局函数,因此它查询的是当前活跃窗口下的 wxml。怎么解决呢,我翻了翻官方文档,用放大镜找了找小字,发现有 this.createSelectorQuery 这个方法,抱着试一试的态度,问题就突然解决了。当然 wx.createIntersectionObserver 也是同样的问题,我就不做演示了。
So~ 为了身体好,我强烈建议直接使用 this.createSelectorQuery 和 this.createIntersectionObserver 。
以上是我这几年开发微信小程序踩过的神坑,望你不要再踩上~~
总结
到此这篇关于微信小程序开发之你可能没有踩过的坑的文章就介绍到这了,更多相关微信小程序开发的坑内容请搜索极客世界以前的文章或继续浏览下面的相关文章希望大家以后多多支持极客世界! |
请发表评论