【Vue3源码Runtime-core篇】 第一章 初识Runtime

第一章 初识Runtime

前言

当设计一个框架的时候,我们有三种选择:纯运行时的、运行时 + 编译时的或纯编译时的。
我们先聊聊纯运行时的框架。
假设我们设计了一个框架,它提供 一个 Render 函数,用户可以为该函数提供一个树型结构的数据对象,然后 Render 函数会根据该对象递归地将数据渲染成 DOM 元素。我们规定树型结构的数据对象如下:

const obj = {
	 	tag: 'div',
 		children: [
 				{ tag: 'span', children: 'hello world' }
		]
 }

每个对象都有两个属性:tag 代表标签名称,children 既可以是一个数组(代表子节点),也可以直接是一段文本(代表文本子节点)。接着,我们来实现 Render 函数:

function Render(obj, root) {
 	const el = document.createElement(obj.tag)
 	if (typeof obj.children === 'string') {
 			const text = document.createTextNode(obj.children)
 			el.appendChild(text)} else if (obj.children) {
			// 数组,递归调用 Render,使用 el 作为 root 参数
 			obj.children.forEach((child) => Render(child, el))
 	}

 		// 将元素添加到 root
 		root.appendChild(el)
 }

 const obj = {
	 	tag: 'div',
		 children: [
				 { tag: 'span', children: 'hello world' }
		]
 }
 // 渲染到 body 下
 Render(obj, document.body)

在浏览器中运行上面这段代码,就可以看到我们预期的内容。
现在我们回过头来思考一下用户是如何使用 Render 函数的。可以发现,用户在使用它渲染内容时,直接为Render 函数提供了一个树型结构的数据对象。这里面不涉及任何额外的步骤用户也不需要学习额外的知识。但是有一天,你的用户抱怨说:“手写树型结构的数据对象太麻烦了,而且不直观,能不能支持用类似于 HTML 标签的方式描述树型结构的数据对象呢?”你看了看现在的 Render 函数,然后回答:“抱歉,暂不支持。”实际上,我们刚刚编写的框架就是一个纯运行时的框架。

以上摘自《Vue3设计与实现》

根据上文再结合之前我们已经学习的@vue/reactivity包,对Vue响应式的原理有了深刻理解,那么这些副作用函数响应式数据到底是怎么在Vue中自动化渲染的呢?

答案: 就是接下来要学习的@vue/runtime-core包

学习完这个包之后,我们就会对 Vue 的挂载和更新有更加深刻的理解。
接下来还会涉及到Vue3中的虚拟节点diff算法、组件化、nextTick等重要知识点。

好了那么开始源码学习吧~

runtime-core模块

runtime-core的核心流程主要对是render函数的调用。在render函数中最重要的就是两个算法:patch算法diff算法

patch算法: 会遍历所有VNode虚拟节点,根据节点类型去运行相应的渲染操作,然后把虚拟节点一一渲染挂载到根节点上。
diff 算法: 会去对比新老虚拟DOM树,通过双端对比过滤出中间乱序部分,对乱序部分使用最长递增子序列算法求得最长递增的新DOM树,根据这个递增的新DOM树为基础,在其基础上 添加和移动 DOM树中其他乱序节点。

后续章节讲到diff算法时,会画图做演示,方便大家理解!

VNode

VNode 就是我们常说的虚拟 DOM 对象
那么我们怎么创建VNode呢?
createVNode 函数就是用来创建虚拟 DOM 节点的辅助函数, 它的基本实现类似于:

 function createVNode(tag, props, children) {
 		const key = props && props.key
 		props && delete props.key
 		return {
 						tag,
 						props,
						children,
 						key
 		}
 }

他最主要包括type,props,children三部分

举个例子:
vue模板中这段HTML结构

<div id="foo">
 	<p class="bar">{{ text }}</p>
</div>

最后会被编译成

render() {
		return createVNode('div', { id: 'foo' }, [
		createVNode('p', { class: 'bar' }, text, PatchFlags.TEXT) //PatchFlags.TEXT 就是补丁标志
	])
}

PatchFlags.TEXT 就是补丁标志
这个标志很重要,它会在patch算法中被识别并进入相应的初始化阶段

那么生成了虚拟dom对象后,我们就可以进入下一步,patch算法环节了。

patch算法

首先我们先看下源码中的runtime-core文件夹下renderer.ts文件找到patch函数。
其实patch算法内部就是判断我们生成好的VNode的类型,然后根据类型去做一些渲染初始化操作

渲染一般分3步:创建元素节点添加元素属性挂载到对应的节点上

在这里插入图片描述

可以看到源码判断了很多种情况,包括Text、Static等类型和SUSPENSE、TELEPORT、Fragment等组件

上文提到patch算法通过判断VNode的类型进行不同的渲染逻辑
我们刚起步,所以主要重点讲一讲runtime-core初始化ElementComponent的这两种类型,它们两对应processElementprocessComponent两个初始化函数,那么这两个函数运行逻辑是怎么样呢?
我们看一下patch算法在识别ElementComponent运行过程的流程图:
在这里插入图片描述

了解了patch算法运行的流程图之后,下一章节我们就可以正式的开始,初始化Component逻辑了,也就是源码中的processComponent(vnode, container)函数。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值