從零開始的微信小程序入門教程(四),理解小程序事件與冒泡機制


壹 ❀ 引

我在之前初識WXML與數據綁定兩篇文章中,介紹了小程序靜態模板與樣式相關概念,以及小程序幾種常用數據綁定方式,在知道這些知識后,我們可以寫一些不算復雜的小程序頁面,並能將一些自定義的數據渲染到視圖層,這非常棒。那么本文我們將繼續介紹小程序中比較重要的事件概念,在學習完事件后,我們可以讓小程序具備一定的交互性,那么本文開始。

貳 ❀ 初識小程序事件

在小程序中,事件是視圖層到邏輯層的通訊方式

比如,我們可以將事件綁定在組件上,當用戶操作該組件並觸發事件時,事件會將用戶行為反饋到邏輯層做處理,也就是對應的執行邏輯層中的事件處理函數。

當然,有時候行為反饋不一定是由用戶來主動觸發,舉個生活中的例子,我們在騰訊視頻看龍嶺迷窟時,當播放到一集結尾,視頻會自動播放下一集。將這個例子拿到小程序中來說,video組件便自帶了bindended事件,只要視頻播放到末尾便會觸發該事件,小程序中存在很多由組件自身提供的事件,所以綜合來說,小程序中的事件由用戶行為反饋事件組件狀態反饋事件兩部分組成。

微信小程序除了WXML,WXSS文件之外,還提供了WXS腳本語言,為什么突然扯到這個呢,因為從基礎庫版本2.4.4開始,支持使用WXS響應事件。針對於IOS環境,WXS腳本執行速度是JavaScript的2-20倍,安卓環境沒啥差別,大體上來理解,用WXS解決事件問題具備一定優勢。由於我們目前暫未了解WXS腳本,所以這里先不做探討,后面會專門另起一篇文章介紹WXS腳本以及事件相關說明。學習總不能一口吃成胖子,我們一步步來。

所以本文還是主要圍繞用戶行為反饋事件展開討論,讓大家對於事件先有個基本概念。

還記得上篇文章中我們接觸的第一個點擊事件tap嗎?我們來重現它,並以此加深對於事件的理解、

首先,我們在index.wxml中與index.js中添加如下代碼:

<button bindtap="alert">bindtap</button>
Page({
  data: {},
  alert: function (event) {
    wx.showToast({
      title: '觸發成功', // 標題
      icon: 'success', // 圖標類型,默認success
      duration: 1500 // 提示窗停留時間,默認1500ms
    })
  }
})

有上述例子可知,實現一個事件綁定主要分為兩步,第一步我們通過bindtap綁定了一個函數alert,第二我們在Page構造器中定義對應的事件處理函數alert。當戶點擊button組件時,該組件就會在Page中找到對應的事件函數並執行,這便是一次視圖到邏輯的通訊過程。

需要注意的是,此時我們使用的事件是tapbind只是一個事件前綴,這就像我們用原生JS事件時所有事件前都得加一個on,比如onclick,onchange,這是同一個道理。

除此之外,小程序中的事件支持bindtap與bind:tap兩種寫法,怎么用都行。

<button bindtap="alert">bindtap</button>
<button bind:tap="alert">bindtap</button>

好了,在了解了事件基本概念后,我們來一一介紹小程序中提供的用戶交互事件。

由於大部分事件均與手指觸碰有關,所以為了大家更好的感受各個事件的作用,這里我推薦大家開啟小程序調試器的自動預覽,如下

點擊編譯並預覽后,登陸的微信賬號即可預覽我們的小程序項目,如果你修改了代碼,記得手動點一次編譯並預覽按鈕。

叄 ❀ 常見事件類型

注意,這里的事件類型均為用戶行為反饋事件,相關說明直接引用官網,有個小規律,小程序中所有事件名均為小寫單詞拼接,無駝峰拼接情況,這點大家記住。

事件類型 觸發條件
touchstart 手指觸摸動作開始觸發
touchmove 手指觸摸后移動觸發
touchcancel 手指觸摸動作被打斷,如來電提醒,彈窗
touchend 手指觸摸動作結束
tap 手指觸摸后馬上離開
longpress 手指觸摸后,超過350ms再離開,如果指定了事件回調函數並觸發了這個事件,tap事件將不被觸發(與tap同時定義,優先級更高)
longtap 手指觸摸后,超過350ms再離開(推薦使用longpress事件代替)
transitionend 會在 WXSS transition 或 wx.createAnimation 動畫結束后觸發
animationstart 會在一個 WXSS animation 動畫開始時觸發
animationiteration 會在一個 WXSS animation 一次迭代結束時觸發
animationend 會在一個 WXSS animation 動畫完成時觸發

現在,我們通過例子一一加深印象,還是使用上文提供的tap事件例子,JS代碼不變,我們只需要切換事件名即可:

  1. touchstart事件
<button bindtouchstart="alert">bindtap</button>

模擬器可能還不是很明顯,大家如果通過手機預覽可以發現,由於button組件按下去有個背景變灰的漸變,而touchstart事件即是手指觸碰到組件的一瞬間方法就被執行,此時按鈕還沒完全按下去,大家多體驗幾次。

  1. touchmove事件
