小程序調(diào)試技術(shù)導(dǎo)讀

近期公司內(nèi)在自研小程序吨铸,我負(fù)責(zé)其中的調(diào)試部分,主要面向于開發(fā)者工具祖秒。

本篇文章是導(dǎo)讀文章诞吱。

調(diào)試能力從0到1一共經(jīng)歷了4個版本,接下來的文章將會以這4個版本為主線分別進(jìn)行介紹竭缝。

初始版

初始版通信關(guān)系圖

上圖為調(diào)試還不存在時的一個通信關(guān)系圖房维。

在彼時已經(jīng)實(shí)現(xiàn)了邏輯代碼與渲染代碼的運(yùn)行隔離,其中邏輯代碼是運(yùn)行在一個vm中的抬纸。

  1. 渲染層通過Electron提供的IPC能力與electron進(jìn)行通信咙俩。
  2. electron持有vm的引用,在收到渲染層的請求后湿故,Electron會直接交給vm執(zhí)行暴浦。
  3. vm中運(yùn)行的代碼會通過vm的Context方法將執(zhí)行結(jié)果拋出溅话。
  4. vm收到代碼后直接通過渲染層容器BrowserView的引用通過executeJavaScript將結(jié)果返還給渲染層。

通過以上4步完成了一個簡單的渲染層歌焦、邏輯層的通信閉環(huán)飞几。這其中有渲染層代碼、邏輯層代碼独撇、preload屑墨、electron、vm纷铣、BrowserView 6個角色參與卵史。

這個階段的特點(diǎn)是:實(shí)現(xiàn)了渲染代碼與邏輯代碼的隔離,還不具備基礎(chǔ)的斷點(diǎn)調(diào)試能力搜立。

第一版

image

這一版比初始版要復(fù)雜了一些以躯,它實(shí)現(xiàn)了邏輯代碼的斷點(diǎn)能力。它的主要改進(jìn)是:

  1. 將vm轉(zhuǎn)移到了獨(dú)立的進(jìn)程中啄踊。
  2. 通過node --inspect-brk使邏輯層代碼運(yùn)行于調(diào)試狀態(tài)忧设。
  3. 由于邏輯層代碼運(yùn)行于獨(dú)立進(jìn)程中,所以使用了IPC使渲染層與邏輯層維持通信狀態(tài)颠通。
  4. 加入了可視化的調(diào)試界面址晕。可以對代碼執(zhí)行基本的調(diào)試控制操作顿锰,可以從控制臺看到渲染層的日志輸出谨垃。

它的不足之處在于無法審查DOM結(jié)構(gòu),也無法查看Network記錄硼控。

第二版

image

這一版比上一版的改進(jìn)在于可以查看Network記錄刘陶,同時也可以審查基本的DOM結(jié)構(gòu)。

運(yùn)行示例:
image

這一版將邏輯層代碼運(yùn)行于worker內(nèi)牢撼。調(diào)試審查面板采用了electron自帶的調(diào)試工具易核。

在worker內(nèi)部運(yùn)行邏輯代碼解決了Network審查的問題。electron自帶的調(diào)試工具可以以較小的成本在調(diào)試工具中增加一個Tab浪默,這里用了chrome extensions的能力牡直。

為了不影響邏輯代碼的執(zhí)行,這一版采用adapter扮演了上一版vm的角色纳决,使上層的邏輯代碼無感知的進(jìn)行了運(yùn)行時環(huán)境的遷移碰逸,adapter負(fù)責(zé)底層數(shù)據(jù)的通信。

這一版最大的難點(diǎn)在DOM結(jié)構(gòu)的審查阔加。這是因chrome extensions 運(yùn)行于邏輯層容器上饵史,而DOM信息位于渲染層容器上。有人可能會問,把擴(kuò)展放到渲染層容器上不就解決了嗎胳喷?答案是否定的湃番,因?yàn)閏onsole network source 這些能力與邏輯層嚴(yán)格關(guān)聯(lián)。而調(diào)試面板只有一個吭露,必須做出成本方面的取舍吠撮。

