前端调试篇(二):如何在各种场景调试vue3源码?

411 阅读4分钟

对于要学习vue3源码的前端小伙伴来说,通过调试vue3源码来学习是一种不错的方法,本期来介绍下调试方法。

一、下载vue3源码

先使用git clone命令把vue3源码从远程仓库克隆本地。

git clone https://github.com/vuejs/core.git

如果出现克隆失败,可以直接download源码的压缩包,下载完后解压即可。

二、安装依赖

vue3采用的是Monorepo + pnpm的方式来管理仓库的,所以我们需要通过pnpm install来安装依赖,如果没有安装pnpm,需要先运行npm install pnpm -g安装下pnpm注意node版本需要在16以上

pnpm install

三、开发环境打包

安装完依赖之后,执行npm run dev命令进行开发环境打包。 打包后会生成两个文件:

  1. core/packages/vue/vue.global.js:以global方式打包后的vue文件
  2. core/packages/vue/vue.global.js.map:生成的源码映射文件,这样我们调试的时候就能映射到对应真实源码中的各个文件了。

这里其实还有npm run build命令,为啥要用npm run dev呢,主要有两个原因:

  1. 相比而言,npm run dev生成的文件更少,速度更快,
  2. npm run dev支持热更新,也就是我们可以手动修改vue源码后,会重新打包,这样可以很方便的在源码中增加debugger

四、编写vue代码,开始调试

我们可以在core/packages/vue/examples路径下增加一个test.html文件,代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <h1>计数:{{ count }}</h1>
        <button @click="handleClick(1)">+1</button>
        <button @click="handleClick(-1)">-1</button>
        <button @click="reset()">reset</button>
    </div>
    <script src="../dist/vue.global.js"></script>
    <script>
        const { ref, createApp } = Vue
        const App = {
            setup() {
                const count = ref(0);
                const handleClick = () => {
                    debugger;
                    count.value++
                }
                const reset = () => {
                    count.value = 0
                }
                return {
                    count,
                    handleClick,
                    reset,
                }
            }
        }
        createApp(App).mount('#app');
    </script>
</body>
</html>

渲染页面如下:

我们点击“+1”按钮后,浏览器就会在debugger那里停住,这样就可以开始调试vue源码了。

前面说过,执行npm run dev是有热更新功能的,我们直接找到ref对应的源码文件/core/packages/reactivity/src/ref.ts,找到对应的ref方法,增加debugger

增加代码并保存文件之后,会发现热更新生效了,重新打包了一次,生成了新的vue.global.jsvue.global.js.map文件,再回来我们之前的页面进行刷新,会看到浏览器就会停在我们刚刚增加的断点位置。

五、如何在vscode中调试vue3源码?

假如你不太喜欢在浏览器调试,你还可以在vscode中调试。

这里需要先启动一个静态服务,这里我直接用http-server来实现,如果没安装的话先执行npm i http-server -g安装一下。

它的用法很简单,直接在对应的目录执行http-server命令就好了,这里我直接在根目录启动静态服务。

然后在vscode中调试配置文件launch.json中增加如下调试程序:

{
  "version": "0.2.0",
  "configurations": [
+    {
+      "type": "chrome",
+      "request": "launch",
+      "name": "debugger vue source",
+      "url": "http://localhost:8080/packages/vue/examples/test.html",
+      "webRoot": "${workspaceFolder}"
+    },
  ]
}

这里注意把url配置成你需要调试的文件路径,端口也在与你之前启动的静态服务的端口对应上,我这里配置的url是http://localhost:8080/packages/vue/examples/test.html"

然后我们就可以点击开始按钮进行调试了:

vscode会启动浏览器,并访问我们配置的网页url路径,而且断点也生效了。

这样的话,我们打断点也会很方便,直接利用vscode文件行号左边的小红点就好了,这里就不多说了。

六、实战:如何在实际vue项目中调试vue3源码?

