又到了 kaopubear 的專(zhuān)欄時(shí)間,本文較長(zhǎng)圖多,真誠(chéng)建議滑到文末 閱讀原文 體驗(yàn)更加。閱讀更多內(nèi)容,也歡迎 閱讀原文 移步我的博客。 第一次接觸網(wǎng)頁(yè)開(kāi)發(fā)是兩三年前的事,那時(shí)我曾經(jīng)問(wèn)過(guò)計(jì)劃帶我入門(mén)前端的前輩:入門(mén)前端的標(biāo)準(zhǔn)是什么。他當(dāng)時(shí)用一種極平和的語(yǔ)氣和我說(shuō):學(xué)會(huì)dubug。幾年后的今天我即便也寫(xiě)過(guò)一點(diǎn)網(wǎng)頁(yè)工具,但還是依然沒(méi)能入門(mén)。反思一下:一是 JavaScript 學(xué)的不好,二就是不敢說(shuō)自己有多少 debug 的能力。 遂放棄。 最近因?yàn)樾枰忠婕耙稽c(diǎn)網(wǎng)頁(yè)工具開(kāi)發(fā),同時(shí)因?yàn)樾枨笳w和 R 交互比較多于是決定用 R 的 Shiny 來(lái)搞一搞。 寫(xiě)了一個(gè)多星期我感覺(jué) Shiny 確實(shí)解決了不熟悉前后端交互的人寫(xiě)網(wǎng)頁(yè)的大多數(shù)問(wèn)題,但如何 debug 的門(mén)檻還是擺在那里。比如前幾天一個(gè)高手和我吐槽寫(xiě) Shiny 時(shí)不知道改了什么突然不能正確運(yùn)行了,更糟心的是還沒(méi)有任何報(bào)錯(cuò)信息。當(dāng)然,后來(lái)經(jīng)過(guò)討論發(fā)現(xiàn)其實(shí)并非沒(méi)有報(bào)錯(cuò)信息,只是那時(shí)他沒(méi)有找到而已。 這篇文章就結(jié)合最近學(xué)習(xí)的一點(diǎn)資料,大致聊聊在 Shiny 中 debug 的一些方法。 Shiny debug 主要有三個(gè)步驟,分別是調(diào)試(Debugging),追蹤(Tracing)和錯(cuò)誤處理(Error handling)。
Debugging 調(diào)試breakpoints 斷點(diǎn)說(shuō)到「斷點(diǎn)」,我不由的想對(duì) bug 說(shuō)一句:
如果你知道哪一行的代碼有錯(cuò)(這話本身就像bug)或者猜測(cè)很可能是哪里不對(duì),就可以直接在所在行設(shè)置一個(gè)斷點(diǎn)。Rstudio 只需在行號(hào)左邊點(diǎn)一下鼠標(biāo)就會(huì)出現(xiàn)紅點(diǎn)提示標(biāo)記成功。開(kāi)始運(yùn)行 Shiny 程序后會(huì)在斷點(diǎn)處停止執(zhí)行,然后就可以開(kāi)始逐步執(zhí)行進(jìn)行代碼調(diào)試了。 如上圖所示,我們?cè)?40 和 41 行設(shè)置了兩個(gè)斷點(diǎn),現(xiàn)在點(diǎn)擊 這個(gè)時(shí)候我們可以方便的查看環(huán)境中已有的變量,例如這里已經(jīng)運(yùn)行完畢的 現(xiàn)在環(huán)境中存在 x 和 bin 兩個(gè)變量,同時(shí)在 console 的 說(shuō)到缺點(diǎn),目前 breakpoints 只可以在 Rstudio IDE 中使用,而且只能用于 小結(jié)如下: browser() 命令其實(shí)從上面 console 的截圖也可以看到,斷點(diǎn)就是執(zhí)行了一次類(lèi)
甚至你還可以把 小結(jié)如下 Tracing 追蹤在許多情況下通過(guò)暫停執(zhí)行來(lái)找問(wèn)題比較困難,相反需要我們?cè)诔绦蜻\(yùn)行時(shí)觀察系統(tǒng)。對(duì)于 Shiny 的程序尤其如此,因?yàn)樗幌?nbsp;R 腳本那樣線性運(yùn)行。 Showcase ModeShiny 在啟動(dòng)時(shí), 如果想要默認(rèn)開(kāi)啟這一功能,可以在該 Shiny 目錄下創(chuàng)建一個(gè) DisplayMode: Showcase 小結(jié)如下 ![]() Reactive Log在 Shiny 中經(jīng)常會(huì)用到響應(yīng)對(duì)象,當(dāng)開(kāi)啟 Reactive Log 之后,程序運(yùn)行時(shí)除了可以告訴你正在執(zhí)行哪些響應(yīng)之外,日志還可以幫助你可視化展示響應(yīng)對(duì)象之間的依賴(lài)關(guān)系。在開(kāi)啟一個(gè)新的 R session 時(shí)首先配置 ![]() 打印 tracing在各種編程語(yǔ)言中,一個(gè)萬(wàn)變不離其宗的調(diào)試技巧就是不停的輸出。在 PHP 里面是不停的
進(jìn)行上述修改后,運(yùn)行 Shiny 每次調(diào)整 input 都會(huì)在 console 中打印輸出。如下圖所示 ![]() 小結(jié)如下 ![]() Shiny Server 進(jìn)行 tracing如果你的程序運(yùn)行在 server 端而非本地,每次 Shiny 程序運(yùn)行都會(huì)生成 log 文件,默認(rèn)的路徑是 客戶(hù)端和服務(wù)器端 Tracing一個(gè) Shiny 程序包括 client (瀏覽器) 和 server (R 進(jìn)程) 兩部分。這兩者通過(guò) websocket連接,websocket 接收來(lái)自客戶(hù)端的狀態(tài),例如輸入控件新的賦值,同時(shí)發(fā)布來(lái)自服務(wù)器端的狀態(tài)更改,例如新的輸出。在一些比較復(fù)雜的情況下,你可以通過(guò)打開(kāi) trace 來(lái)跟蹤 JSON 格式的 websocket 內(nèi)容。 ![]() 如上圖所示,在輸出內(nèi)容中,
Errors 錯(cuò)誤跑程序最怕看到的就是報(bào)錯(cuò),但是真要有問(wèn)題了最希望看到的就是明確的報(bào)錯(cuò)。 R 報(bào)錯(cuò)在 Shiny 中大多數(shù)報(bào)錯(cuò)信息都是由R引起的,在 0.13.0 之后的 Shiny 版本中已經(jīng)有了比較直觀的報(bào)錯(cuò)形式,會(huì)直接給出哪里的程序出現(xiàn)了錯(cuò)誤。這里首先人為引入一個(gè)報(bào)錯(cuò),當(dāng) input 大于 40 的時(shí)候停止程序并且拋出 ![]() 運(yùn)行程序后調(diào)整輸入如果錯(cuò)誤,可以觀察 console 的輸出內(nèi)容: ![]() 首先直接觀察顏色不同的部分,直接告訴我們 JavaScript errors目前 Shiny 有很多第三方 JavaScript 組件,有時(shí)如果使用上面幾種方式都沒(méi)有定位到錯(cuò)誤相關(guān)問(wèn)題或者沒(méi)有看到報(bào)錯(cuò)信息,很可能是 JavaScript 中發(fā)生錯(cuò)誤導(dǎo)致程序出現(xiàn)了bug。畢竟 Shiny 是個(gè)網(wǎng)頁(yè)應(yīng)用,各種和用戶(hù)的交互少不了 JavaScript 的使用。 要進(jìn)行 JavaScript 的調(diào)試在 Rstudio 就不靈了。如果你是通過(guò) Rstudio 打開(kāi)了一個(gè)單獨(dú) Shiny 頁(yè)面,可以通過(guò)右鍵單擊 Shiny頁(yè)面,選擇 ![]() 在 Shiny 中 UI 的每個(gè)部分都會(huì)有一個(gè) id 參數(shù),這個(gè) id 對(duì)應(yīng)的參數(shù)在瀏覽器中解析之后就是對(duì)應(yīng)著 HTML 標(biāo)簽中的 id。在 HTML 中,這個(gè) id 是必須唯一(區(qū)別于name)。因此,在Shiny的ui中每一個(gè)id參數(shù)也必須唯一。解析效果如下圖所示: ![]() 如果你不小心在 UI 中寫(xiě)入了兩個(gè)一樣的 id,在上圖中就有兩個(gè)標(biāo)簽的 id 都是 a,程序運(yùn)行后在 Rstudio 并不會(huì)拋出什么錯(cuò)誤,但是在 Shiny 頁(yè)面端的各種操作就進(jìn)行不了。如果不在開(kāi)發(fā)者模式下進(jìn)行調(diào)試只能通過(guò)各種方法在 Rstudio 進(jìn)行測(cè)試,但是如果打開(kāi) JavaScript 的 console,就會(huì)看到其實(shí)已經(jīng)給出了明確的報(bào)錯(cuò)信息。 ![]() 當(dāng)然,Chrome 開(kāi)發(fā)者工具的用法是在太多,這也是我在文章開(kāi)頭提到的自己入門(mén)不了前端的原因之一。如果在你的 Shiny 中用到了大量 JavaScript 相關(guān)內(nèi)容,或者需要定制很多 CSS 相關(guān)的內(nèi)容,可以學(xué)習(xí)一下官方的開(kāi)發(fā)者工具文檔。 至此,也就簡(jiǎn)單的寫(xiě)完了 R Shiny debug 的三個(gè)主要步驟,其中提到的每一個(gè)用法在實(shí)際使用中都需要進(jìn)一步深入學(xué)習(xí)。當(dāng)然,每一個(gè)方法用到的頻率也各有不同,可以根據(jù)個(gè)人的實(shí)際情況進(jìn)行后續(xù)的練習(xí)。 One more thing:shinyjs寫(xiě)到這里本來(lái)文章就可以結(jié)束了,但是似乎總有哪里不對(duì)。 為什么在 Rstudio 的 console 里就不能查看 JavaScript 的 log 信息。要知道 Rstudio GUI 本身使用的就是 QT框架,其中的很多部分都可以理解為一個(gè)網(wǎng)頁(yè)。從維基百科或者它自己的說(shuō)明中都可以看出這一點(diǎn)。 ![]() ![]() 不信的話你也可以在 Rstuido 的每個(gè) pane 里右擊然后選擇 ![]() 你會(huì)看到下面圖所示的內(nèi)容 ![]() 既然如此,沒(méi)有理由不去解決這個(gè)不方便的問(wèn)題。其實(shí)在 R 中有一個(gè)專(zhuān)門(mén)為 Shiny 提高 JavaScript 使用體驗(yàn)開(kāi)發(fā)的R包,叫做shinyjs。這個(gè)包的存在讓 Shiny 使用 JavaScript 變得強(qiáng)大和高效了很多。其中針對(duì)調(diào)試有兩個(gè)專(zhuān)門(mén)的函數(shù)。 showLog
這個(gè)函數(shù)類(lèi)似于 JavaScript 中的 logjs
這個(gè)函數(shù)則可以把信息輸出到 JavaScript console 中方便進(jìn)行調(diào)試。 例如下面一段代碼: library(ShinyJavaScript) 運(yùn)行后通過(guò)點(diǎn)擊 button ,就可以把 ![]() 嗯,先寫(xiě)到這里吧。 我的學(xué)習(xí)材料
|
|