小程序操作dom


小程序不能使用各種瀏覽器暴露出來的 DOM API,進行 DOM 選中和操作

原因:在小程序中,渲染層和邏輯層是分開的,分別運行在不同的線程中,邏輯層運行在 JSCore 中,並沒有一個完整瀏覽器對象,因而缺少相關的DOM API和BOM API。

為什么要這樣設計?

因為JavaScript是可操縱DOM的,如果JavaScript線程和UI線程同時運行,即在修改這些元素屬性同時渲染界面,那么渲染線程前后獲得的元素數據就可能不一致,導致傳統web開發渲染線程和腳本線程是互斥的。於是當JavaScript引擎執行時GUI線程會被掛起,GUI更新會被保存在一個隊列中等到引擎線程空閑時立即被執行。 因此長時間的腳本運行可能會導致頁面失去響應。

小程序的通信過程

小程序的邏輯層和渲染層是分開的兩個線程, 小程序的運行環境分成渲染層和邏輯層,其中 WXML 模板和 WXSS 樣式工作在渲染層,JS腳本工作在邏輯層。

在渲染層,宿主環境會把WXML轉化成對應的JS對象,在邏輯層發生數據變更的時候,我們需要通過宿主環境提供的setData方法把數據從邏輯層傳遞到渲染層,再經過對比前后差異,把差異應用在原來的Dom樹上,渲染出正確的UI界面。

小程序的渲染層和邏輯層分別由2個線程管理:渲染層的界面使用了WebView 進行渲染;邏輯層采用JsCore線程運行JS腳本。一個小程序存在多個界面,所以渲染層存在多個WebView線程,這兩個線程的通信會經由客戶端做中轉,邏輯層發送網絡請求也經由Native轉發,小程序的通信模型下圖所示。

雙線程的優劣:

  1. 小程序是基於雙線程模型,那就意味着任何數據傳遞都是線層間的通信,也就是統一都會有一定的延遲,不像傳統web,當界面需要更新時,通過調用更新接口UI就會同步地渲染出來。在小程序的架構里,一切都是異步。
  2. 除了邏輯層與渲染層之間的通信有延時,各層與客戶端原生交互同樣是有延時的。
  3. 異步帶來的好處,頁面也js並行執行,不會出現傳統web端js阻塞頁面渲染的情況。

小程序DOM操作API —— SelectQuery

首先我們來看dom操作在瀏覽器和小程序中用法的區別:

瀏覽器中:

const el = document.querySelector('#the-id')
console.log(el.boundingClientRect().top)

小程序中:

const query = wx.createSelectorQuery()
// 組件中:const query = wx.createSelectorQuery().in('組件id')
query.select('#the-id').boundingClientRect()
query.exec(function (res) {
    console.log(res[0].top)
})

可以發現有三點明顯的區別:

  1. 通過createSelectorQuery創建了一個query對象而不是document.querySelector
  2. 在query對象上執行查詢操作並非馬上執行,而是進入等待隊列,直至query對象上exec被調用才觸發查詢行為
  3. 查詢結果是異步返回的,在callback中按查詢順序依次從參數中讀取

第一點區別產生的原因是首先小程序雙線程模型決定了業務代碼中不能拿到document對象, 也就無法在上面調用相關查詢方法。那為什么要通過調用createSelectorQuery()返回查詢對象而不是把查詢對象定義為全局的呢?這是因為小程序每個頁面的視圖層都對應一個webview,而所有的頁面都共用一個邏輯線程,這種一對多的關系在通訊時需要有id來進行區分,所以每次createSelectorQuery時返回的query對象是綁定了當前視圖層webview對應id的。

第二、三點的查詢非立即執行,而是在exec被調用時真正觸發比較好理解:業務代碼在邏輯線程,真實dom在另一個webview線程,線程間的通訊需要借助宿主能力完成,query對象的exec方法被觸發時小程序把callbak存儲起來,然后調用native宿主暴露的方法去通訊,拿到webview線程查詢回來的結果之后進行反序列化處理,傳遞給先前保存的callbak並開始執行,所以這里是異步的。


免責聲明!

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



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