如果我们想在实际vue项目中调试vue源码,要怎么做呢?

6.1、创建vue项目

我们先使用vue-cli创建一个vue3项目:

  1. 执行vue create vue3-demo命令创建项目,选择vue3版本
  2. 通过npm run serve把项目跑起来。

需要修改下devtool配置:

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
   transpileDependencies: true,
+  configureWebpack: config => {
+    config.devtool = 'source-map';
+  }
})

6.2、增加断点

App.vue中增加如下代码:

import { onMounted } from 'vue';
onMounted(() => {
  debugger;
})

6.3、创建调试文件

然后创建vscode的调试文件,如下所示。

{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "chrome",
            "request": "launch",
            "name": "debugger vue3 source",
            "url": "http://localhost:8080",
            "webRoot": "${workspaceFolder}",
        }
    ]
}

6.4、开始调试

通过断点进入vue源码后,发现调试的文件是E:/code/vue3-demo/node_modules/@vue/runtime-core/dist/runtime-core.esm-bundler.js,这显然是打包后的文件,不符合我们的预期,我们需要调试的是代码源文件。

6.5、修改vue源码中rollup.config.js

默认通过pnpm run build打包是不生成sourcemap,我们需要修改下rollup.config.js中的配置文件,使其产出sourcemap

// rollup.config.js
- output.sourcemap = !!process.env.SOURCE_MAP
+ output.sourcemap = true

然后执行pnpm run build命令。

可以看到带有.map后缀的sourcemap文件已经生成了。

随后将生成的E:\code\vue3-demo\node_modules\@vue\runtime-core\dist目录,替换掉我们之前项目里的文件目录,我这里是E:/code/vue3-demo/node_modules/@vue/runtime-core/dist

替换完毕后,重新跑下npm run serve,然后重新跑下调试。

虽然进到了通过sourcemap进入到了源码文件,但这个路径明显不是本地的路径,导致这个文件仅仅是readonly可读的,这样对调试也不太友好。

这里显示的路径的E:/code/src/apiLifecycle.ts,而我们真实的文件路径是E:\code\core\packages\runtime-core\src\apiLifecycle.ts,没对应上,自然就只读了。

我们需要通过rollupoutput.sourcemapPathTransform修改下生成的sourcemap文件路径:

// rollup.config.js
+ output.sourcemapPathTransform = (relativeSourcePath, sourcemapPath) => {
+  const newSourcePath = path.join(
+    path.dirname(sourcemapPath),
+    relativeSourcePath,
+  )
+  return newSourcePath
+ }

这里就拿我们上面提到的apiLifecycle.ts文件来说,relativeSourcePathsourcemapPathnewSourcePath分别为:

  • relativeSourcePath: ..\..\runtime-core\src\apiLifecycle.ts
  • sourcemapPath:E:\code\core\packages\vue-compat\dist\vue.runtime.esm-browser.prod.js.map
  • newSourcePath:E:\code\core\packages\runtime-core\src\apiLifecycle.ts

然后重新执行npm run build,再把之前说的dist文件再替换一次。

再次重新运行npm run serve之前,需要清空下babel-loader缓存:rm -rf node_modules/.cache/babel-loader/

这样就ok了,对应上了本地的源码文件,这个文件也能修改了。

6.6、调试 create-vite 创建的 vue 项目

如果通过vite跑的vue项目,需要注意两点:

  1. 需要修改vite.config.ts
export default defineConfig({
    plugins: [vue()],
+   optimizeDeps: {
+    exclude: ['vue']
+  }
})

加这个配置项,是为了不让vuevite预编译时被esbuild处理,否则路径会出问题。

  1. 如果已经被处理过,需要rm -rf node_modules/.vite清除缓存

七、小结

上述介绍了如何在各种场景中调试vue3源码,主要包括以html文件demo的方式,以及在实际项目中如何调试vue3源码,希望对大家学习vue3源码和调试bug有帮助!