解決DOM審查的辦法是將渲染層與邏輯層的審查通信通道打通。這里就不得不提到chrome extensions的實(shí)現(xiàn)讲竿,chrome extensions主要由3部分組成:

  • frontend.js 這個文件運(yùn)行于調(diào)試面板tab內(nèi)泥兰。
  • backend.js 這個文件運(yùn)行于網(wǎng)頁的上下文中。
  • background.js 這個文件負(fù)責(zé)frontend.js與backend.js的通信题禀。

以下這張圖簡單的描述了它們?nèi)咧g的關(guān)系:


image

這里以已經(jīng)非常成熟的extensions vue-devtools來做說明鞋诗。vue-devtools的結(jié)構(gòu)和上圖一樣,backend.js是負(fù)責(zé)從頁面中獲得Vue的組件樹結(jié)構(gòu)然后再通過background.js發(fā)送給frontend.js來展示的迈嘹。

而在我們的小程序中削彬,backend.js所運(yùn)行的環(huán)境中并沒有Vue的組件信息,這些信息在哪呢秀仲?它位于渲染層的運(yùn)行環(huán)境中融痛。所以我們需要做一些適當(dāng)?shù)母脑欤ɑ趘ue-devtools),如下:


image

就是將原本運(yùn)行于邏輯層網(wǎng)頁環(huán)境中的backend.js移植到了渲染層網(wǎng)頁環(huán)境中執(zhí)行啄育。而之前在邏輯層網(wǎng)頁環(huán)境中運(yùn)行的backend.js變?yōu)榱薭ackend.proxy.js酌心,它負(fù)責(zé)內(nèi)外環(huán)境的通信拌消。這里的內(nèi)是指extensions的proxy.js挑豌,外是指electron.js。渲染層中的GlueLayout.js扮演了之前的proxy.js的角色墩崩,負(fù)責(zé)backend.js與外部的通信適配氓英。

以上僅僅是打通了邏輯層與渲染層的審查通信通道,而這還不夠鹦筹。因?yàn)槲覀冃枰獙彶榈氖卿秩緦拥腄OM結(jié)構(gòu)铝阐,目前只能看到的是渲染層的Vue組件結(jié)構(gòu)。所以還需要一些改造铐拐。

為了兼容DOM審查與數(shù)據(jù)審查兩種能力徘键,我們想出了一種創(chuàng)新方式,就是將組件結(jié)構(gòu)與DOM結(jié)構(gòu)合二為一遍蟋。例如:

# Main.vue
<template>
  <div class="main">
    <Hello></Hello>
  </div>
</template>
# Hello.vue
<template>
  <div class="hello">
    <span>This is Hello components!</span>
  </div>
</template>

實(shí)際審查時會變?yōu)椋?/p>

<div class="main">
  <Hello>
    <div class="hello">
      <span>This is Hello components!</span>
    </div>
  </Hello>
</div>

當(dāng)點(diǎn)擊組件節(jié)點(diǎn)時展示的是組件本身的信息(完全是vue-devtools的能力)吹害,而當(dāng)點(diǎn)擊DOM節(jié)點(diǎn)時展示的是元素本身的信息(沒有實(shí)現(xiàn))。

這一版相比上一版實(shí)現(xiàn)了DOM樹結(jié)構(gòu)的審查與組件數(shù)據(jù)審查虚青,也實(shí)現(xiàn)了Network的審查它呀。而不足之處在于還不能夠?qū)崿F(xiàn)Elements本身的審查,比如修改樣式,查看內(nèi)外邊距等基礎(chǔ)能力纵穿。

第三版

image

這一版相比于上一版有了比較完善的能力:

  • 完整的DOM審查能力下隧。
  • Console控制臺。
  • Source調(diào)試谓媒。
  • Network審查淆院。
  • 頁面數(shù)據(jù)審查。

與市面上的其它小程序開發(fā)者工具相比篙耗,該有的基礎(chǔ)能力都具備了迫筑。

這一版的調(diào)試面板又采用了第二版所使用的chrome devtools frontend方案。與第二版不同的在于邏輯代碼的運(yùn)行采用的是第三版的方案宗弯。

