原来调试vue源码这么简单!

818 阅读6分钟

前言

遥想入行时,最先接触的框架就是vue,但从工作以来再也没碰过vue,直到现在,vue的基本语法和api的使用基本都忘得七七八八了,但现在公司要求用vue,只能是看下官网快速上手,然后接手需求开发。

接连使用了几星期,感受到了vue的上手简单,而且因为模板的限制,写出来的代码不至于太难看。而且使用的越是久就越觉得双向绑定在一些场景真是非常方便,很多时候更新列表某一条数据,无需父组件透传setState,也无需通过id筛选出自己再改自己,而是直接改props传下来的数据即可。

但是,俗话说的好,凡事有两面性,它带来了便利性,自然也会舍弃了一些东西。正所谓知其然,知其所以然,我们不能只停留在会用的程度,这样很容易踩坑,最好是深入其中了解其基本实现逻辑,真正了解其舍弃了什么,方可让我们少踩坑或者踩了坑也知道怎么上去。

而想知道其基本实现原理,自然少不了对源码的调试了。

很多朋友看到调试源码,瞬间望而却步,觉得这又难又麻烦。其实不然,vue本身不复杂,它的架构也非常清晰,我们可以很容易的调试到想要调试的代码。而本文就这点展开聊聊,手把手教大家调试vue源码。

打包目标源码

首先我们先把vue拉下来,然后直奔package.json文件。

此时,我们可以看到scripts中有一个dev命令,这条命令就是方便开发和调试vue的,所以我们只要深入到这条命令,搞清楚如何开发和调试vue。

{
  "scripts": {
    "dev": "node scripts/dev.js"
  }
}

这条命令告诉我们,它要执行的是scripts下的dev.js文件,那么我们去翻一下这个文件。

虽然这个文件大多是node相关的东西,但就算不太熟悉node也不影响,我们只要关注一些重点即可。

const args = minimist(process.argv.slice(2))
const targets = args._.length ? args._ : ['vue']
const format = args.f || 'global'

这里贴出了三行代码,这三行代码的意思是我们在执行dev命令的时候是可以传入两个参数的,一个是_,另一个是f

不熟悉node的,可以随意在dev命令后面添加代码,然后在打印一下args,比如下面这样。

// package.json
"dev": "node scripts/dev.js abc -f efg"

// dev.js
const args = minimist(process.argv.slice(2));
console.log('args', args);

像这样修改后,然后执行npm run dev命令。

image.png

这下就很清楚所谓的_f这两个参数了。

弄清楚这两个参数之后,我们继续看上面的targets和format的定义,targets表示,如果不传参那么默认就是vue,而format不传参的话默认就是global。

看到这里,targets其实很好理解,经过刚刚执行dev命令,大家可以去看看产物,会发现packages下的vue多了个dist,我们就清楚了,targets表示要打包的代码。

而format就还是有点懵了,我们继续往下看。

const outputFormat = format.startsWith('global')
  ? 'iife'
  : format === 'cjs'
    ? 'cjs'
    : 'esm'

这里就是针对format再做解释了,所谓的global就是挂全局,所以打包出来是个iife(立即执行函数),同时在此我们也可了解到所谓的firmat就是打包后输出的格式,有iife,cjs,esm。

从这里,直到最后面通过esbuild执行打包都可以不太关心,我们进行调试代码的话,除了打包目标代码和esm格式好让我们轻松看懂打包后的代码外,还有一点非常重要,那就是source-map,不过即是dev命令,vue已经帮我们设置好了。

image.png

ok,了解到这就够我们进行vue源码的调试了,我们可以针对packages下面的包进行打包,然后选择esm的格式,最后我们写demo的时候只要引入这个打包的产物就好了。

写demo调试源码

有了上面的基础后,我们可以开始调试源码了。

众所周知,vue的核心之一在于响应式,对于我这样的初学者来说,掌握响应式极其重要,那么这里我们就以调试响应式代码为例。

我们先将dev命令改一改。

"dev": "node scripts/dev.js reactivity -f esm-browser"

reactivity这个包就是实现响应式的包,所以我们只打包它就好了,然后输出格式是esm。

接着我们执行dev命令,执行完成后,packages/reactivity下就多了个dist产物,这个dist中有两个文件,一个是代码,另一个是sourcemap文件。

image.png

我们打开代码文件。

image.png

我们可以看到很多熟悉的api,也会看到很多不熟悉的api,这就是看源码的好处之一了,能认识更多api。

ok,那么我们就开始写demo,比如这里我们想了解ref的实现,那么就写下如下代码。

image.png

然后通过live server打开这个文件,打开控制台,然后再刷新,这时候我们就开始调试了。

image.png

通过调试的工具,我们进入到ref的实现中。

image.png

image.png

由于sourcemap的存在,我们也可以轻松定位到目标文件中。

虽然至此整个调试源码就已经非常方便了,但其实还是不够的,正所谓调试调试,我们会不断更新demo或者在源码中打断点,更新demo好说,直接该demo代码刷新浏览器即可,但在源码中打断点就麻烦了,因为我们引入的是打包后的产物,这个产物已经固定,我们在源码中加入的debugger自然不会生效。

但是,这可是dev命令啊,dev是开发和调试的命令,没有类似热更新的东西,那开发效率多低,所以不用担心,我们尽管改源码,大胆写猜想,剩余的交给打包工具👍

我们回到dev文件中,看末尾的一行代码。

image.png

这是什么?watch!它会监听目标打包文件的改动,若有改动就会重新打包,这就能达到类似热更新的效果了。我们来体验一下。

此时,我们在ref的入口添加debugger。

动画.gif

注意看终端的显示,当我敲完debugger后进行保存,终端就显示我们又进行了一次打包。

结尾

ok,本文到此就结束了,本文以初学者想要了解vue的一些核心api的实现为引,不断引出调试vue源码的方法,最后通过一个简单例子展现了这一过程。

最后的最后,希望本文能帮助到各位,另外,觉得本文不错的话,请不要吝啬手中的赞哦🌹🌹🌹