1. 程式人生 > 程式設計 >微信小程式開發之你可能沒有踩過的神坑總結

微信小程式開發之你可能沒有踩過的神坑總結

目錄
  • getApp()
  • 在頁面入口檔案頂部定義變數
  • 你不知道的 wx.createSelectorQuery() and wx.createIntersectionObserver();
  • 總結

getApp()

getApp() 函式是用來獲取 app 例項的函式,一般情況下沒啥問題http://www.cppcns.com,但是在幾個特殊的場景下它會給你帶來意想不到的 bug。
在 app. 中的 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 中直接賦值全域性變數

直接上程式碼:

//http://www.cppcns.com 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 。

以上是我這幾年開發微信小程式踩過的神坑,望你不要再踩上~~

總結

到此這篇關於微信小程式開發之你可能沒有踩過的坑的文章就介紹到這了,更多相關微信小程式開發的坑內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!