這一版遇到了三個很大的挑戰(zhàn):

  1. 如何使用一個調(diào)試面板控制渲染層的DOM結(jié)構(gòu)與邏輯層的代碼邏輯脯燃?
  2. 如何在缺少資料的情況下在chrome devtools frontend項(xiàng)目中增加一個新的有完全能力的tab?
  3. 如何獲得審查數(shù)據(jù)蒙保?

這里簡單分別說明一下以上三個問題是如何解決的辕棚。

問題1

chrome devtools frontend(下文簡稱frontend)是谷歌官方研發(fā)的給chrome使用的調(diào)試面板項(xiàng)目。

frontend在啟動后會通過WebSocket連接到一個目標(biāo)調(diào)試地址邓厕,注意逝嚎,這個地址只能是一個地址。那么問題來了详恼,現(xiàn)在邏輯層补君、渲染層分別運(yùn)行于兩個獨(dú)立的環(huán)境中,我應(yīng)該連接誰呢昧互?連接誰都不靠譜挽铁。

唯一的解決方案是,我們提供一個調(diào)試中繼服務(wù)敞掘,讓frontend連接這個中繼服務(wù)叽掘,這個中繼服務(wù)分別去連接邏輯層調(diào)試服務(wù)與渲染層調(diào)試服務(wù)。如下圖所示:


image

問題2

由于frontend項(xiàng)目在今年完全改為了TS的寫法玖雁,導(dǎo)致每次修改更扁、查看需要花費(fèi)10多分鐘的編譯時間。而為了壓縮這可觀的時間赫冬,順藤摸瓜找到了在修改為TS寫法之前的最后一個版本浓镜,這個版本是用JS寫的,可以修改后直接在瀏覽器中預(yù)覽效果劲厌。最大的好處在于可以實(shí)時的調(diào)試代碼了膛薛,這對了解frontend項(xiàng)目的運(yùn)行原理大開方便之門。

有了以上條件還不夠脊僚,因?yàn)閒rontend項(xiàng)目不同于傳統(tǒng)的前端項(xiàng)目相叁,它沒有構(gòu)建的過程遵绰,龐大的項(xiàng)目全是依靠配置文件動態(tài)加載生成的。

經(jīng)過一段時間的摸索和大量的調(diào)試增淹,找到了frontend從啟動到最終渲染一個TAB的完整過程椿访。知道了它是怎么加載的,那增加一個TAB也是板上釘釘?shù)氖虑榱恕?/p>

在frontend中增加一個TAB的關(guān)鍵代碼一覽:

image

但問題到此就解決了虑润?不不不成玫,還早著呢。完成以上步驟僅僅是有了一個TAB拳喻,但它里面是空的哭当,什么都沒有,那怎么往里面添加內(nèi)容呢冗澈?

下面這段代碼是調(diào)試面板Element的初始化代碼(一部分):

image

Emmm钦勘,怎么說呢,和我們一般見到的形式完全不同亚亲,既不是原生DOM操作彻采,也不是JQuery、Vue這類的第三方框架捌归,這怎么下手呢肛响?

原來frontend封裝了大量的組件,上面代碼中的ElementPanel所繼承的UI.Panel.Panel就是一個組件惜索。最開始我嘗試使用這些組件特笋,但由于沒有文檔,加上代碼量龐大巾兆,用起來非常的吃力猎物,效果也不好。最終通過代碼閱讀找到了這些組件暴露在外的element臼寄,那么我將Vue掛載到這個Element上就可以使用vue的方式去實(shí)現(xiàn)這個TAB的內(nèi)容了霸奕。如圖所示:

image

問題3

因?yàn)閒rontend是基于websocket與外界通信的溜宽,element吉拳、console、source這些模塊都是通過內(nèi)置的websocket client與外界交換數(shù)據(jù)适揉。而這個websocket實(shí)例被高度封裝留攒,很難在Vue中直接使用。例如嫉嘀,Element是通過這種方式去獲取DOM數(shù)據(jù)的:


image

注意這里的invoke_getDocument方法是動態(tài)合成的:


image