<button bindtouchmove="alert">bindtap</button>

這個就非常明顯了,手指按下按鈕完全變灰后沒執行,一定要我們按住手指並拖動才會觸發。

  1. touchcancel事件
<button bindtouchcancel="alert">bindtap</button>

這個我交大家怎么模擬,在手機上用左手點擊button組件不要放開,用右手點擊小程序更多功能按鈕,此時會彈窗,由於觸碰被打斷,可以發現事件被順利觸發。

其次,左手按鈕button,點擊關閉小程序,也就是這個按鈕,此時小程序會暫時退出並保存在手機后台中,通過后台直接再進入小程序,我們會發現touchcancel事件也會觸發。

  1. touchend事件
<button bindtouchend="alert">bindtap</button>

點擊按鈕,長按拖動都不會觸發,直到手指離開屏幕便會觸發。

  1. tap事件
<button bindtap="alert">bindtap</button>

上文中給了例子,雖然官方說手指觸碰后馬上離開觸發,事實證明我按住按鈕十幾秒后離開,也會觸發,我默認理解為click事件。

  1. longpress事件
<button bindlongpress="alert">bindtap</button>

快速點擊快速松開並不會觸發該事件,只有點擊超過350ms時才會觸發。

  1. 與動畫相關的API,由於涉及到了小程序動畫,這里先通過官方動畫例子展示API作用,動畫怎么玩后面再做介紹(留個坑...)。
<view class="box {{extraClasses}}"
  bindtransitionend="transitionEnd"
  bindanimationstart="animationStart"
  bindanimationiteration="animationIteration"
></view>

<button class="btn" bindtap="triggerTransition">觸發CSS漸變</button>
<button class="btn" bindtap="triggerAnimation">觸發CSS動畫</button>
.box {
  width: 100rpx;
  height: 100rpx;
  margin: 60rpx;
  background: red;
}
.btn {
  margin: 30rpx 60rpx 0;
}

.box-transition {
  transition: all 0.5s;
}
.box-moved {
  margin-left: 590rpx;
}

@keyframes box-ani {
  from {margin-left: 60rpx}
  to {margin-left: 590rpx}
}
.box-animation {
  animation: box-ani 1s alternate infinite;
}

const app = getApp()

Page({
  data: {
    extraClasses: '',
  },
  triggerTransition: function () {
    if (this.data.extraClasses == 'box-transition box-moved') {
      this.setData({
        extraClasses: 'box-transition'
      })
    } else {
      this.setData({
        extraClasses: 'box-transition box-moved'
      })
    }
  },
  triggerAnimation: function () {
    this.setData({
      extraClasses: 'box-animation'
    })
  },
  transitionEnd: function () {
    console.log('漸變已結束')
  },
  animationStart: function () {
    console.log('動畫已開始')
  },
  animationIteration: function () {
    console.log('動畫進行中')
  },
  animationend:function () {
    console.log("動畫結束")
  }
})

肆 ❀ 事件對象

在事件處理函數中,我們能接受到一個event對象參數,該參數包含了當前事件類型,以及當前組件相關信息,具體屬性如下:

屬性 類型 說明
type String 當前綁定的事件類型
timeStamp Integer 頁面打開到觸發事件所經過的毫秒數
target Object 觸發事件的組件的一些屬性值集合
currentTarget Object 當前組件的一些屬性值集合
detail Object 額外的信息
touches Array 觸摸事件,當前停留在屏幕中的觸摸點信息的數組
changedTouches Array 觸摸事件,當前變化的觸摸點信息的數組

我們來看個例子:

<button bindtap="alert" id="btn" data-name="聽風是風" data-age="27">bindtap</button>
Page({
  data: {},
  alert: function (event) {
    console.log(event);
  }
})

通過點擊按鈕任意區域,可以看到控制台輸出如下數據:

可以看到組件上自定義的data-**數據,組件X軸Y軸偏移量,鼠標點擊時的坐標等等信息均有記錄。

伍 ❀ 復習事件冒泡與理解小程序事件捕獲/冒泡

我們知道,在JavaScript事件監聽中,分為捕獲階段--目標階段--冒泡階段三個部分。考慮到有同學對於該部分知識有遺忘,這里做個簡單補充。

我們先來看個JavaScript的例子:

<div class="parent">
    父
    <div class="child">
        子
    </div>
</div>
.parent{
    width: 200px;
    height: 200px;
    background-color: #bbded6;
}
.child{
    width: 100px;
    height: 100px;
    background-color: rgba(255,80,47,1);
}
var parent = document.querySelector(".parent"),
    child = document.querySelector(".child");

parent.addEventListener("click", function () {
    console.log("A");
}, true);
parent.addEventListener("click", function () {
    console.log("B");
});
child.addEventListener("click", function () {
    console.log("D");
}, true);
child.addEventListener("click", function () {
    console.log("C");
}, false);

現在,讓我們用鼠標點擊紅色區域,猜猜會一次輸出什么呢?

在說答案之前,我們先復習下addEventListener事件監聽參數以及含義:

