date
Apr 7, 2023 09:04 AM
type
status
slug
summary
tags
category
updated
Oct 10, 2024 08:04 AM
icon
password
摘要
前面主要是vue的源码仿写,最主要的是vue的响应式,以及依赖收集。是一个简易版本,和官方源码还是有很多细微差别的,例如数组代理之后改变数组长度,会触发更新之类的。数组还会被收集长度这种依赖关系,以及数组的一些
splice,push,shift,unshift,pop
这些方法重写,来完成修复一些数组在vue依赖更新中的BUG。Vue 中为了解耦,将逻辑分成 2 个模块
- 运行时 核心(runtime)(不依赖平台的 browsweer test 小程序 app canvas....) 靠的是虚拟 DOM
- 针对不同平台运行时,vue 是针对浏览器平台的
- 渲染器
构建自己的runtime-dom
这个功能主要是为了提供一个操作dom的方法,新建一个
rumtime-dom
的文件夹在packages
中。然后cd 到该目录下运行pnpn init
,生成的package.json
,修改成如下 。修改项目的package.json中dev的参数。
然后参考着官方的文件。创建如下文件
在
nodeOps.ts
中编写需要的dom操作方法。而
patchProp.ts
主要是操作样式的方法先打个小样,后期慢慢填充。
而主要文件index.ts中就是将这些合并起来
编写runtime-dom内容
runtime-dom主要是提供一个虚拟dom的操作方法。前端在代码编写的过程中,要设置类名,style样式,绑定事件,还有设置普通属性。还有node自身的属性操作。例如将节点增加到指定位置,删除节点等等,这些是dom原生就有的功能,可以进一步封装使用。这里学习一下他的核心思想。
所以runtime-dom的核心就是提供渲染器需要的options。实际上runtime-dom并未做什么事情。
所以
patchProp.ts
的代码编写就是这样的。传入class的时候
<div class="a"></div>
==> <div class="b"></div>
这个时候是需要被操纵的元素dom,还有最新传入的class值。这里简单的来看是不需要旧的class值的,直接覆盖新值就可以了。
所以modules/class.ts
的文件就是抛出一个pathcClass函数,这个函数接受了(el,nextValue)
传入style值的时候
<div style="color:red;font-size:14px;"></div>
==> <div style="color:yellow"></div>
这样的操作,好像直接可以旧值覆盖新值,不用做比较。如果你是一个vue开发的话,就知道这样一种写法<div :style="{color:'red',fontSize:'14px'}"></div>
,style可以动态的改变,作为一个对象。这样的话,如果直接覆盖,是不会识别font-size的。所以需要做一个新旧值的对比。传入绑定事件
通常原生的JS在一个dom元素上绑定一个事件,然后换绑定另一个事件。要经历一个绑定->解绑 ->再绑定新的事件。这样的操作十分耗费性能。而如果我们绑定一个自定义的事件,然后在里面绑定要绑定的方法,这样当要绑定的方法更换的时候,不需要重新解绑再绑定,而只需要更新要绑定的方法就行。
所以
event.ts
这样第一次进入的时候是没有值的,所以el.vei是一个空对象,并且invokers也没有值,那么就不存在缓存了方法名。当进入到下一步的时候要判断传入的时候是空,这样就可以解绑对应的方法。当有值的时候,就进入到了上面说的,绑定一个自定义事件。这样el.vei中就有了一个{onClick:(e)=>invoker.value()}。如果你这时候绑定的是一个a方法那么就会是这样
{onClick:(e)=>a()}
。这样当你要绑定成b方法的时候就变成了{onClick:(e)=>b()}
。
这里并未细致考虑绑定多方法的问题。vue3是通过数组存储来完成。传入自定义属性
简单点就是有这个自定义属性就添加,没有值就移除它。
结尾
平常在编写的时候用的都是
render
或h
这样的函数,来渲染虚拟dom,而不是像文章开头一样,编写很多的api。那么为了 这样的操作,vue3d都是交由runtime-core
来操作。
也就是说runtime-dom的index.ts改成新建的runtime-core就有h.ts和renderer.ts的函数。
index.ts
h.ts
renderer.ts