前端笔记:Vue源码解析之虚拟DOM及diff算法
本内容笔记来源于[尚硅谷]Vue源码解析之虚拟DOM和diff算法的视频讲解
第一节课: 虚拟DOM和diff算法
介绍
-
diff算法是实现最小量更新的,它可以同层比较,不可以跨层比较
-
虚拟DOM是将真是的DOM转变为js对象,js中有三个属性,sel表示selector选择器,data表示一些额外的数据,children数据表示数据,如图所示:
变为了
- webpack是可以提供模块化打包的工具,webpack-dev-server是一个提供代码热更新,可以在http://localhost:8080/ 网页上进程调试的
第二节课:snabbdom
简介
- snabbdom是瑞典语单词,单词原意为“速度”
- sanbbdom是著名的虚拟DOM库,是diff算法的鼻祖,Vue源码就是借鉴了snabbdom
- 官方Git:github.com/sn...
安装
- 在git上的snabbdom源码是用TypeScript写的,git上并不提供编译好的JavaScript版本
- 如果直接使用build出来的JavaScript版的snabbdom库,可以从npm上下载snabbdom,命令为 npm i -S snabbdom,
snabbdom测试环境搭建
①新建一个文件夹,在文件夹的访问路径上输入cmd,进入命令行窗口,然后创建身份证输入npm init,
在此文件夹生成了一个文件名为package.json的文件
②安装snabbdom,命令为npm i -S snabbdom; npm i表示安装依赖,-D表示项目开发依赖,-S表示项
目的真正依赖,装好之后,会生成node_modules,里面的src依然提供的是ts代码,build文件夹里面有
js代码,还有后缀为.d.ts文件,这个文件是ts在变成js的时候,需要额外留一个文件保存文件当中的
具体类型。
③snabbdom中的package.json里面的export引入别名,webpack版本5才支持export,所以要装版本5,
在输入npm i -D webpack@5 webpack-cli@3 webpack-dev-server@3 --force
④在编辑器(如vscode)中建立webpack.config.js,在webpack官网中找到模板,配置一下
const path = require('path');
module.exports = {
//入口
entry: './src/index.js',
//出口
output: {
//虚拟打包路径,就是说文件夹不会真正生成,而是在8080端口虚拟生成
publicPath:'xuni';
//打包出来的文件名
filename: 'bundle.js',
},
devServer:{
//端口号
port:8080,
//静态资源文件夹
contentBase:'www'
}
};
此时目录为
package.json内容为
src/index.js内容不写,www.html 中内容为
之后运行npm run dev
在浏览器输入127.0.0.1:8080看到结果
虚拟路径下
之后在www/index.html中引入代码src/index.js打包在了/xuni/bundle.js中
结果是
跑通
跑通snabbdom官方git首页的demo程序,即证明调试环境已经搭建成功
跑出来的结果是
至此环境搭建完成
第三节课笔记 虚拟DOM和h函数
虚拟DOM:用javaScript对象描述DOM的层次结构。DOM中的一切属性都在虚拟DOM中有对应的属性。
diff是发生在虚拟DOM上的
新虚拟DOM和老虚拟DOM进行diff(精细化比较),算出应该如何最小量更新,最后反映到真正的DOM上。
本次课研究的知识点
- 研究1:虚拟DOM如何被渲染函数(h函数)产生?我们要手写h函数
- 研究2:diff算法原理?我们要手写diff算法
- 研究3:虚拟DOM如何通过diff变为真正的DOM的事实上,虚拟DOM变为真正的DOM,是涵盖在diff算法里面的
h函数用来产生虚拟节点(vnode)
比如这样调用h函数:
将得到这样的虚拟节点:
它表示的真正的DOM节点:
一个虚拟节点能有哪些属性
了解h函数和diff算法
练习1
结果
练习2
结果
练习3
结果
第四节课
手写h函数
资源管理器目录
第五节课
感受diff算法心得
- key很重要。key是这个节点的唯一标识,告诉diff算法,在更改前后它们是同一个DOM节点。
- 只有是同一个虚拟节点,才能进行精细化比较,否则就是暴力删除旧的、插入新的。同一个虚拟节点:选择器相同并且key相同。
- 只进行同层比较,不会进行跨层比较。即使是同一片虚拟节点,但是跨层了,对不起,精细化比较不diff你,而是暴力删除旧的,然后插入新的。
第六节课
diff处理新旧节点不是同一个节点时
在node_modules中的snabbdom中的src中的init.ts中找到patch函数画出流程图
第七节课
手写第一次上树时
第八节课
手写递归创建子节点
oldVnode和newVnode的sel和key不同=>暴力删除新的,插入旧的功能已完成(将createElement.js中的页面里面的标杆pivot去掉,因为h函数的第三个参数是数组的时候需要递归,这个时候不需要用到pivot,他们是顺下来的,用appendChild上树)
第九节课
完整流程图
第十节课
手写新旧节点text不同的情况
第十一节课
尝试书写diff更新子节点(此方法不好)
第十二节课
四种命中查找
①新前与旧前
②新后与旧后
③新后与旧前(此种发生了,涉及移动节点,那么新前指向的节点,移动到旧后之后)
④新前与旧后(此种情况发生了,涉及移动节点,那么新前指向的节点,移动到旧前之前)
命中其中一种,就不会再进行命中判断了 如果都没有命中,就需要用循环来寻找了,移动到oldStartIdx(旧前)之前。
最后这个图有点小错误,旧节点那个E(也就是最后一个被绿色undefined挡上的)其实不用用挡上,这个时候是命中1(新前与旧前),蓝色框框的E也不对,没有这个
第十三节课
手写子节点更新策略
将patch.js中的一部分提取出来,封装为一个函数,取名叫patchVnode.js,这个函数是用来对比同一个虚拟节点的,如图
然后以diff算法的思想比较不同之处最小化更新
第十四节课
继续手写子节点更新策略
继续在updateChildren.js中更新了红色的小框框(有错误,会在第十五节课改进)
第十五节课
补充updateChild.js代码以及改正一些错误
第十六节课
小总结