微信小程序开发的坑 微信小程序开发之你可能没有踩过的神坑总结
豌豆公主技术部 人气:0getApp()
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 。
以上是我这几年开发微信小程序踩过的神坑,望你不要再踩上~~
总结
加载全部内容