第二章初始化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类型了!