第二章初始化Component
前言
上一篇文章讲了整个runtime-core
包的核心逻辑,以及patch
算法流程图
流程图: render
函数内patch
算法初始化组件和Element的流程,这一章就开始实现整个流程,我们只要按着流程去创建函数就可以了。
案例
今天项目的结构目录为以下图所示:
需要新建的文件比较多
我们首先实现一个案例
\example\helloworld\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>Document</title></head><body> <div id="app"></div> <script src="main.js" type="moudle"></script></body></html>
\example\helloworld\main.js
import App from './App.js'// vue3createApp(App).mount("#app")
\example\helloworld\App.js
export const App = { render() { return h('div','hi,'+this.msg) }, setup() { return { msg:"mini-vue" } }}
上文已经说过纯运行时的框架:
用户在使用它渲染内容时,直接为Render
函数提供了一个树型结构的数据对象。这里面不涉及任何额外的步骤,用户也不需要学习额外的知识。
创建好案例文件,我们希望编写完runtime
初始化组件模块,再去打开html
文件时,浏览器可以识别并且渲染出"hi,mini-vue"
的字符串。
createApp 、 creatVNode 、 render
createApp API
createApp API
主要接受一个树形结构rootComponent
参数
import { creatVNode } from './vnode'import { render } from './renderer'export function creatApp(rootComponent) { return { mount(rootContainer) { // 先转换成vnode // Component转换成vnode // 所有逻辑操作都基于vnode const vnode = creatVNode(rootComponent) render(vnode, rootContainer) } }}
参数
rootComponent
接受一个树形结构的内容,也就是App.js
中的内容
然后
creatApp
返回一个mount
函数
mount
函数接受一个参数rootContainer
,表示根节点
也就是index.html
中的根节点,所有
VNode
到时候都基于根节点开始依次往深层挂载
creatVNode API
生成VNode
export function creatVNode(type,props" /> render API
这时候我们就需要新建一个renderer.ts
的文件夹了
导出我们的render
函数,上一章已经详细说了runtime-core
的核心流程主要对是render
函数的调用。在render
函数中最重要的就是patch
算法
export function render(vnode, container) { // patch // 递归处理 patch(vnode, container)}
这样写是因为抽离出patch
函数后,可以方便我们递归调用patch
Patch
算法
好了接下来是比较简单的一些封装,迅速过一遍,根据注释和函数名就可以知道他们的功能作用了
\src\runtime-core\renderer.js
import { createComponentInstance,setupComponent } from "./component"export function render(vnode, container) { patch(vnode, container)}// 抽离出patch是为了方便后续的递归处理function patch(vnode, container) { // 处理组件 processComponent(vnode, container)}// 处理组件的进程function processComponent(vnode, container) { mountComponent(vnode,container)}// 挂载组件function mountComponent(vnode,container) { // 获得组件实例 const instance = createComponentInstance(vnode) // setup组件 setupComponent(instance) // 给组件绑定effect setupRenderEffect(instance,container)}// 这里主要是为将来更新组件diff算法做铺垫的// setup渲染并且绑定effect副作用function setupRenderEffect(instance,container) { const subTree = instance.render() // vnode-> patch // vnode -> element -> mountElement patch(subTree,container)}
\src\runtime-core\component.js
// 创建组件实例export function createComponentInstance(vnode) { const component = { vnode, type:vnode.type } return component}// 初始化组件export function setupComponent(instance) { // TODO // initProps() // initSlots() setupStatefulComponent(instance)}// 设置组件状态function setupStatefulComponent(instance) { const Component = instance.type const { setup } = Component if (setup) { const setupResult = setup() handleSetupResult(instance, setupResult) }}// 处理setup的结果function handleSetupResult(instance, setupResult) { // function object // TODO funciton if (typeof setupResult === "object") { instance.setupState = setupResult } finishComponentSetup(instance)}// 完成组件设置function finishComponentSetup(instance) { const Component = instance.render if(!Component.render) { instance.render = Component.render }}
完成源码初始化组件的逻辑,可以对照着这张流程图看一看代码的运行过程:
流程图建议大家多看看,这样理解程序运行逻辑时会更加的透彻!!!!后面文章也会反复提到这张图。
当然第一章中我们已经说了patch
算法会识别VNode
类型并且执行相应的渲染逻辑,那么下一章要对代码进行正式打包,并且识别VNode
类型了!