element.addEventListener(event,function,useCapture);

其中event與function為必填,event表示事件類型,function為事件處理函數,而useCapture為選填,它的值為Boolean值。用於指定事件是否在捕獲或者冒泡階段執行,默認為false,即在冒泡階段執行,反之在捕獲階段執行。

而上文的例子中,我們用了一個父盒子包裹了一個子盒子,拋開根元素以及body,捕獲與冒泡如下:

捕獲階段:parent--child

冒泡階段:child--parent

再觀察上述例子關於useCapture的值,所以答案是ADCB

OK,花了點時間用於解釋JS事件監聽的冒泡概念。小程序中事件同樣存在捕獲與冒泡階段。

比如我們前面所說的bind前綴就表示事件在冒泡階段執行,而如果我們想事件在捕獲階段執行,可以在bind前面加上capture,即capture-bind表示捕獲階段執行。

微信小程序中的捕獲與冒泡執行與JS事件監聽保持一致,這里引用官方一張圖解就一目了然了:

<view
  id="parent"
  bind:tap="tap1"
  capture-bind:tap="tap2"
>
  outer view
  <view
    id="child"
    bind:tap="tap3"
    capture-bind:tap="tap4"
  >
    inner view
  </view>
</view>
#parent {
  display: block;
  background-color: #bbded6;
  color: #fff;
}
#child{
  background-color: rgba(255,80,47,1);
}
Page({
  data: {},
  tap1: function (event) {
    console.log('tap1');
  },
  tap2: function (event) {
    console.log('tap2');
  },
  tap3: function (event) {
    console.log('tap3');
  },
  tap4: function (event) {
    console.log('tap4');
  },
})

當我們點擊inner view區域,可以看到依次輸出如下:

為了防止大家疑惑,這里我們先做個知識小結,首先我們前面說了事件支持bind與bind:兩種寫法,這兩種寫法都不會阻止冒泡,所以如果大家分別給父子綁定bind事件,點子區域,會先執行子的bind再執行父的bind,畢竟我們沒使用capture定義捕獲階段,所以全程就只有冒泡。記住了,bind不會阻止冒泡,添加capture前綴可以響應捕獲階段。

那么問題來了,假設我們想阻止冒泡呢?這時候就得將bind替換為catch事件了,catch也支持兩種寫法catch與catch:,我們來試試下面這個例子:

<view
  id="parent"
  catch:tap="tap1"
>
  outer view
  <view
    id="child"
    catch:tap="tap3"
  >
    inner view
  </view>
</view>

現在點擊inner view區域,可以發現只輸出了tap3,因為冒泡被阻止了。

那有同學又要問了,如果我在catch前添加capture前綴會怎么樣?怎么樣咱們修改例子試試不就知道了,看下方例子:

<view
  id="parent"
  bind:tap="tap1"
  capture-catch:tap="tap2"
>
  outer view
  <view
    id="child"
    bind:tap="tap3"
    capture-catch:tap="tap4"
  >
    inner view
  </view>
</view>

我們將capture-bind都改為capture-catch,可以發現不管點擊父區域還是子區域,都只會輸出一個tap2,這是因為capture-catch會中斷捕獲階段和取消冒泡階段。所以不管點擊哪,都是從捕獲階段開始,先捕獲到父,然后中斷捕獲,也不會存在冒泡了,就這么個意思。

OK,來個小總結。

bind不會阻止冒泡,但如果想抓捕獲階段,可以添加前綴capture,也就是capture-bind

catch會阻止冒泡,如果添加capture前綴,捕獲階段會中斷的同時,也會阻止冒泡。

最后的最后,官方給了個貼心說明,除了上文中我們列舉的用戶交互反饋事件之外的其它任意組件狀態反饋事件,除非有特殊聲明,否則都是非冒泡事件。也就是說,上文給的用戶交互反饋事件都是冒泡事件。

好了,關於小程序冒泡機制就聊到這。

陸 ❀ 總結

又花了一天事件整理了小程序事件相關概念,通過本文學習,我想大家對於小程序事件應該有了一個清晰的概念。小程序中的事件分為用戶交互反饋事件與組件狀態反饋事件(也就是組件自帶的事件)。但總體來說,事件就是視圖層到邏輯層的通訊方式。

除此之外,我們粗略的給出了各個事件的使用區別,幫助大家在特定的需求下能快速定位應該使用哪個事件。最后,我們復習了JS事件監聽中捕獲與冒泡的基礎概念,以此為基礎來了解了小程序中的冒泡機制。知道了原來除了bind還有catch事件,以及我們還能使用capture前綴來決定是否阻止捕獲與冒泡,這對於日后開發非常有幫助。

好了,關於事件的描述就先說到這了,關於下篇文章我還沒想好寫什么,我是通讀小程序官方文檔再做的記錄,我也很怕有些知識點讀的遺漏了沒記錄清除,反正我會加油持續更新。

那么到這里,本文結束。

對了,我始終覺得我在介紹事件監聽時給的圖片配色在哪里見過,思考了一番,才想起來了這是良品鋪子包裝袋的配色,如下圖。

好了,我確實夠無聊...本文結束。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM