導讀 | 近日,騰訊開源跨端框架 Hippy,一週即吸引3000+star。在騰訊內部,Hippy 已運行3年之久,跨 BG 共有 18 款線上業務正在使用 Hippy,日均 PV 過億,且已創建一套完整生態。html
相較於其餘跨端框架,Hippy 對前端開發者更友好:緊貼 W3C 標準,聽從網頁開發各項規則,使用 JavaScript 爲開發語言,同時支持 React 和 Vue 兩種前端主流框架。本文將爲你們介紹Hippy 經常使用調試方法和常見問題案例,但願可以幫助開發者快速上手。前端
前端調試在官網 [1] 已經有專門章節進行描述,就很少說,這裏具體說一下調試常見問題、案例和一些基本原理。vue
Hippy 已經在 hippy-debug-server [2] 中集成了一套基於 Chrome DevTools Protocol [3] 的調試服務器,啓動後在終端進入本地調試界面,即可以進入遠程調試模式。react
目前 iOS 和 Android 都已經支持了真機調試,Android 經過 adb reverse 命令直接實現了本地調試端口的轉發。就是指在手機上訪問 localhost:38989 的調試端口時,訪問的實際是開發機上的 38989 端口。webpack
可是 iOS 須要終端和前端的雙方面配合修改端口才能夠作到真機調試,因此建議先經過 iOS 模擬器進行調試工做。ios
啓動調試服務、進入終端的本地調試環境後,JavaScript 代碼將會經過調試服務加載到真機中運行。若是代碼沒問題應該能正常運行,但有時候會碰到啓動就 Crash 的狀況,能夠參考常見案例最後一條「iOS 版本低於 9 時模擬器報告 SyntaxError」。git
一樣的,iOS 上某些特性有的能用 Polyfill 解決,但有的不行(例如 Proxy、正則表達式的 Sticky Flag 等就須要 iOS 10 以上纔可使用,並且沒法 Polyfill),因此若是要兼容低版本 iOS,要注意不能使用到太新的 JS 特性。github
有的 App 調試模式下運行很正常,可是打完包集成進去之後發現不行,這時候咱們須要用到整合後的 jsbundle 包調試大法了。值得注意的是:該方案暫時只適用於 iOS。web
其實很是簡單,Hippy 在 iOS 中是經過自帶的 JavaScriptCore 運行的,因此能夠經過自帶的 Safar 進行調試,在 Safari 的設置 -> 高級打開開發者菜單後 ,啓動 Hippy 就能看到多出了一個模擬器設備。正則表達式
Safari 調試菜單位置
而後就能夠用 Safari 開始調試了。惟一要注意的是,斷點須要在啓動後才生效,啓動時是斷不下來的,啓動問題能夠在關鍵點加上日誌,日誌可以正常輸出。若是是其它啓動後問題,能夠直接打斷點,跟 Chrome 調試服務的使用方法基本一致。
整合後包打斷點
前端開發廣泛對內存佔用缺少概念,直到終端同窗過來講 JS 內存佔用太多把 App 搞崩潰了纔回過神來。
JavaScript 目前主要以標記清除算法 [4] 的方案來進行內存回收,它的核心是按期從全局對象中遍歷全部對象,而且對不可到達的對象進行標記,並進而清除。
在絕大多數狀況下做爲前端開發確實不須要關心內存佔用,可是 Hippy 中不太同樣,Hippy 是前端的開發方式去開發終端 App,有幾個類在組件卸載時必定要記得銷燬。
包含了 React 中負責事件監聽的 EventEmitter 實例、Animation/AnimationSet 動畫組件,Vue 中的 $app.on() 終端事件監聽等等,不釋放掉它們,它們就會一直佔用着內存,隨着界面愈來愈多,App 最終將會崩潰。
其實調試方法也很是簡單,直接在調試器的 Memory 觀察內存佔用狀況,打快照看一下當時各種對象對內存的佔用狀況,它是 Hippy 在瀏覽器裏運行的容器,能夠表明 App 的總體內存佔用狀況。
內存調試方案
固然,這部份內容 Google 官方也有文檔 [5],感興趣的同窗也能夠去檢索查看。
這是常常碰到的,最直接的方式是對 React 和 Vue 進行界面繪畫的模塊 - UIManagerModule 的三個方法 :createNode、updateNode、deleteNode 打斷點。
其實無論 MVVM 怎麼作,最終都會經過這三個方法把界面通知終端畫上去,這其實也帶來了無限的擴展性,任何框架只要對接了這三個方法就能夠進行 Hippy 繪製。
若是掌握了 UIManagerModule 的語法,甚至不須要 React 或者 Vue 也能夠直接經過它畫界面。
但從必定程度上來說,Hippy 畫界面的方式其實跟瀏覽器是不同的,它是異步的。
MVVM 組件建立完畢,componentDidMount 或者 mounted 後,其實並不意味着界面真的畫上去了(可是這個耗時極少,mounted 後基本能夠認爲真的畫上去了)。
若是要對界面進行操做,須要肯定終端確實畫上去了才行,這能夠經過 onLayout 事件得到。其次能夠看到畫界面和普通的 Native Module 調用沒有本質區別,最終都要經過 JSBridge 進行通信。-- 這部分正在經過 C++ 方式重寫。
經過觀察它,咱們能夠了解到最終經過 React、Vue 解析後的組件是什麼樣的,能夠觀察到爲何界面沒有更新,或者樣式不如預期。
Hippy 的前端框架在開發初期就考慮到了調試的便利性,調試模式下會將前端框架與終端之間的通信都打印到 Console 裏,當以爲本身的業務 App 或者框架顯示存在問題時,直接觀察它就能很方便得到全部信息。
以 Hippy-Vue 爲例:
Hippy-Vue 的終端通信日誌
Hippy-Vue 要關閉該功能只要將入口文件中的 Vue.config.silent 改成 true 便可;Hippy-React 要關閉該功能須要在啓動參數裏增長一個 silent: true。不過通常不建議關閉,它在打包後會自動中止輸出。
在 Hippy 中只有這兩種 View 是能夠滾動的,剩下的都不能夠滾動,可是要讓它們能滾起來也不是那麼簡單,須要有樣式進行配合,簡單說就是:
ScrollView(Vue 的 div + overflow-x/y:scroll) 以上全部父節點都必須有一個固定的高度,ScrollView 中只能嵌套一個內容子節點,它能夠隨意變高。
ListView (Vue 的 ul/li)以上全部父節點都必須有一個固定的高度,裏面全部的 renderRow 出來的 ListItemView(Vue 中的 li)能夠隨意變高。
這裏的固定高度能夠是直接指定高度,也能夠是經過 flex 進行界面動態分割的高度。可是必定要是固定的,由於滾動實際是終端去實現的,它須要可以區分能夠滾動和不能夠滾動的區域,若是容器高度和內容高度同樣,那就變成不能夠滾動了。
另外 Vue 裏的 ul 默認已經加上了 flex: 1 樣式會把整個 View 撐滿屏幕,通常狀況下不用作特別處理,可是 div + overflow-x/y: scroll 依然須要手工指定高度。
當滾動出現異常的時候,能夠經過 XCode 調試一下終端代碼,它有個 Debug View Hierarchy 功能,能夠很是直觀地看到界面層級和尺寸,對調試樣式問題有很大幫助。
XCode 的界面層級調試
這裏須要提到前端三點很是須要注意的地方:
(1)界面發生異常閃爍
首先須要經過第一個小章節裏的UIManagerModule 觀察法,看一下那三個方法是否有異常的執行。例如 updateNode 執行過於頻繁,或者 deleteNode/createNode 異常執行,一般是因爲數據有變化致使界面重繪,能夠經過調用棧看一下是哪裏的數據更新致使界面重繪,並針對性地進行前端優化。
(2)處理 key 值
ListView 決定界面是否重繪,有個很關鍵的參數是 key(React 官文 [6]、Vue 官文 [7]),Hippy-React 也經過 getRowKey() 的方法實現了 key 在 ListView 中的應用。key 實際上是數據的惟一標示符,數據不發生改變,key 就不該該發生改變,而 key 一旦發生改變 ListView 就會重繪。
目前不少業務在開發時 key 不指定,或者把 index 做爲 key,前者會致使 ListView 每次有數據更新都作一次完整的 Array diff,開銷很是大;後者會致使刪除中間一個節點時將後面全部的節點所有刪除再從新插入一次,開銷也很是大。
出於性能考慮,key 是必需要加的,通常跟數據的主鍵保持一致便可。可是:若是 ListView 中的數據須要進行排序,那就不要指定 key 了。
目前 Hippy 的 moveNode 功能,已經計劃但仍未完成,指定 key 後在從新排序時會由於對應索引的 key 值不一樣,先刪除所有節點內容,再所有重建,可能會形成輕度閃爍。若是此時不指定 key,就只有一個更新節點的請求,兩次請求合併爲一次,終端層會對數據進行對比並更新節點內容。
(3)終端渲染
若是到這一步終端渲染依然很慢、幀率低,咱們就要提到另一個參數 type 了,對應到 Hippy-React 裏是 getRowType() 方法,它是用來表示組件樣式的,樣式不變,type 就不變。
這裏須要先說一下 Hippy ListView 的複用機制,當不指定 type 時,每次有新的ListItemView被渲染(HippyReact 裏 renderRow() 將返回 ListItemView、Hippy-Vue 裏的 li),終端都會從新構建全部終端組件節點。
加了 type 以後,會將將以前渲染過的終端組件節點放到緩存池中,下次碰到相同 type 類型的 ListItemView,就不會從新渲染,而是從緩存池中把緩存的節點拿出來作次拷貝並更新數據,再上屏,即便只有一個樣式的 ListItemView,經過 type 也能作到性能優化。
通過上面三步,基本能夠解決 90% 的 ListView 性能問題。一樣的,Hippy-Vue 官方範例中也對這三個參數加了註釋。
首先須要檢查 numberOfRows 參數是否真的是 ListView 中 ListItemView 的數量,這個除了在業務代碼中打斷點查看數據數量是否和 numberOfRows 一致之外,也能夠經過第一個 UIManagerModule 的調試方法查出來。
這個問題牽扯到 iOS 上一個 ListView 的上屏性能優化,iOS 上並非發一個 ListItemView 就上屏一個的。而是須要先改變 ListView 的 numberOfRows 再去建立節點,當節點數量與 numberOfRows 一致時再上屏。
目前碰到的全部不渲染的問題都是由於這個緣由形成的。
另外在 Hippy-Vue 中,對於靜態的 li(就是終端的 ListItemView),能夠不須要手工指定 numberOfRows,Hippy-Vue 會在 DOM 層計算子節點數量。
可是對於動態獲取的數據,也必需要加上該參數,由於 Hippy-Vue 位於 Vue 的渲染層,跟業務還隔了一個 Vue,沒法知道業務到底有多少數據準備要渲染。
這裏須要提到 Hippy App 的啓動方式:當終端 JS 引擎加載完 JavaScript 後,會從 GLOBAL.appRegister對象裏去尋找終端指定的 moduleName,而 __GLOBAL__.appRegister 是在 Hippy 啓動時經過 HippyRegister.regist() 方法註冊上的。
在 Hippy-React 入口文件或者 Hippy-Vue 入口文件定義的 appName 最終都會執行到 regist() 方法上進行 __GLOBAL__.appRegister 的註冊,因此,首先咱們要檢查終端的 moduleName 是否和 appName 一致。
若是一致依然出錯的話,很大概率是以前 JS 執行失敗,也不排除 SDK 更新後存在 bug,也有可能其它問題,會致使 __GLOBAL__.appRegister 未註冊成功。
但咱們能夠在該錯誤拋出時二次確認一下終端所尋找到 moduleName 是否和前端定義的 appName 一致,只要在那一行打上日誌,而後使用上文的 Release 包調試方案檢查終端過來查的究竟是什麼 appName,問題就能夠獲得解決。
這是由於 Hippy 自帶的 Webpack 默認調試模式配置文件,最低僅開啓了 iOS 9 的輸出,由於輸出到 iOS 8 會多出不少 polyfill,語法上也會轉換,致使體積大不少。
Hippy 自己最低支持的 iOS 8,咱們建議在高版本的 iOS 上進行調試,而後打包後在低版本 iOS 走一遍測試流程,沒什麼問題便可。
若是非要在低版本的 iOS 上進行調試,修改一下 webpack 配置文件 iOS 將 preset-env 中的 ios 版本改爲更低便可,但目前通過測試 core-js 對 iOS 8 那樣對低版本可能存在問題,這就須要你本身手工調整了。
參考資料:
[1] Hippy 前端調試詳解:https://tencent.github.io/Hippy/#/guide/debug
[2] 關於 hippy-debug-server :https://www.npmjs.com/package/hippy-debug-server
[3] Chrome DevTools Protocol 簡介:https://developer.chrome.com/devtools/docs/integrating
[4] 標記清除算法:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management#Mark-and-sweep_algorithm
[5] 解決內存問題的Google官方文檔:https://developers.google.com/web/tools/chrome-devtools/memory-problems/
[6] React 官方文檔:https://reactjs.org/docs/lists-and-keys.html#keys
[7] Vue 官方文檔:https://vuejs.org/v2/guide/list.html#Maintaining-State
歡迎關注「雲加社區」,Hippy 的實戰和原理解析系列文章將會陸續上線。