从零搭建调试Vue源码的环境

512 阅读4分钟

从零搭建调试Vue源码的环境

前言

应该会有不少刚开始读源码的小伙伴对刚clone下来的仓库不知道该如何下手,我个人目前比较喜欢的方式是写一些小列子+调试。虽然听过一些颅内编译的传说(bushi)但是那也仅限于一些简单的源码,像一些框架的源码内容太多了,光靠颅内编译是行不通的还是得调试,而且对于大部分人来说应该都还处于所见即所得的阶段吧。这篇文章会带你搭建一个简单的Vue源码调试的环境。

下载源码

git clone https://github.com/vuejs/core.git
cd core
pnpm i

构建源码

pnpm dev # 开启构建后就可以不用停了

目录在packages/vue/dist/vue.global.js

创建测试应用

# 当前目录是 dist 的同级目录
mkdir demo
cd demo
pnpm init
pnpm pkg set scripts.dev="vite" # 因为 vue3 是 monorepo 的项目,workspace里面有 vite 我们不用重复安装vite

创建一个index.html文件

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>vue3调试</title>
  </head>
  <body>
    <h1>hello world</h1>
  </body>
</html>

创建完毕直接执行命令

pnpm dev

然后我们就可以看到页面了

0-0.png

现在我们开始引用vue.global.js的文件,

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>vue3调试</title>
  </head>
  <body>
    <h1>hello world</h1>
    <script src="../dist/vue.global.js"></script>
    <script>
      console.log(Vue)
    </script>
  </body>
</html>

如果我们以打开文件的方式打开index.html的话会发现控制台成功打印出了Vue这个对象

0-1.png

但如果我们是用vite启动的一个服务则会看到两处报错

0-2.png

原因也显而易见,因为我们使用了vite打开这个项目,本质上就是以demo为根目录启动了一个web服务,且demo目录是静态文件目录,我们只能访问到demo目录下面的文件,但dist是和我们的demo同级目录。所以找不到vue.global.js这个文件。

下面我们找到这个scripts/dev.js这个文件,修改部分如下(dist前面加了个demo)

0-3.png

然后重新运行一次pnpm dev命令,

这时候我们可以发现在demo目录下面已经存在vue项目打包后的vue.global.js文件了

我们再修改一下index.html文件

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>vue3调试</title>
  </head>
  <body>
    <h1>hello world</h1>
    <script src="./dist/vue.global.js"></script>
    <script>
      console.log(Vue)
    </script>
  </body>
</html>

就会发现引入成功了,接着我们创建main.ts文件,

const { createApp } = Vue
const app = createApp({
  render() {
    return 'hahahah'
  }
})
app.mount('#app')

修改index.html文件

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>vue3调试</title>
  </head>
  <body>
    <div id="app"></div>
    <script src="./dist/vue.global.js"></script>
    <script src="./main.ts" type="module"></script>
  </body>
</html>

再次查看网页会发现hahahah已经出现了,证明Vue确实是被引入了。但是我们是使用的ts文件,会报错找不到名称Vue。我们在demo目录下创建一个类型声明文件vue.d.ts

declare let Vue: {
  [Key: string]: any
}

然后类型报错就消失了

我们新建一个App.vue文件。

<template>
  <div>我是{{ name }}</div>
</template>
<script setup>
const { ref } = Vue
const name = ref('vue/core')
</script>

修改main.ts

const { createApp } = Vue
import App from './App.vue'
const app = createApp(App)
app.mount('#app')

再次打开网页会发现报错了

0-4.png

因为ts并不能识别vue的模板语法,根据提示我们需要使用@vitejs/plugin-vue这个插件。

新建vite.config.ts文件

import vue from '@vitejs/plugin-vue'
import { defineConfig } from 'vite'
export default defineConfig({
  plugins: [vue()]
})

重新运行pnpm dev内容就能正常展示了

0-5.png

但这个时候main.ts还是有报错,因为我们ts无法识别.vue的单文件,所以我们还得新建一个类型声明文件shim-vue.d.ts

declare module '*.vue' {
  import { DefineComponent } from 'vue'
  const component: DefineComponent<{}, {}, any>
  export default component
}

这里的vue就是我们要调试的vue(怎么怪怪的🤤)。

# 首先我们在core根目录下打包源码
pnpm build # 打包原文件
pnpm build-dts # 打包类型声明文件
# 然后我们进入packages/vue
cd packages/vue
# 将vue项目link到全局
pnpm link --global
cd demo
pnpm add vue # 这里我们安装的vue会是我们全局链接的vue

0-6.png

ps:这里安装vue不仅仅是为了解决类型问题,如果单纯要解决类型问题的话,直接使用main.js就行了,这里安装vue主要是为了可以方便我们从新编译的vue和"正式"的vue之间切换

const { createApp } = Vue // 新 pnpm dev 打包后的vue
// import { createApp } from 'vue' // 旧 pnpm build 打包后的vue
import App from './App.vue'
const app = createApp(App)
app.mount('#app')

然后我们又会发现一个问题,如果vue文件设置langts,会找不到Vue,

<template>
  <div>我是{{ name }}</div>
</template>
<script lang="ts" setup>
const { ref } = Vue // 报错
const name = ref('vue/core')
</script>

并且如果我们关闭了类型声明文件后也无法识别.vue单文件,原因很简单,我们没有这个项目默认的typescript配置,只需要tsc --init就好了,最后我们就可以得到这个构建好的调试项目了

测试

修改

现在我们简单测试一下是否可以正常调试,修改App.vue如下

<template>
  <div>我是{{ name }}现在{{ age }}</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
// const { ref } = Vue
const name = ref('拾叁')
const age = ref(20)
</script>

修改packages/reactivity/src/ref.ts文件下的ref函数

0-7.png

现在网页即控制台信息是下面这样的

0-8.png

然后我们注释掉引入的vue,使用我们新打包的Vue

<template>
  <div>我是{{ name }}现在{{ age }}</div>
</template>
<script lang="ts" setup>
// import { ref } from 'vue'
const { ref } = Vue
const name = ref('拾叁')
const age = ref(20)
</script>

然后可以看到i'm chovrio已经被打印出来了,两遍是因为我们执行了两次ref

0-9.png

断点调试

只要打开控制台再刷新页面后就可以开始调试了,因为有sourcemap的我们调试的也是源码,不会是打包后的vue.global.js文件,这里的debugger的报错就是eslint的问题了,可以不管。

0-10.png

小结

其实将ts文件全部改为js文件的话会简单不少,不过全凭个人喜好吧,我暂时喜欢ts一点🥰。