Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 国产精品久久久久久久久免费观看 ,亚洲欧洲日韩国产aa色大片,亚洲日本免费

          整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          JavaScript兩個快速調試的技巧

          JavaScript兩個快速調試的技巧

          頭條創作挑戰賽#

          本文不同本人掘金平臺的文章:https://juejin.cn/post/6875899284961787911

          直接上干貨!

          console.table展示數據

          在控制臺上展示數組或對象,使用console.table比console.log更加直觀明了。

          // 在控制臺上運行
          console.table([
            { firstName: 'John', lastName: 'Doe', age: 2 },
            { firstName: 'William', lastName: 'Shakespeare', age: 3 }
          ])

          展示為一個table表,友好很多:

          當然,你還可以指定展示哪些列~

          // 在控制臺上運行
          console.table([
            { firstName: 'John', lastName: 'Doe', age: 2 },
            { firstName: 'William', lastName: 'Shakespeare', age: 3 }
          ], ['firstName', 'lastName'])

          上面指定展示firstName和lastName這兩列,當然,(index) 是默認有的。

          注意?:語法 console.table(data [, columns]);

          copy復制數據

          如果你使用谷歌瀏覽器并需要復制控制臺輸出的數據。你可以使用copy()命令行,而不是手動高亮選擇對應代碼進行復制。

          const data=[2, 3, 4];
          copy(data);

          執行上面的代碼,會將data數據值復制到你的粘貼板上。你可以在任意文檔中進行粘貼。

          注意?:copy命令僅在谷歌瀏覽器控制臺上生效,并且在node.js環境中無效。

          音小程序開發者工具(https://developer.open-douyin.com/docs/resource/zh-CN/mini-app/develop/developer-instrument/overview)是面向字節系小程序開發者推出的桌面端集成開發環境,支持小程序開發、調試、預覽、上傳等基本功能,旨在幫助開發者更高效地開發小程序,我也是負責本地開發能力的建設。

          因為工作原因最近對斷點調試進行一些研究,百度了一下,遺憾的是發現網絡上大部分內容都是在教學如何使用調試工具,并沒有擴展到具體的細節,譬如通信邏輯,基本原理等。因此,為了嘗試去弄懂一些斷點調試的底層邏輯,特意去找了一些英文文檔并實踐。

          前言

          作為一個前端開發,前端調試的方式一般有如下幾種:

          1. 代碼中直接打印,比如很多時候直接在代碼中使用 console 來打印一些變量,或者在 vscode 中使用 Turbo Console Log 等插件生成特色的日志內容。
          2. debugger,在代碼中輸入 debugger 關鍵字,然后在瀏覽器中進行斷點調試,或者在瀏覽器中找到源碼,然后進行斷點調試。
          3. vscode 自帶的 web 調試能力。

          相比于 console,debugger 可以看到代碼實際的執行路線以及每個變量的變化,代碼可以跳著看,也可以針對某個函數步步執行。

          但是 console 與 debugger 方式對代碼都有侵入,在開發階段可能要不斷增加和移除來調試,如果不小心忘了,那 mr 又得打回并重新提交了…

          相信很多人在提 mr 都有類似經驗…

          相對來說,瀏覽器中找到 source 源碼打斷點是一個更好的方式,但是還是需要打開 Devtools ,并在 sources 面板找到文件注入斷點,操作上也是有點小麻煩。

          因此第 3 種方式,可能是不錯的方式,在 vscode 中直接在源碼中調試,并能看到具體的變量信息和網頁效果。

          實際上,瀏覽器打斷點與在 vscode 打斷點本質原理都類似。下面就聊一聊瀏覽器斷點調試和 vscode 斷點調試的原理。

          基本知識

          Chrome Devtools Protocol

          在了解具體場景之前,首先有一個比較重要的概念,那就是 CDP。

          基本概念

          CDP(Chrome DevTools Protocol)是一種通過網絡協議與 Google Chrome 或其他兼容的瀏覽器進行通信的協議。通過 CDP,開發者可以遠程控制瀏覽器,獲取瀏覽器狀態信息,以及執行各種瀏覽器操作,從而實現自動化測試、性能分析、調試等應用場景。

          CDP 最早于 2011 年在 Chrome 15 版本中引入,作為 Chrome DevTools 的核心組件之一而出現。在此之前,開發者通常需要通過瀏覽器插件或者第三方工具來進行調試和測試,這些工具通常不夠標準化和通用,也難以實現遠程控制。

          就跟 Emoji 的歷史差不多了,都是亂的,然后規范化,最后大力發展。

          CDP 的出現解決了這些問題,使得開發者可以通過標準化的協議來遠程控制瀏覽器,獲取瀏覽器狀態信息,以及執行各種瀏覽器操作。CDP 的出現和發展推動了 Web 開發和測試的發展,為開發者帶來了更加高效和便捷的開發和測試方式。

          CDP 通過 JSON-RPC 協議來進行通信,提供了一套完整的 API,包括 DOM、CSS、網絡、調試、安全等方面的接口。實際上,可以使用各種編程語言來編寫 CDP 客戶端,從而實現與瀏覽器的交互。

          上圖為 CDP 的官網(https://chromedevtools.github.io/devtools-protocol),可以看到,CDP 包括很多 Domains,常見的 CDP 信息包括:

          • DOM:提供了對文檔對象模型的訪問和操作接口,如節點遍歷、樣式計算、事件處理等。
          • CSS:提供了對樣式表的訪問和操作接口,如樣式計算、應用、修改等。
          • Network:提供了對網絡請求和響應的訪問和操作接口,如請求攔截、修改、模擬等。
          • Console:提供了對瀏覽器控制臺的訪問和操作接口,如日志記錄、錯誤捕獲、命令執行等。
          • Debugger:提供了對瀏覽器調試器的訪問和操作接口,如斷點設置、單步執行、變量查看等。
          • Performance:提供了對瀏覽器性能分析的訪問和操作接口,如性能指標獲取、性能分析報告生成等。

          這幾個也是平常開發中最常用到的幾個 Domains 了。

          常見 CDP

          • Page:Page.navigate:頁面跳轉
          • Network:Network.enable:開啟網絡,可以用來模擬網絡開閉能力
          • DOM:DOM.getDocument:獲取頁面樹,比如調試器 Elements 面板的展示
          • CSS:CSS.getComputedStyleForNode:返回給定 nodeId 的所有樣式,比如點擊 dom 節點,展示 css
          • Runtime:Runtime.evaluate:在當前頁面中執行 JavaScript 代碼
          • Debugger:下面會提到很多,譬如 Debugger.pause/Debugger.setXXX

          應用場景

          chrome 的 Devtools (Front-End Devtools)與 Web Page 之間的調試也是通過 CDP 通信的,如下圖所示:

          除了調試,CDP 額外應用場景也很多,比如剛才提到的自動化測試,通過 CDP 模擬用戶行為,操作頁面元素等,或者 CDP 獲取瀏覽器的性能指標生成性能報告,還可以通過 CDP 模擬瀏覽器行為,獲取頁面數據,實現爬蟲等等。

          瀏覽器斷點調試原理

          帶著問題出發,可能需要搞懂以下 3 點:

          頁面與 Devtools 是如何通信的?

          斷點操作邏輯通信過程是什么?

          如何實現命中斷點并停止代碼執行的?

          操作流程

          增加斷點

          在瀏覽器中,網頁的調試能力是由 Devtools 提供的。Devtools 與網頁之間的通信利用的是 Websocket,而通信協議則是 CDP。

          除了開發中常用到的元素高亮,日志打印和網絡審查,上面也提到了還可以在 sources 面板中使用 debugger。

          如下圖所示,找到一行 js 代碼,在代碼中點擊斷點調試,可以看到 Protocol Monitor 中有一些 CDP 消息,下面就來具體分析一下相關 CDP 信息。

          為什么會發送多次,我也不理解,內容基本上是一致的。

          點擊斷點以后,主要有以下一些 CDP 消息在頁面與 Devtools 之間通信:

          • Debugger.setBreakpointsActive:Activates / deactivates all breakpoints on the page.
          • Debugger.setBreakpointByUrl:Sets JavaScript breakpoint at given location specified either by URL or URL regex.
          • Debugger.getPossibleBreakpoints:Returns possible locations for breakpoint.

          setBreakpointsActive 表示告訴頁面要設置一個調試斷點了;setBreakpointByUrl 則是告訴頁面設置的具體信息;getPossibleBreakpoints 表示設置以后獲取正確的斷點位置,并展示藍色小塊。

          有時候可能會發現設置了某一行為斷點,但是斷點的位置并不是指向的位置,而是另外的位置。比如上面截圖,如果在 15 行設置斷點,則最后展示斷點位置為 18 行。

          整體流程如下圖:

          移除斷點

          除了在 sources 面板增加斷點,還可以取消斷點。取消斷點的 CDP 非常簡單, Devtools 會給 Web Page 發送一個 Debugger.removeBreakpoint 來移除斷點。

          實時斷點調試

          當點擊完斷點以后,頁面會走到斷點所在的代碼位置,同時 Devtools 會接收到一些 CDP 消息,通知它當前斷點的狀態和上下文信息。

          我寫了一個實例,是關于數字的增減邏輯,并在數字增加的時候,走到斷點位置(不需要刷新頁面)。

          可以看到,當點擊 + 號以后,頁面就進入斷點調試邏輯,此時 Devtools 會收到 Debugger.paused消息:

          • Debugger.paused:Fired when the virtual machine stopped on breakpoint or exception or any other stop criteria.

          此時表示頁面已經暫停了代碼執行,Devtools 可以通過 Debugger.paused事件中的參數,獲取當前斷點的上下文信息,如斷點所在的函數、變量值、堆棧信息等。

          具體信息沒有對應看

          點擊“Step Over next function call”(按鈕 1),Devtools 會收到 Debugger.resumed r??zu?m d 消息,通知繼續執行代碼。

          • Debugger.resumed:Fired when the virtual machine resumed execution.

          隨后代碼跳到下一行,此時又會收到 Debugger.paused消息。

          點擊“Resume Script Execution” (按鈕 2)按鈕,Devtools 會收到 Debugger.resumed消息,如果還存在斷點,則此時也會收到 Debugger.paused消息。

          此外這里還有一個 Overlay.setPausedInDebuggerMessage 消息,為 Devtools 發送給頁面,其信息主要是讓頁面展示代碼停止狀態下應該展示的消息,默認為 {"message":"Paused in debugger"},也就是如下圖展示的內容:

          除了上面兩個按鈕,還有幾個調試按鈕,如下圖綠色區域內:

          分別是:Step into next function call、Step out of current function、Step、Deactivate breakpoints。

          Step into next function call:這個按鈕用于進入當前行代碼所在的函數內部,即單步進入函數中執行。

          Step out of current function:這個按鈕用于跳出當前函數,即單步跳出當前函數執行。

          Step:這個按鈕用于單步執行代碼,即逐行執行代碼。

          Deactivate breakpoints:這個按鈕用于禁用所有的斷點,即暫停調試器的所有斷點。

          點擊“Step into next function call”,Devtools 會發送 Debugger.stepInto 消息,并收到 Debugger.resumedDebugger.paused消息,進入到函數內部。

          • Debugger.stepInto:Steps into the function call.

          點擊“Step out of current function”,Devtools 會發送 Debugger.stepOut消息,并收到 Debugger.resumedDebugger.paused消息,跳出該函數。

          點擊 “Step” 按鈕,Devtools 則發送 Debugger.stepInto,代碼執行到下一行,每次點擊,都會發送 Debugger.stepInto消息。

          點擊 “Deactivate (/?di??k.t?.ve?t/) breakpoints”,Devtools 則發送 Debugger.setBreakpointsActive 消息。如果當前斷點狀態為執行狀態,則參數為 active: false,同時設置藍色小塊顏色為透明色。

          重新執行代碼,斷點調試能力失效。

          再點擊一次,則參數為 active: true,斷點調試能力生效。

          基本通信源碼

          了解完相關斷點操作流程以后,再分析一下相關邏輯的源碼。

          首先,Devtools 的源碼就是 Front-End Devtools,UI 上的邏輯這里就不多分析。關于頁面的調試通信邏輯在 DebuggerModel 中:https://source.chromium.org/chromium/chromium/src/+/main:out/Debug/gen/third_party/devtools-frontend/src/front_end/core/sdk/DebuggerModel.js;l=280;drc=f09c12c84b39d13189a7039a05253ca3766d4751;bpv=0;bpt=0

           async stepInto() {
              const skipList=await this.computeAutoStepSkipList("StepInto" /* StepInto  /);  void this.agent.invoke_stepInto({ breakOnAsyncCall: false, skipList });  }  async stepOver() {   this.#autoSteppingContext=this.#debuggerPausedDetailsInternal?.callFrames[0]?.functionLocation() ?? null;  const skipList=await this.computeAutoStepSkipList("StepOver" /  StepOver  /);  void this.agent.invoke_stepOver({ skipList });  }  async stepOut() {   const skipList=await this.computeAutoStepSkipList("StepOut" /  StepOut */);
              if (skipList.length !==0) {
                void this.agent.invoke_stepOver({ skipList });
              } else {
                void this.agent.invoke_stepOut();
              }
            }
            pause() {
              this.#isPausingInternal=true;
              this.skipAllPauses(false);
              void this.agent.invoke_pause();
            }
          

          很清晰的看到,上面提到的各種操作邏輯的函數,譬如 pausestepXXX等 API。

          這里列舉幾個操作按鈕通信較多的 API。

          pause() 的主要邏輯為 2 點:

          1. 設置使頁面斷點暫停狀態為 ture。
          2. 發送 Debugger.paused消息到頁面。

          stepInto() 的主要邏輯為:

          1. 拿到跳轉的 skipList,它是一個字符串數組,用于指定要跳過的函數名稱列。在操作調試按鈕時,一般都是空數組。
          2. 發送 Debugger.stepInto消息到頁面。

          其他 API 邏輯類似。

          再分析一下 chromium /?kro?.mi.?m/ 中的斷點調試代碼邏輯。chromium 中發送 CDP 消息到 Devtools 的邏輯在 devtools_agent_host_impl中,而斷點調試邏輯在devtools_session文件中,通過 agent 的 DispatchProtocolMessage最后調用到 session 的 shoulSendOnIO函數。

          具體來說,這個函數接收一個包含 CDP 方法的 span 參數,然后檢查該方法是否屬于一組特定的方法,如果是,則返回 true,表示該 CDP 消息需要轉發。

          DevToolsSession 是 Chromium 源碼中的一個類,代表一個 DevTools 會話。DevToolsSession 負責管理與 DevTools 和頁面之間的通信,包括上面提到的調試。

          bool ShouldSendOnIO(crdtp::span<uint8_t> method) {
            static auto* kEntries=new std::vector<crdtp::span<uint8_t>>{
                crdtp::SpanFrom("Debugger.getPossibleBreakpoints"),
                crdtp::SpanFrom("Debugger.getScriptSource"),
                crdtp::SpanFrom("Debugger.getStackTrace"),
                crdtp::SpanFrom("Debugger.pause"),
                crdtp::SpanFrom("Debugger.removeBreakpoint"),
                crdtp::SpanFrom("Debugger.resume"),
                crdtp::SpanFrom("Debugger.setBreakpoint"),
                crdtp::SpanFrom("Debugger.setBreakpointByUrl"),
                crdtp::SpanFrom("Debugger.setBreakpointsActive"),
                crdtp::SpanFrom("Emulation.setScriptExecutionDisabled"),
                crdtp::SpanFrom("Page.crash"),
                crdtp::SpanFrom("Performance.getMetrics"),
                crdtp::SpanFrom("Runtime.terminateExecution"),
            };
            ...
          }
          

          可以看到,這里定義了所有發送到 Devtools 的 API。在 chromium 的各種斷點調試方法,最后都會調用 DispatchToAgent方法,并走到 ShouldSendOnIO邏輯。

          命中斷點

          通過上面的分析,了解到了調試器和頁面之間的 CDP 通信內容和 API 的基本實現。那 chromium 又是如何停止代碼到斷點的呢?為何可以停止代碼執行呢?

          在 DevTools 中,停止代碼執行到斷點的核心實現是通過使用 V8 JS 引擎中的斷點機制來實現的。當 chromium 執行到一個斷點時,V8 會暫停 JS 代碼的執行,并將控制權轉交給 Devtools。這時候,Devtools 可以執行上述提到的斷點調試的各種操作。

          這塊邏輯的代碼在 chromium auction_v8_devtools_agentauction_v8_devtools_session 中,看起來比較復雜,涉及到 AuctionV8DevToolsSession 和 AuctionV8DevToolsAgent 兩個類,我的理解是 DevtoolsAgent 提供了一些 Devtools debugger 的服務,并找到對應的 DevtoolsSession 進行通信。V8 將 ws 格式信息轉交給了 DevtoolsSession,最后通過 DevtoolsAgent 發送到了 Devtools。

          大概邏輯如下:

          通過 Devtools Agent,負責接收 Devtools 通信信息,并將斷點信息移交給 V8,然后由 V8 來對代碼進行停止操作。

          V8 里面的邏輯我只能看一個大概,整體邏輯如下:

          V8Debugger 是一個抽象,V8DebuggerAgentImpl 類實現了這個類,它是 Debug 類和 V8 調試協議之間的中介,負責將調試消息轉換為 V8 調試協議中定義的格式。

          關于 V8 斷點 Debugger 更底層的邏輯是與 os、cpu 相關,os 提供了系統調用來實現可執行代碼的中斷。

          中斷則是 cpu 執行下一條指令之前,關注一下中斷標記,從而判斷是否需要中斷執行。整體邏輯上對照著 Vue 的渲染原理即可,每次事件循環結束后最后去走一次渲染 DOM。

          V8 本身也是將 JS 轉為可執行語言,這也就是為何 JS 可以在瀏覽器中擁有斷點能力了。

          這里涉及到一些指令操作,沒有深究。

          同時,V8 中斷代碼執行,也會提供一些環境數據到 Devtools,譬如當前變量數值等,這時候 V8 就會將這些調試信息通過 V8 Debug Protocol 協議的格式丟給 Debug,最后丟給 Devtools,從而鼠標懸浮在 sources panel 即可看到對應的數據內容。

          Debugger.evaluateOnCallFrameRuntime.getProperties 可以拿到一些環境信息,前者比如一些 number 數字就可以得到。

          Vscode Web 代碼斷點調試原理

          在 Vscode 中調試代碼,能讓開發者專注于代碼本身,一邊開發運行一邊斷點調試查看變量信息,并減少一些臟代碼的開發。如下圖所示,可以看到,似乎是將瀏覽器的 Debugger 的邏輯照搬到了 Vscode 中。

          在介紹完瀏覽器斷點調試的邏輯以后,我們大概了解了頁面與 Devtools 的通信過程和相關 CDP 信息。有了這些基礎,我們再分析分析 Vscode 中是如何實現斷點調試 Web 代碼的。

          launch.json

          在 Vscode 中配置調試后,會生成一個 .vscode/launch.json 文件,其主要是配置需要調試的 url 和遠程調試的端口號 port。

          {
            "version": "0.2.0",
            "configurations": [
              {
                "type": "chrome",
                "request": "launch",
                "name": "針對 localhost 啟動 Chrome",
                "url": "http://localhost:8080",
                "webRoot": "${workspaceFolder}"
              }
            ]
          }
          

          Debugging Architecture of VS Code

          [?ɑrk??tekt??r]

          Vscode 并不只是前端開發者調試 JS 使用,還可以調試其他語言,Python 一些教程就建議使用 Vscode 調試。因此 Vscode 的調試架構高度靈活,可以支持多種編程語言和調試場景,并且可以基于該架構實現各種調試擴展。

          如上圖,Vscode 的調試架構中,有 3 個 Core Module:

          • Debug Adapter:調試適配器是 Vscode 和具體調試目標之間的橋梁。適配器主要就是負責將調試請求轉換為調試目標,并將調試目標轉為調試器需要的結果,其通過 Vscode Debug Protocol 協議通信。Debug Adapter 提供了一組標準的調試接口,包括設置斷點、單步執行、查看變量值等。
          • Debug Extension:調試擴展是 Vscode 內置的插件,提供特定語言或者場景的實現。比如可以調試 JS TS Python 等,同時社區也可以提供相關擴展,譬如 Java:
          • Debug UI:即 Vscode 的操作界面,它提供了調試器的各種操作和功能,例如設置斷點、單步調試、查看變量值等。

          :別忘了另外一個 Debugger,即為 launch.json 中的 type,指底層的調試目標,例如 Node.js 運行時、Chrome 瀏覽器等等。比如斷點后的信息需要傳遞給 chrome,需要去暫定代碼執行,并斷點逐步執行等。

          原理

          在了解原理之前,先看一些現象:

          1. 當 Vscode 啟動調試并走到指定斷點時,Chrome 自身調試器也會走到對應的調試邏輯(Devtools 本身也是一個 ws client,任何 client 都會收到 chrome 的 cdp 消息)。
          2. 當在 Vscode 調試面板操作 StepInto 按鈕時,Vscode 代碼會走到下一步,同時 chrome 調試也會走到下一步。
          3. 當在 Chrome Devtools 中操作 StepInto 按鈕時,Chrome Devtools 代碼也會走到下一步,同時 Vscode 中代碼也會跳轉到下一步。

          通過上面 3 種現象可以看出,Vscode Webpage Devtools 關系如下:

          細品一下,這時候就可以知道為何需要 Debug Adapter 了。實際上,就是將 CDP 消息轉為 DAP。

          Workflow

          Vscode Chrome Debug 的工作流程如下:

          1. Vscode 啟動 JavaScript 調試器,并配置調試器相關的參數,例如調試類型、調試目標等等。
          2. JavaScript Debug Extension 會根據配置啟動一個 Debug Adapter 進程,并向該進程發送調試請求,請求 Debug Adapter 與 Debugger 之間建立連接。
          3. Debug Adapter 進程會根據用戶的配置,啟動相應的 Chrome,并與對應網頁(Debugger)建立連接。
          4. Debug Adapter 進程會將調試結果轉換為 Vscode 支持的調試消息格式,即上面提到的 DAP,并將調試消息發送給 Vscode。

          這里的核心就是 Extension,其作用就是調度與控制,比如啟動 Adapter 進程,發送與接收調試信息等等,屬于大 BOSS,而 Adapter 只是下屬。

          JS Debug Extension

          上面提到,chromium 內部是使用 CDP 協議通信,因此 Extension 想要正確調試 Chrome WebPage,首先就得遵守 Chrome 的玩法。比如,在 Vscode 中點擊 StepInto 按鈕,這時候會將對應操作信息轉化為 CDP 信息,然后再發送給 WebPage。

          Extension 啟動 Chrome 的邏輯在 companionBrowserLaunch 中:https://github.com/microsoft/vscode-js-debug/blob/main/src/ui/companionBrowserLaunch.ts#L50

          await vscode.commands.executeCommand('js-debug-companion.launchAndAttach', {
            proxyUri: tunnel ? 127.0.0.1:${tunnel.localAddress.port} : 127.0.0.1:${args.serverPort},
            wslInfo: process.env.WSL_DISTRO_NAME && {
              execPath: process.execPath,
              distro: process.env.WSL_DISTRO_NAME,
              user: process.env.USER,
            },
            ...args,
          });
          

          另外,Devtools 與 WebPage 是通過 ws 通信的,這里 JavaScript Extension 內部實現與開發者工具調試器和模擬器的通信相似, Extension 與 WebPage 通信也是拿到了頁面的 debug ws url,在 Extension 內部創建一個 ws client,通過該 client 監聽來自于 WebPage CDP 信息,并轉發到會話的 Adapter,最后再交給 Vscode。

          看最新的代碼,JS Debug Extension 也會負責部分調試 UI 相關邏輯。

          Command 實例

          StepInto舉例,在 Vscode 中點擊該按鈕以后,會發送一個 DAP 消息:

          {
              "command": "stepInTo",
              "seq": number,
              "type": "request",
              "arguments": {
                  "threadId": number
              }
          }
          

          然后,Exetension 將該消息轉為 CDP 消息,并發送給 WebPage:

          {
              "id": 1,
              "method": "Debugger.stepInto",
              "params": {
                  "callFrameId": number/string
              }
          }
          

          WebPage 收到該消息后,返回執行結果到 Extension:

          {
              "id": 1,
              "result": {}
          }
          

          Extension 再將該 response 通過 Debug Adapter 轉給 Vscode,Vscode 調整 UI:

          {
            "body": {
                "reason": "OK",
                "threadId": number
            },
            "type": "response"
          }
          

          相關 DAP 格式可以在 debug-adapter-protocol 查閱:https://microsoft.github.io/debug-adapter-protocol/overview

          如果要在 Vscode 中查看實時的 DAP 和 CDP 消息,可以通過如下操作:

          源碼調試

          上面給到的例子非常簡單,js 代碼也沒有經過構建生成編譯后的代碼。但是實際場景中開發的項目會引入各種開源庫,然后經過諸如 Webpack 等打包構建工具做編譯打包,才能在瀏覽器中運行。編譯壓縮后的代碼一般不具備可讀性,因此在編譯后代碼進行調試成本比較高。

          We all know,SourceMap 存儲著源碼和生產代碼之間的映射關系。譬如我這里啟動了一個 Vite 項目:

          當我在源碼的 main.ts 中設置斷點時,可以看到 Request 中的 url 為 host:port/src/main.ts,即實際傳給 WebPage 的斷點文件為編譯后的文件。

          JS Debug Extension 亦是如此。

          當在 Vscode 的源碼中增加了一個斷點,JS Debug Extension 會根據 sourceMap 將源代碼路徑映射到編譯后的代碼路徑中,并將這個信息發送給瀏覽器。

          所以呀,解析是前端行為。

          擴展:SourceMap 加載

          SourceMap 雖然也是靜態資源,但是其加載在 Network 面板并不能看到,而是在 Developer Resources 中。

          為了啟動快,我用的 Vite 來生成項目。Vite 利用了瀏覽器原生的 ES modules 功能,根據文件依賴關系,生成依賴樹,然后各模塊文件模塊單獨加載。Vite 文件都有單獨的 SourceMap,不需要配 SourceMap 依賴。

          可以看到,這里 Vite 默認是直接內嵌的 SourceMap,無需單獨請求, 可以在代碼文件加載完成后,就直接解析了,紅框里面展示的鏈接就是 Base64 的形式了。

          ??SourceMap 的解析是交給 Devtools 本身的,Debugger 只負責運行和暫停。因此,如果斷點在 SourceMap 解析完成之前觸發,則沒法告訴 Debugger 正確的地址,可能會出現斷點無效情況。

          IDE 小程序斷點調試

          Devtools Debug

          根據上面的介紹,小程序斷點調試的最簡單辦法就是在代碼中寫上 debugger,然后交給 v8 處理即可。另外還有一種方式就是打開小程序調試器,在 sources panel 中打斷點,如下圖:

          打斷點,刷新小程序,即可跳轉到斷點位置。此時可以看到對應的 CDP 消息中的 Request。

          可以看到,這里點擊的是 56 行,但實際上 Request 中卻不是,Devtools 通過 sourceMap 進行了處理,定位到了 64 行。根據上面提到的源碼調試邏輯,這里的位置為編譯后的代碼位置,找到編譯產物代碼 app.js 即可看到 real position。

          IDE Editor Debug???

          考慮到上面提到的 Vscode 有 web 斷點調試能力,那 IDE Editor 或許也是可以支持斷點調試能力的。

          Vscode 可以直接在編輯器運行項目,然后啟動自定義的調試目標(Debugger)。

          IDE 為小程序運行時的載體,與 Vscode 啟動 web 項目不一樣,其邏輯為編譯完成后生成一個編譯產物目錄,通過靜態服務,Simulator 直接加載對應編譯產物。因此,IDE 的 Editor 實際上跟 Simulator 沒什么聯系的。

          假設借用 Devtools Debug 的邏輯,當在 Editor 打斷點時,捕獲所有的斷點 DAP 消息,當開啟調試時,刷新模擬器,將所有的斷點信息轉為 CDP 信息發送給模擬器,或許就可以簡單實現該能力。

          當然,考慮到是在源碼中打斷點,這里的難點應該是在于要實現 sourceMap 解析,而 Debug UI 則可以利用 Vscode JS Extension,或者通過自定義實現一個 Debug UI。

          總結

          本文從抖音開發者工具支持斷點調試能力需求引入,概述了瀏覽器斷點調試的基本原理,也介紹了 Vscode Web 代碼斷點調試能力,詳細介紹了各模塊中各 CDP 消息通信邏輯。閱讀本文可以掌握前端各種調試方法的基本原理。

          加入我們

          抖音開放平臺提供小程序、移動應用、網站應用、直播小玩法等多業務載體,為開發者提供豐富的能力和解決方案。抖音開放平臺基于平臺規則和開發者訴求,提供了兩種開放模式:能力開放和行業開放。

          • 在能力開放方面,平臺提供了運營抖音號、頭條號的相關能力,如發布視頻和獲取視頻數據、用戶數據和各類榜單信息等。同時也圍繞抖音小程序提供了擔保支付、客服管理、訂閱消息等基礎能力,以及短視頻 / 直播間掛載小程序、流量主、廣告主等經營能力。
          • 在行業開放方面,平臺基于挖掘行業痛點、解決實際問題角度出發,提供了到店餐飲、機酒旅、泛知識等多行業解決方案,提升品牌及商家的經營收入和行業競爭力。

          參考文檔

          [1]

          V8 本地調試: https://zhuanlan.zhihu.com/p/568432229

          [2]

          Debugging over the V8 Inspector Protocol: https://v8.dev/docs/inspector

          [3]

          Adapter Debug Protocol: https://microsoft.github.io/debug-adapter-protocol/

          [4]

          SourceMap: https://zhuanlan.zhihu.com/p/615279891

          作者:Rabbitzzc

          來源:微信公眾號:字節前端 ByteFE

          出處:https://mp.weixin.qq.com/s/DGSSDEmAdj8sE_KfN3wQsg

          術領域總是充滿著神秘的未知和挑戰,有趣又令人不能自拔。就像 JavaScript,即使是每天使用它進行開發交互的開發人員,而語言的某些部分仍然未被開發。

          了解工具可以使工具最大限度的幫助你完成任務。盡管JavaScript的調試非常麻煩,但在掌握了技巧 (tricks) 的情況下,依然可以用盡量少的的時間解決這些錯誤 (errors) 和問題 (bugs) 。

          下面小編列出了14個關于JavaScript的調試技巧。雖然調試技巧也可以用在別的檢查工具上,但大部分的技巧還是用在 Chrome Inspector 和 Firefox 上的。

          1. ‘debugger;’

          ‘debugger’ 是 console.log 之外最好的調試工具,簡單暴力。只要把它寫到代碼里,Chrome 運行的時候就會自動自動停在那。甚至可以用條件語句把它包裹起來,這樣就可以在需要的時候才執行它。也是一款非常好用的調試工具了。

          if (thisThing) {

          debugger;

          }

          2. 把 objects 輸出成表格

          很多的時候,你可能會有一堆對象需要查看。可以用console.log把每一個對象都輸出出來,也可以用console.table語句直接把所有的對象都直接輸出成為一個表格。demo如下:

          var animals=[

          { animal: 'Horse', name: 'Henry', age: 43 },

          { animal: 'Dog', name: 'Fred', age: 13 },

          { animal: 'Cat', name: 'Frodo', age: 18 }

          ];

          console.table(animals);

          輸出結果:

          3. 試遍所有的尺寸

          雖然把各種各樣的手機都擺在桌子上看起來很酷,但卻很不現實。但是,瀏覽器內卻提供了你所需要的一切。進入檢查面板點擊“切換設備模式”按鈕。這樣,就可以在窗口內調整視窗的大小。

          4. 如何快速定位 DOM 元素

          在元素面板上標記一個 DOM 元素并在 concole 中使用它。Chrome Inspector 的歷史記錄保存最近的五個元素,最后被標記的元素記為 >在元素面板上標記一個 DOM 元素并在 concole 中使用它。Chrome Inspector 的歷史記錄保存最近的五個元素,最后被標記的元素記為 $0,倒數第二個被標記的記為 $1,以此類推。<,倒數第二個被標記的記為 ,以此類推。

          如果你像下面那樣把元素按順序標記為 ‘item-4′, ‘item-3’, ‘item-2’, ‘item-1’, ‘item-0’ ,你就可以在 concole 中獲取到 DOM 節點:

          5. 用 console.time() 和 console.timeEnd() 測試循環耗時

          當你想知道某些代碼的執行時間的時候這個工具將會非常有用,特別是當你定位很耗時的循環的時候。你甚至可以通過標簽來設置多個 timer 。demo 如下:

          console.time('Timer1');

          var items=[];

          for(var i=0; i < 100000; i++){

          items.push({index: i});

          }

          console.timeEnd('Timer1');

          運行結果:

          6.獲取函數的堆棧軌跡信息

          在使用JavaScript框架的時候會產生很多的代碼。

          它創建視圖觸發事件而且最終會想知道函數調用是怎么發生的。

          因為 JavaScript 不是一個很結構化的語言,有時候很難完整的了解到底發生了什么以及什么時候發生的。 這個時候就輪到 console.trace(在終端的話就只有 trace )出場來調試 JavaScript了 。

          假設現在想看 car 實例在第33行調用 funcZ 函數的完整堆棧軌跡信息:

          var car;

          var func1=function() {

          func2();

          }

          var func2=function() {

          func4();

          }

          var func3=function() {

          }

          var func4=function() {

          car=new Car();

          car.funcX();

          }

          var Car=function() {

          this.brand=‘volvo’;

          this.color=‘red’;

          this.funcX=function() {

          this.funcY();

          }

          this.funcY=function() {

          this.funcZ();

          }

          this.funcZ=function() {

          console.trace(‘trace car’)

          }

          }

          func1();

          var car;

          var func1=function() {

          func2();

          }

          var func2=function() {

          func4();

          }

          var func3=function() {

          }

          var func4=function() {

          car=new Car();

          car.funcX();

          }

          var Car=function() {

          this.brand=‘volvo’;

          this.color=‘red’;

          this.funcX=function() {

          this.funcY();

          }

          this.funcY=function() {

          this.funcZ();

          }

          this.funcZ=function() {

          console.trace(‘trace car’)

          }

          }

          func1();

          第33行會輸出:

          在這里我們可以看見到func1調用了func2, func2又調用了func4。Func4 創建了Car的實例,然后調用了方法car.funcX,等等。

          盡管我們感覺我們已經對自己的腳本代碼非常了解,但是這種分析依然有用。比如優化代碼。獲取到堆棧軌跡信息和一個所有相關函數的列表。每一行都是可點擊的,并且可以在他們中間前后穿梭。這種感覺非常的棒。

          7. 格式化代碼使調試 JavaScript 變得容易

          有時候你發現產品有一個問題,而 source map 并沒有部署到服務器。不要害怕。Chrome 可以格式化 JavaScript 文件,使之易讀。格式化出來的代碼在可讀性上可能不如源代碼 —— 但至少你可以觀察到發生的錯誤。點擊源代碼查看器下面的美化代碼按鈕 {} 即可。

          8. 快速找到調試函數

          我們來看看怎么在函數中設置斷點。

          通常情況下有兩種方法:

          1. 在查看器中找到某行代碼并在此添加斷點
          2. 在腳本中添加 debugger

          這兩種方法都必須在文件中找到需要調試的那一行。

          使用控制臺是不太常見的方法。在控制臺中使用 debug(funcName),代碼會在停止在進入這里指定的函數時。

          這個操作很快,但它不能用于局部函數或匿名函數。不過如果不是這兩種情況下,這可能是調試函數最快的方法。(注意:這里并不是在調用 console.debug 函數)。

          var func1=function() {

          func2();

          };

          var Car=function() {

          this.funcX=function() {

          this.funcY();

          }

          this.funcY=function() {

          this.funcZ();

          }

          }

          var car=new Car();

          在控制臺中輸入 debug(car.funcY),腳本會在調試模式下,進入 car.funcY 的時候停止運行:

          9. 屏蔽不相關代碼

          如今,經常在應用中引入多個庫或框架。其中大多數都經過良好的測試且相對沒有缺陷。但是,調試器仍然會進入與此調試任務無關的文件。解決方案是將不需要調試的腳本屏蔽掉。當然這也可以包括你自己的腳本。 點此閱讀更多關于調試不相關代碼(http://raygun.com/blog/javascript-debugging-with-black-box/)。

          10. 在復雜的調試過程中尋找重點

          在更復雜的調試中,我們有時需要輸出很多行。你可以做的事情就是保持良好的輸出結構,使用更多控制臺函數,例如 console.log,console.debug,console.warn,console.info,console.error 等等。然后,你可以在控制臺中快速瀏覽。但有時候,某些JavaScrip調試信息并不是你需要的。

          現在,可以自己美化調試信息了。在調試JavaScript時,可以使用CSS并自定義控制臺信息:

          console.todo=function(msg) {

          console.log(‘ % c % s % s % s‘, ‘color: yellow; background - color: black;’, ‘–‘, msg, ‘–‘);

          }

          console.important=function(msg) {

          console.log(‘ % c % s % s % s’, ‘color: brown; font - weight: bold; text - decoration: underline;’, ‘–‘, msg, ‘–‘);

          }

          console.todo(“This is something that’ s need to be fixed”);

          console.important(‘This is an important message’);

          輸出:

          例如:

          在console.log()中, 可以用%s設置字符串,%i設置數字,%c設置自定義樣式等等,還有很多更好的console.log()使用方法。 如果使用的是單頁應用框架,可以為視圖(view)消息創建一個樣式,為模型(models),集合(collections),控制器(controllers)等創建另一個樣式。也許還可以像 wlog,clog 和 mlog 一樣發揮小伙伴們的想象力!

          11. 查看具體的函數調用和它的參數

          在 Chrome 瀏覽器的控制臺(Console)中,大家的注意力都集中在具體的函數上。每次這個函數被調用,它的值就會被記錄下來。

          var func1=function(x, y, z) {

          //....

          };

          然后輸出:

          這是查看將哪些參數傳遞到函數的一種很好的方法。但在這里小編要說明,如果控制臺能夠告訴我們需要多少參數,那就好了。在上面的例子中,函數1期望3個參數,但是只有2個參數被傳入。如果代碼沒有在代碼中處理,它可能會導致一個 bug 。

          12. 在控制臺中快速訪問元素

          在控制臺中執行 querySelector 一種更快的方法是使用美元符。$(‘css-selector’) 將會返回第一個匹配的 CSS 選擇器。$$(‘css-selector’) 將會返回所有。如果你使用一個元素超過一次,它就值得被作為一個變量。

          13. Postman 很棒(但 Firefox 更快)

          很多開發人員都使用 Postman 來處理 Ajax 請求。Postman 真不錯,但每次都需要打開新的瀏覽器窗口,新寫一個請求對象來測試。這確實有點兒煩人。

          有時候直接使用在用的瀏覽器會更容易。

          這樣的話,如果你想請求一個通過密碼保證安全的頁面時,就不再需要擔心驗證 Cookie 的問題。這就是 Firefox 中編輯并重新發送請求的方式。

          打開探查器并進入網絡頁面,右鍵單擊要處理的請求,選擇編輯并重新發送。現在你想怎么改就怎么改。可以修改頭信息,也可以編輯參數,然后點擊重新發送即可。

          現在我發送了兩次同一個請求,但使用了不同的參數:

          14. 節點變化時中斷

          DOM 是個有趣的東西。有時候它發生了變化,但你卻并不知道為什么會這樣。不過,如果你需要調試 JavaScript,Chrome 可以在 DOM 元素發生變化的時候暫停處理。你甚至可以監控它的屬性。在 Chrome 探查器上,右鍵點擊某個元素,并選擇中斷(Break on)選項來使用:

          為了幫助小伙伴們更好的學習Python,技術學派整理了Python的相關學習視頻及學習路線圖。

          領取方式

          關注“技術學派”后,評論轉發文章,私信回復:Python學習


          主站蜘蛛池模板: 中文字幕一区二区日产乱码| 蜜桃视频一区二区三区| 色婷婷一区二区三区四区成人网 | 午夜肉伦伦影院久久精品免费看国产一区二区三区 | 精品少妇ay一区二区三区 | 国产成人一区二区三区在线| 国产MD视频一区二区三区| 一区二区三区视频观看| 综合久久一区二区三区| 91久久精品午夜一区二区| 久久99精品免费一区二区| 无码精品久久一区二区三区 | 国产一区二区电影| 国产日韩一区二区三免费高清| 日本精品一区二区三区在线视频一 | 亚洲va乱码一区二区三区| 国产午夜精品一区二区三区不卡 | 精品少妇ay一区二区三区| 欧洲精品无码一区二区三区在线播放| 国产99精品一区二区三区免费| 亚洲国产一区二区三区 | 亚洲AV无一区二区三区久久| 国内国外日产一区二区| 午夜影视日本亚洲欧洲精品一区 | 天天视频一区二区三区| 美女免费视频一区二区三区| 亚洲国产精品第一区二区三区| 久久精品国产一区二区电影| 日韩欧国产精品一区综合无码| 一区二区免费视频| 国产香蕉一区二区精品视频| 亚洲AV无码一区东京热久久| 国产日本一区二区三区| 精品视频一区二区观看| 精品视频一区二区三区免费| 亚洲色无码专区一区| 人妻无码一区二区三区| 久久久久久免费一区二区三区 | 亚洲A∨精品一区二区三区| 久久久精品人妻一区亚美研究所 | 一区二区三区四区在线播放|