从零搭建调试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
然后我们就可以看到页面了
现在我们开始引用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这个对象
但如果我们是用vite启动的一个服务则会看到两处报错
原因也显而易见,因为我们使用了vite打开这个项目,本质上就是以demo为根目录启动了一个web服务,且demo目录是静态文件目录,我们只能访问到demo目录下面的文件,但dist是和我们的demo同级目录。所以找不到vue.global.js这个文件。
下面我们找到这个scripts/dev.js这个文件,修改部分如下(dist前面加了个demo)
然后重新运行一次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')
再次打开网页会发现报错了
因为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内容就能正常展示了
但这个时候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
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文件设置lang是ts,会找不到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函数
现在网页即控制台信息是下面这样的
然后我们注释掉引入的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
断点调试
只要打开控制台再刷新页面后就可以开始调试了,因为有sourcemap的我们调试的也是源码,不会是打包后的vue.global.js文件,这里的debugger的报错就是eslint的问题了,可以不管。
小结
其实将ts文件全部改为js文件的话会简单不少,不过全凭个人喜好吧,我暂时喜欢ts一点🥰。