[移動端新特性] Passive Event Listeners

滴滴出行·DDFE發表於2019-02-20

作者:滴滴公共前端團隊 – 小春

Passive Event Listeners 這個東西其實有一段時間了,關注 2016 Google I/OMobile Talk 的同學應該有些印象。

PS:

建議一些新技術方向探索的同學關注一下每一年的 Google I/O,而一般大會之後,像類似 medium 網站的一些大咖會有英文的分解。

基本我們很多技術革新或者應用產品化都會從這裡面得到一些思考和啟發:比如 Polymer 等。

一般新東西我都會問幾個問題?

1、它有什麼用?

A new feature in the DOM spec that enable developers to opt-in to better scroll performance by eliminating the need for scrolling to block on touch and wheel event listeners.

Developers can annotate touch and wheel listeners with {passive: true} to indicate that they will never invoke preventDefault.

提高頁面的滑動流暢度

PS:

FB 有一個報告:
頁面滑動的響應重新整理率從 60 FPS 到 30 FPS,會讓使用者的參與度極速下降

很多人可能對這個資料表現不是很瞭解,有沒有比較主觀的資料統計呢?

rbyers.github.io/scroll-late…

如下截圖所示:

[移動端新特性] Passive Event Listeners

我們再回顧一下上面的那段話:

1、設定新屬性 passive

2、比如在監聽 mousewheel 或者 touch 事件中,增加了 passive 為 true 的設定,它就不會呼叫 preventDefault 來阻止預設滑動行為

3、或者叫:被動監聽器

程式碼片段如下:

function handler () {
    console.log(`DDFE`);
}
document.addEventListener(`mousewheel`, handler, {passive: true})複製程式碼

大家發現:

其實變化就是:前端 DOM 中常用常考的 addEventListener 的第三個引數:之前都是 true | false

2、相容性?

Chrome 51 開始
Firefox 49

也已經有人做了補丁包:

github.com/WICG/EventL…

github.com/zzarcon/def…

大概的設計流程:

1.判斷是否支援:
var supportsPassive = false;
document.createElement("div").addEventListener("test", function() {}, {
    get passive() {
        supportsPassive = true;
        return false;
    }
}
`複製程式碼
2.覆蓋內建的 3 個原型鏈方法:
// 賦值預設事件原型鏈的 addEventListener
var super_add_event_listener = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = function(type, listener, options) {
    var super_this = this;
    //...
    var wrapped = {
        handleEvent: function (e) {
            e.__passive = passive;
        }
    };
    //...
    super_add_event_listener.call(super_this, type, wrapped, useCapture);

    //...
    super_add_event_listener.call(super_this, type, listener, useCapture);
}複製程式碼
// 賦值預設事件原型鏈的 removeEventListener
var super_remove_event_listener = EventTarget.prototype.removeEventListener;
EventTarget.prototype.removeEventListener = function(type, listener, options) {
    var super_this = this;
    //...
    super_remove_event_listener.call(super_this, type, listener, useCapture);
}複製程式碼
// 賦值預設事件原型鏈的 preventDefault
var super_prevent_default = Event.prototype.preventDefault;
Event.prototype.preventDefault = function() {
    // 判斷是否設定了
    if (this.__passive) {
 console.warn("Ignored attempt to preventDefault an event from a passive listener");
 return;
    }
    super_prevent_default.apply(this);
}複製程式碼

參考文獻:

developers.google.com/web/updates…

medium.com/@devlucky/a… (自備梯子)

github.com/WICG/EventL…

dom.spec.whatwg.org/#dom-eventl…

blog.chromium.org/2016/05/new…

stackoverflow.com/questions/3…

blog.csdn.net/tengxy_clou…

PS:本文插圖來自 sofish 大大,感謝


歡迎關注DDFE
GITHUB:github.com/DDFE
微信公眾號:微信搜尋公眾號“DDFE”或掃描下面的二維碼

[移動端新特性] Passive Event Listeners

相關文章