如何使用 VS Code 除錯 Vue.js 專案?
簡介
此教程會以一個全新的 Vue.js 專案作為模板進行配置,你可以跟隨教程一步步操作,也可以按照教程將配置新增到已有的專案中。
倉庫地址: http://github.com/mrlmx/debug-vuejs-project-with-vscode
建立專案
通過 vue 提供的create-vue 腳手架,建立一個 vue3 專案。
npm init [email protected]
注意:通過上述命令,建立的是基於 vite 的專案,而不是基於 webpack 的專案。
然後在 VS Code 中開啟建立後的專案:
code ./debug-vuejs-project-with-vscode
-
code
是 VS Code 自帶的命令,如果你執行時提示沒有此命令,可以看這裡設定一下。 -
debug-vuejs-project-with-vscode
是我的專案名稱。
生成 sourcemap 檔案
Vite
如果是通過create-vue 建立的專案,則修改 vite.config.ts 配置檔案,在開發環境生成 sourcemap 檔案。
export default defineConfig({ build: { sourcemap: true, }, // other configs... });
更多配置,請參考: http://vitejs.dev/config/build-options.html#build-sourcemap
Vue Cli
如果是通過vue-cli 建立的專案,則修改 vue.config.js 配置檔案,在開發環境生成 sourcemap 檔案。
module.exports = { configureWebpack: { devtool: "source-map" } // other configs... };
更多配置,請參考: http://cli.vuejs.org/guide/webpack.html
Webpack
如果是自己搭建的專案,則修改自己定義的 webpack 配置檔案,在開發環境生成 sourcemap 檔案。
module.exports = { devtool: "source-map", // other configs... };
更多配置,請參考: http://webpack.js.org/configuration/devtool/#devtool
配置檔案
launch.json
通過如下步驟,建立 launch.json 配置檔案(如果你的專案中已經存在該檔案,則可跳過此步驟)
- 選擇左側選單中的 Debug icon,開啟除錯選單。
- 點選 create a launch.json file,建立一個新的配置檔案。
- 選擇 Web App(Edge),當然,你也可以選擇 Web App(Chrome)
生成的 launch.json 檔案大致長這樣(不同版本的 VS Code 可能略有不同):
{ // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: http://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "type": "pwa-msedge", "request": "launch", "name": "Launch Edge against localhost", "url": "http://localhost:8080", "webRoot": "${workspaceFolder}" } ] }
將生成的 launch.json 檔案內容,替換為下方配置:
{ "version": "0.2.0", "configurations": [ { // 使用 Edge 瀏覽器除錯 "type": "msedge", // 使用 Chrome 瀏覽器除錯 // "type": "chrome", "request": "launch", "name": "vuejs: msedge", // 專案的訪問地址(需要改成你專案開發環境對應的地址和埠號) "url": "http://localhost:5173", "webRoot": "${workspaceFolder}", "pathMapping": { "/_karma_webpack_": "${workspaceFolder}" }, "sourceMapPathOverrides": { "webpack:/*": "${webRoot}/*", "/./*": "${webRoot}/*", "/src/*": "${webRoot}/*", "/*": "*", "/./~/*": "${webRoot}/node_modules/*" }, // 設定進入 debug 環境之前需要執行的任務。 // 此名稱對應專案中 .vscode 目錄下 tasks.json 檔案中的 label 屬性) "preLaunchTask": "vuejs: start" } ] }
上面的配置中,有以下幾點需要注意:
-
type
:VS Code 的 Debug 型別。msedge chrome
-
url
:瀏覽器啟動時訪問的地址。- 需要改為你專案的開發環境地址,如果一致則無需修改。
-
preLaunchTask
:設定進入 debug 環境之前需要執行的任務。- 此名稱對應專案中 .vscode 目錄下 tasks.json 檔案中的 label 屬性。
- tasks.json 檔案下面會建立。
更多資訊:
-
關於 launch.json 檔案的更多配置,請參考:
tasks.json
在專案的 .vscode 目錄建立 tasks.json 檔案,然後將下方內容貼上進去:
{ "version": "2.0.0", "tasks": [ { "label": "vuejs: start", "type": "npm", // 需要執行的命令(對應於 package.json 中的 scripts 命令) "script": "dev", "isBackground": true } ] }
上面的配置在執行時,執行的命令是: npm run dev
,如果你的專案是其他的啟動命令,那麼修改為對應的 script 名稱即可。
注意: type
的其他可選值是 shell
或者 process
,可不要傻乎乎的改成 yarn
。
type
:任務的型別。對於自定義任務,可以設定為 shell
或 process
。
shell process
更多資訊:
-
關於 tasks.json 檔案的更多配置,請參考:
-
關於 VS Code tasks 功能,更多資訊,請參考:
打斷點
咱們將 src/views/AboutView.vue 檔案的內容稍微改一下,然後打兩個斷點。
<script lang="ts" setup> import { reactive, ref } from "vue"; const other = reactive([ { name: "lmx", age: 18 }, { name: "foo", age: 20 }, { name: "bar", age: 12 }, ]); const count = ref(0); const handlePlus = () => { console.log("plus before", count.value); count.value++; console.log("plus after", count.value); }; const handleMinus = () => { console.log("minus before", count.value); count.value--; console.log("minus after", count.value); }; </script> <template> <div class="about"> <h1>This is an about page</h1> <div> <p>{{ count }}</p> <button @click="handlePlus">plus</button> <button @click="handleMinus">minus</button> <hr style="margin: 20px 0" /> <p v-for="item of other" :key="item.name"> {{ item.name }}: {{ item.age }} </p> </div> </div> </template>
在第 13 行和 第 19 行,分別打了 2 個斷點(在對應行號左邊,點選滑鼠左鍵即可打斷點):
注意事項
需要注意的是:一定要在啟動 Debug 前打好斷點,否則你將無法匹配到斷點。
啟動之後,在原始檔中新增新的斷點是無效的,執行中的編譯檔案無法匹配到新的斷點,除非修改原始檔的程式碼觸發編譯,這樣新生成的編譯檔案才會對映到新斷點。
我猜測的原因是:因為 *.vue
這種 SFC 格式的檔案,需要將 script
, template
, style
這 3 個模組拆分編譯,實際執行的是編譯後的 js 檔案,而且每次檔案修改或者重啟專案之後,都會編譯出新的檔案。
如果不提前打斷點,那麼原始檔和編譯後的檔案將不會關聯上。
彈窗提示
另外,我發現:不管是否提前打了斷點,在啟動時都會提示:
The task 'xxx' cannot be tracked. Make sure to have a problem matcher defined。
我搜了一下,暫時沒有找到特別完美的解決方案,這裡提供兩種蹩腳的方法:
方案一:
如果你不在意這個提示的話,可以每次都點選一下「Debug Anyway」按鈕,或者勾選一下「Remember my choice for this task」,以後每次執行的時候就不會提示了,所謂眼不見心不煩。
方案二:
把 launch.json 檔案中的 preLaunchTask
屬性去掉,Debug 之前自己手動啟動專案,反正配置 preLaunchTask
的目的就是自動幫你把專案啟動起來,所謂自己動手豐衣足食。
啟動 Debug
經過上述配置之後,就可以通過 Debug 模式啟動專案了,咱們來分別介紹一下「快捷鍵」和「手動啟動」這 2 種啟動方式。
快捷鍵:F5
如果你的專案只有 1 個 Debug 配置的話,可以直接通過 F5
快捷鍵啟動 Debug 模式,非常的簡單方便,推薦日常使用。
手動啟動
如果你的專案有多個 Debug 配置,launch.json 檔案的 configurations
陣列有多個配置物件。
這個時候 F5
快捷鍵啟動的就是第一個配置,如果你想要啟動其他 Debug 配置,就需要通過手動選擇了。
可以看到,點選「下拉選單」之後,展示了 2 個配置選項: vuejs: msedge
和 vuejs: chrome
示例中 launch.json 配置檔案的內容是這樣的:
{ "version": "0.2.0", "configurations": [ { "type": "msedge", "request": "launch", "name": "vuejs: msedge", "url": "http://localhost:5173", "webRoot": "${workspaceFolder}", "pathMapping": { "/_karma_webpack_": "${workspaceFolder}" }, "sourceMapPathOverrides": { "webpack:/*": "${webRoot}/*", "/./*": "${webRoot}/*", "/src/*": "${webRoot}/*", "/*": "*", "/./~/*": "${webRoot}/node_modules/*" }, "preLaunchTask": "vuejs: start" }, { "type": "chrome", "request": "launch", "name": "vuejs: chrome", "url": "http://localhost:5173", "webRoot": "${workspaceFolder}", "pathMapping": { "/_karma_webpack_": "${workspaceFolder}" }, "sourceMapPathOverrides": { "webpack:/*": "${webRoot}/*", "/./*": "${webRoot}/*", "/src/*": "${webRoot}/*", "/*": "*", "/./~/*": "${webRoot}/node_modules/*" }, "preLaunchTask": "vuejs: start" } ] }
看到這裡,你應該已經把 debug 的環境配置好了,現在可以開始愉快的除錯了。
一些問題
在我寫這篇文章的過程中,也發現了幾個讓我頭痛的問題,這裡順帶提一下。
在開始說這些問題之前,咱們先看一下這張圖:
- 標註 1:是咱們的程式碼原始檔。
-
標註 2:是執行時命中斷點後,VS Code 自動開啟的編譯後的檔案。
AboutView.vue?t=1661699383436 t
- 標註 3:是第一個斷點,行號是 13。
- 標註 4:是第二個斷點,行號是 19。
1. 必須先打斷點
我們可以看到,在執行過程中,其實斷點命中是編譯後的檔案。
前面我們提到,在執行 Debug 模式之後再去原始檔中新增新斷點,正常情況下是無法匹配的。
那如果我在除錯過程中,想要新增新斷點該怎麼辦呢?
方法 1:直接在「編譯後的檔案」中打新斷點。
此方法的弊端是:他是個一次性的斷點。
因為新斷點是針對這個編譯檔案的,如果原始檔改動後,會重新編譯出新的檔案,那麼這個斷點將會失效,後續將不會被匹配到。
方法 2:直接在「原始檔」中打新斷點。
此方法的弊端是:需要手動觸發編譯。
前面也提到過,在原始檔中新增新斷點之後,執行中的編譯檔案是無法感知到的,所以必須讓原始檔觸發重新編譯,生成新的編譯檔案,這樣原始檔的所有斷點就會同步對映到新的編譯檔案中了。
我每次觸發重新編譯的方式是,隨便在某個地方新增一行 console.log("")
,然後每次直接修改列印的內容即可。
2. 斷點位置不一致
原始檔和編譯後的檔案斷點的行號一致,但是對應的行號卻是不同的程式碼,和我們預期的斷點位置不一致:
對比之後可以看出,@vue/compiler-sfc 自動將
- SegmentFault 2022 年社群週報 Vol.9
- 社群精選 | 不容錯過的9個冷門css屬性
- 2022最新版 Redis大廠面試題總結(附答案)
- 手寫一個mini版本的React狀態管理工具
- 【vue3原始碼】十三、認識Block
- 天翼雲全場景業務無縫替換至國產原生作業系統CTyunOS!
- JavaScript 設計模式 —— 代理模式
- MobTech簡訊驗證ApiCloud端SDK
- 以羊了個羊為例,淺談小程式抓包與響應報文修改
- 這幾種常見的 JVM 調優場景,你知道嗎?
- 聊聊如何利用管道模式來進行業務編排(下篇)
- 通用ORM的設計與實現
- 如此狂妄,自稱高效能佇列的Disruptor有啥來頭?
- 為什麼要學習GoF設計模式?
- 827. 最大人工島 : 簡單「並查集 列舉」運用題
- 介紹 Preact Signals
- 手把手教你如何使用 Timestream 實現物聯網時序資料儲存和分析
- 850. 矩形面積 II : 掃描線模板題
- Java 併發程式設計解析 | 基於JDK原始碼解析Java領域中的併發鎖,我們可以從中學習到什麼內容?
- 令人困惑的 Go time.AddDate