【我要学源码】 Vue3源码知识剖析 (第二期)

回顾

哈喽~ 各位亲爱的小伙伴,第二期的Vue3.0源码共读来啦!!!
本期内容小编将给大家带来分析启动脚本,查找入口文件,看看首次编译过程,从源码层面解析初始化细节。
另外,最为重要一点,还没有关注B站Up主的小伙伴一定要关注哦,关注大佬不迷路~

WechatIMG63.jpeg

在我们开始这期内容之前,小编先带领大家回顾一下上期的知识点。

erDiagram
vue ||--o{ runtime-dom : import
vue ||--o{ compiler-dom : import
runtime-dom ||--o{ runtime-core : import
vue ||--o{ reactivity : import

还是从这张图开始,在上期内容中我们谈到,Vue3.0的源码中最为核心的包就是叫做以 vue 命名的文件包,在这个包内有三个依赖关系,其中 runtime-domcompiler-dom 是明显的依赖关系,但其中还有一个隐性的依赖叫做 reactivity 的依赖,这里面主要是做响应式的一些处理,后期小编会带领大家着重探讨这个依赖包的原理,而其实真正的依赖在 runtime-core 这个包里,这个包才是真正告诉你 createApp 是怎么由来的,内部又做了哪些事情。

graph TD
createApp --> ensureRenderer --> baseCreateRenderder --> renderer --> renderer.createApp --> createAppAPI --> App

小编这里梳理了一下逻辑关系图,createApp 这个方法最终在 createAppAPI 内部有一个工厂函数在这里得到了一次扩展 createApp 的方法,扩展此方法最为重要的一点就是为了让这个函数变得更加通用,我们知道在 createApp 里面传入一个 render 函数,但是对于 createApp 来说它只管调用传入进来的参数,而并不关心你这个参数做了哪些的逻辑处理,真正我们能够使用实例上的方法就是来源这个 createAPI 扩展之后得到的属性,例如 component mixin use

正文

经过之前的内容回顾,想必大家对 createApp 已经有了深刻的了解,接下来将进入到本章内容的核心部分,首先带领大家分析启动脚本,看看启动脚本都做了哪些事情。

"dev": "node scripts/dev.js --sourcemap",

这行代码想必大家应该都不陌生吧,这就是我们启动项目的运行脚本我们就要从 dev.js 这个脚本文件开始下手。

WeChat46b3d93f7c154ae5e1b95022c46eb57b.png

minimist 是专门用来解析传进来的参数,什么意思呢?当我们在运行 npm run dev 这行命令的时候实际上是运行 node scripts/dev.js 当后面的路径传入 -- 或者 - 的时候会被解析成为第一个参数,否则会打包成 vue 的包。

WeChat96a3eefa625cef34b030b68fe2353724.png

在这张图中也能看出 dev-sfc 部分使用的打包格式为 esModule 格式,在浏览器中则会以 type=module 出现。
那么我么现在知道指令映射的代码中的含义了,那么问题来了下一步我们该干什么呢?哈哈哈~
如果对工程化的框架比较了解的话,我们就要看看打包工具的配置文件,看看打包过后你的 entry 入口文件和你的 export 打包后输出文件是怎样配置的,在源码中使用的是 rollup 打包生成的,所以我们要浏览一下 rollup.config.js 这个配置文件。

WeChatc8eb0ccf03ff5fb6b79d0ed338c01de1.png

在配置系文件中我们就可以看到,所有获取包目录都是从 packages 中得来的,通过 process.env.TARGET 进行包名的路径拼接,就会得到 /packages/xxx

WeChat024e26087aec3918dc0ecf425d3c7580.png

这里就是要配置的一些打包选项,例如 CommonJs ECMAScript 格式,这里讲一下 iife 格式,就是打包后生成一个匿名函数自调的格式,(()=>{})()

WeChat465926fb4d72cd8107ca6bf763ffba32.png

这里我们看到真正打包的是以 runtime 开头的包名称,这时候他会根据你的指令中是否包含 -f runtime-xxx 这种格式的映射,生成两种第一个是运行时打包,没有在初始化的时候把编译器打包进去,第二个则是全量打包。

WeChatee256bb4095f1e2a86c2e2321b505971.png

所以,我们就找到了在 packages/vue/index.ts 这个路径下的包,在这个包里面有一个叫做 compileToFunction 编译函数其真正的作用就是一个是解析 template 中的 innerHhtml,另一个就是生成一个 render 渲染函数,另外,在这个函数中他会判断你传进来的参数是不是以字符串形式的模板另外一个是不是 dom,就是说支持的写法 mount("< div>xxx</ div>") mount('#app') 或者 mount(app)

WeChat98c822fa89560f875415a68ff6276a83.png

这里在 callStack 中就真正的解析了 compileToFunction 是如何一步一步的变成渲染函数。

WeChat00a9bbcb59649cbbf8b1ce99b6c81d43.png

WechatIMG81.jpeg

从这个上面我们就看出,template 真正就是传入的 innterHtml

WeChat70bfdad15d56582ac1f31f3ea6ccab68.png

通过浏览器调试后我们就得出 compileToFunction 是在 finishComponentSetup 这个函数中调用的,而 template 就是要解析的 innerHtml

WeChat5f3653a52a450b5ece0d35a757cabe17.png

其实对于 compileToFunction 这个包并没有做什么事情,只是原封不动的把依赖包又导了出去,那么我们就找到了 vue 包中有一个叫做 runtime-dom 的依赖,我们点击这个路径后就找到了 runtime-core 的依赖,就下来就和昨天的操作顺序一样。

WeChat581967ab4ceb37c6fefa2ed599d85bad.png

WeChat802ba115543725ce52877e96d64aa870.png

随后的流程就是:
createRenderer() => baseCreateRenderer() => createAppAPI(render, hydrate) => createApp() => mount() => render() => patch(n1, n2) 对比新老节点 => ShapeFlags.COMPONENT 首次执行挂载走 component => processComponent() => mountComponent() 挂载组件 | updateComponent() 更新组件

这里我们主要研究一下 mountComponent() 做了哪些事情。

WechatIMG82.jpeg

WeChat280ea252a066a6ad52f94d5f261b9ba3.png

在这个函数内主要有两个函数调用,一个是 createComponentInstance 创建组件实例,另一个是 setupComponent 初始化组件实例,从这个函数开始找寻。

WechatIMG86.jpeg

WechatIMG87.jpeg

在这里我们就看到组件初始化接收的就是一个 setup 这块对传入不同方法做了各种处理,例如 promise随后会在 handleSetupResult() 这个方法内处理 setup 的返回值。

WechatIMG88.jpeg

根据 setup 返回不同的类型作出处理,比如 返回的是一个函数,对象或者字符串之类的。

WechatIMG89.jpeg

执行完毕后都会走 finishComponentSetup() 这个方法。

WeChatde62b32d0810ec4d669cb295a41fd91a.png

WeChat3cc852f1a9e27e9a71503075b277a1c8.png

所以在这个函数内真正的是 返回一个 render 函数,并且兼容 Vue2.0 的方法。

到这里,基本上初始化所有的逻辑就理顺啦~

总结

这期内容小编就告诉大家从代码的层面一步步的深入了解初始化过程,首先是从 package.json 目录文件中锁定执行的哪个脚本文件,我们通常使用的打包工具如:webpack vite 或者 rollup 之类的都会有一个配置文件 xxx.config.js 从这个文件中我们就能锁定 entry 的入口文件,通过确定入口文件之后我们就能找到各个包之间的依赖关系,根据依赖包,一步步深挖理解初始化的整体流程。

另外,顺便说一句 Vue3.0gitHub 上的地址已经 从 next 变为 core 这意味着属于 Vue3.0 的时刻即将到来~

WeChat5d2b761a06ae8bb77a30134ff72bde23.png

npm 上还是 vue2.0 的版本,需要使用3.0仍需用到 next 不过从2.0升级到3.0也就是时间上的问题,所以各位小伙伴静候佳音。

猜你喜欢

转载自juejin.im/post/7054949920830128164