阅读vue3记录(三)

273 阅读4分钟

「这是我参与2022首次更文挑战的第14天,活动详情查看:2022首次更文挑战

这次我们直接在编辑器中阅读,但是不要忘了之前的断点执行过程。

打包配置

在读源码前不妨看看,整个项目的入口文件和打包输出目录,都在rollup的打包配置里了。

image.png

image.png

找到入口文件vue-next\packages\vue\src\index.ts ,点进去,发现这个文件就做了两件事。

1 声明了一个函数compileToFunction 
2 用registerRuntimeCompiler注册了它 , registerRuntimeCompiler引自 @vue/runtime-dom

compileToFunction

我们就着重看compileToFunction 这个函数,看它的命名和形参,应该是个编译vue模板的函数。

template: string | HTMLElement, options?: CompilerOptions 模板和编译选项两个参数。 模板的处理 模板有可能是字符串也可能是一个元素节点, 如果是一个元素节点就取其innerHTML .如果是字符串,就要判断这是一个选择器还是vue的模板. 最终都要是一个类似html的模板字符串。 然后把这个字符串编译为对应的render函数,所以如果提供了render函数,就应该不会调用这个方法。

看到下面处理模板的代码,只处理了#开头的选择器,也就是id选择器,让我忍不住想试试其他的选择器。

尝试了一下,标签 类 属性选择器,果然都支持, 因为用的是queryselector,不过似乎没看到处理其他选择器的地方。暂且放下。

image.png

继续往下就到了,把template编译为render的部分了.后面是一些错误处理,就先不看了。 调用 compile 函数 编译template.然后把code 从complie函数的返回值里解构出来。

image.png

最后用Function传入code把render生成函数构造出来,调用它生成render,缓存render,返回render。也就是说这个compileToFunction 最终结果就是返回一个render函数 。这波操作真的是长见识了。function 居然可以这么用,虽然有时候把function类型打印出来,会看到native code 。

image.png 于是看了一眼mdn Function接收两个参数,前面的是形参,后面的是函数主体代码字符串。然后,发现了下面这个, 不过这个确实不影响,这个函数的形参也比较奇特,总是取最后一个参数作为body。

image.png

到这里这个文件就差不多了,目前的主线还是初始化和挂载,所以就不去看compile register这些函数的具体实现了。 找到之前断点定位的找到之前的声明createApp函数所在的文件vue-next\packages\runtime-dom\src\index.ts,我之前打了书签标记(bookmark)

image.png 这就是前面断点调试看过的 先调用ensureRenderer 获得一个renderer,然后调用人renderer上的createApp得到实例APP。 这个createApp经过两层查找,发现是在 packages\runtime-core\src\apiCreateApp.ts 里,我们重点看mount方法。mount只会执行一次,所以需要判断状态,并且执行之后改变状态为已挂载, 如果状态是已经挂载,则抛出警告。

image.png

初次挂载,会创建根组件VNode,继续往下走,发现无论如何都会调用render函数,这个render是外部传入的,于是退回到packages\runtime-core\src\renderer.ts ,这个render是在这个被声明的。

image.png

所以,重点就要看patch函数, 初次挂载时,老vNode肯定是没有的。

patch

n1 n2 就是新老Vnode。

image.png 走到switch的时候,由于type是一个对象,因此走default,最后走到了processComponent. 进入这个函数就能看出,由于初次挂载n1为null,因此不走更新组件而是 mountComponent。

image.png 然后走过了一些函数,突然有个函数让人眼前一亮 setupComponent,这就是新的api setup相关的吧,这个时候,mount还没执行完, vnode还没有转换为dom。继续往下走就到能传入的实例, 上面的一个属性ctx里面是熟悉的vue2的组件属性。最后调用setupStatefulComponent,这个方法里面我们就能看到,用户传入的setup函数的调用。

如果有setup,就会调用handleSetupResult,没有就会finishComponentSetup。 但是,进去走了一段之后,你会发现最终还是要调 finishComponentSetup,这个函数里面可以看到,由于没有传入render,就会调用complie方法(这就是我们在入口文件里看到, 被注册了的compileToFunction),赋值给render。

image.png