「这是我参与2022首次更文挑战的第14天,活动详情查看:2022首次更文挑战」
这次我们直接在编辑器中阅读,但是不要忘了之前的断点执行过程。
打包配置
在读源码前不妨看看,整个项目的入口文件和打包输出目录,都在rollup的打包配置里了。
找到入口文件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,不过似乎没看到处理其他选择器的地方。暂且放下。
继续往下就到了,把template编译为render的部分了.后面是一些错误处理,就先不看了。 调用 compile 函数 编译template.然后把code 从complie函数的返回值里解构出来。
最后用Function传入code把render生成函数构造出来,调用它生成render,缓存render,返回render。也就是说这个compileToFunction 最终结果就是返回一个render函数 。这波操作真的是长见识了。function 居然可以这么用,虽然有时候把function类型打印出来,会看到native code 。
于是看了一眼mdn Function接收两个参数,前面的是形参,后面的是函数主体代码字符串。然后,发现了下面这个, 不过这个确实不影响,这个函数的形参也比较奇特,总是取最后一个参数作为body。
到这里这个文件就差不多了,目前的主线还是初始化和挂载,所以就不去看compile register这些函数的具体实现了。
找到之前断点定位的找到之前的声明createApp函数所在的文件vue-next\packages\runtime-dom\src\index.ts,我之前打了书签标记(bookmark)
这就是前面断点调试看过的 先调用ensureRenderer 获得一个renderer,然后调用人renderer上的createApp得到实例APP。 这个createApp经过两层查找,发现是在
packages\runtime-core\src\apiCreateApp.ts 里,我们重点看mount方法。mount只会执行一次,所以需要判断状态,并且执行之后改变状态为已挂载, 如果状态是已经挂载,则抛出警告。
初次挂载,会创建根组件VNode,继续往下走,发现无论如何都会调用render函数,这个render是外部传入的,于是退回到packages\runtime-core\src\renderer.ts ,这个render是在这个被声明的。
所以,重点就要看patch函数, 初次挂载时,老vNode肯定是没有的。
patch
n1 n2 就是新老Vnode。
走到switch的时候,由于type是一个对象,因此走default,最后走到了processComponent. 进入这个函数就能看出,由于初次挂载n1为null,因此不走更新组件而是 mountComponent。
然后走过了一些函数,突然有个函数让人眼前一亮
setupComponent,这就是新的api setup相关的吧,这个时候,mount还没执行完, vnode还没有转换为dom。继续往下走就到能传入的实例, 上面的一个属性ctx里面是熟悉的vue2的组件属性。最后调用setupStatefulComponent,这个方法里面我们就能看到,用户传入的setup函数的调用。
如果有setup,就会调用handleSetupResult,没有就会finishComponentSetup。 但是,进去走了一段之后,你会发现最终还是要调 finishComponentSetup,这个函数里面可以看到,由于没有传入render,就会调用complie方法(这就是我们在入口文件里看到, 被注册了的compileToFunction),赋值给render。