這里不展開展示細(xì)節(jié)了炼邀。總之如果按照frontend的方式實(shí)現(xiàn)通信的過程改造難度非常大剪侮。這時我想另辟蹊徑拭宁,自建一條通信通道洛退。但后來想想又放棄了,這不是個好的辦法杰标。最終還是決定從內(nèi)置的websocket上入手兵怯,看看哪些關(guān)鍵的地方可以暴露給全局使用。最終經(jīng)過不斷的調(diào)試找到了這個關(guān)鍵的對象:


image

這樣一來腔剂,我便可以隨便使用了:

export function sendMessage(method, params) {
    return new Promise((resolve, reject) => {
        // self.target為通信關(guān)鍵對象
        self.target._router.sendMessage("", "DataInspect", `DataInspect.${method}`, params, (error, result) => {
            if (error) {
                console.error('Request ' + method + ' failed. ' + JSON.stringify(error));
                reject(null);
                return;
            }
            resolve(result);
        })
    })
}

target這個對象可以保證請求與回調(diào)完全一一對應(yīng)媒区,不出錯,不混掸犬,不亂袜漩。這為我后來實(shí)現(xiàn)主動監(jiān)聽邏輯層回調(diào)提供了實(shí)現(xiàn)思路。

Final

*小程序的調(diào)試技術(shù)從0到1一共經(jīng)歷了3個版本的演化才達(dá)到了一個完善的狀態(tài)湾碎,雖然演化的過程中被不斷推翻之前的方案宙攻,但帶來的結(jié)果終究是完美的。這是一個必然的過程介褥,因?yàn)椴徊瓤硬恢揽拥拇嬖凇?/strong>

小程序調(diào)試這塊對我來說最大的挑戰(zhàn)在于:每一步幾乎都在摸索粘优。假設(shè)、實(shí)現(xiàn)呻顽、驗(yàn)證無限循環(huán)雹顺,不斷完善。


最后貼一下第三版基本調(diào)試能力實(shí)現(xiàn)全圖:

數(shù)據(jù)審查面板:


image

Source面板:


image

Console控制臺:


image

DOM審查面板:


image

調(diào)試中斷:


image

Network網(wǎng)絡(luò)資源審查:


image

Network XHR審查:


image

好廊遍,導(dǎo)讀文章就到這里嬉愧。接下來會分幾篇文章詳細(xì)介紹第三版的完整實(shí)現(xiàn)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末喉前,一起剝皮案震驚了整個濱河市没酣,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌卵迂,老刑警劉巖裕便,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異见咒,居然都是意外死亡偿衰,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進(jìn)店門改览,熙熙樓的掌柜王于貴愁眉苦臉地迎上來下翎,“玉大人,你說我怎么就攤上這事宝当∈邮拢” “怎么了?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵庆揩,是天一觀的道長俐东。 經(jīng)常有香客問我跌穗,道長,這世上最難降的妖魔是什么虏辫? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任瞻离,我火速辦了婚禮,結(jié)果婚禮上乒裆,老公的妹妹穿的比我還像新娘套利。我一直安慰自己,他們只是感情好鹤耍,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布肉迫。 她就那樣靜靜地躺著,像睡著了一般稿黄。 火紅的嫁衣襯著肌膚如雪喊衫。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天杆怕,我揣著相機(jī)與錄音族购,去河邊找鬼。 笑死陵珍,一個胖子當(dāng)著我的面吹牛寝杖,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播互纯,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼瑟幕,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了留潦?” 一聲冷哼從身側(cè)響起只盹,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎兔院,沒想到半個月后殖卑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡坊萝,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年孵稽,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片屹堰。...
    茶點(diǎn)故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡肛冶,死狀恐怖街氢,靈堂內(nèi)的尸體忽然破棺而出扯键,到底是詐尸還是另有隱情,我是刑警寧澤珊肃,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布荣刑,位于F島的核電站馅笙,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏厉亏。R本人自食惡果不足惜董习,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望爱只。 院中可真熱鬧皿淋,春花似錦、人聲如沸恬试。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽训柴。三九已至哑舒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間幻馁,已是汗流浹背洗鸵。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留仗嗦,地道東北人膘滨。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像稀拐,于是被迫代替她去往敵國和親吏祸。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內(nèi)容