Vue源码解析之虚拟DOM及diff算法

217 阅读5分钟

前端笔记:Vue源码解析之虚拟DOM及diff算法

本内容笔记来源于[尚硅谷]Vue源码解析之虚拟DOM和diff算法的视频讲解

第一节课: 虚拟DOM和diff算法

介绍

  • diff算法是实现最小量更新的,它可以同层比较,不可以跨层比较

  • 虚拟DOM是将真是的DOM转变为js对象,js中有三个属性,sel表示selector选择器,data表示一些额外的数据,children数据表示数据,如图所示:

    微信图片_20220108220103.png 变为了

微信图片_20220108220120.png

  • 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'
  }
};

此时目录为

目录.png

package.json内容为

package.json.jpg

src/index.js内容不写,www.html 中内容为

图片.png 之后运行npm run dev

图片.png 在浏览器输入127.0.0.1:8080看到结果

图片.png 虚拟路径下

图片.png 之后在www/index.html中引入代码src/index.js打包在了/xuni/bundle.js中

图片.png

图片.png 结果是

图片.png

跑通

跑通snabbdom官方git首页的demo程序,即证明调试环境已经搭建成功

图片.png

图片.png

图片.png 跑出来的结果是

图片.png 至此环境搭建完成

第三节课笔记 虚拟DOM和h函数

 虚拟DOM:用javaScript对象描述DOM的层次结构。DOM中的一切属性都在虚拟DOM中有对应的属性。

图片.png 图片.png

diff是发生在虚拟DOM上的

新虚拟DOM和老虚拟DOM进行diff(精细化比较),算出应该如何最小量更新,最后反映到真正的DOM上。

图片.png

本次课研究的知识点

  • 研究1:虚拟DOM如何被渲染函数(h函数)产生?我们要手写h函数
  • 研究2:diff算法原理?我们要手写diff算法
  • 研究3:虚拟DOM如何通过diff变为真正的DOM的事实上,虚拟DOM变为真正的DOM,是涵盖在diff算法里面的

h函数用来产生虚拟节点(vnode)

比如这样调用h函数:

图片.png

将得到这样的虚拟节点:

图片.png

它表示的真正的DOM节点:

图片.png

一个虚拟节点能有哪些属性

图片.png 了解h函数和diff算法

练习1

图片.png 结果

图片.png

练习2

图片.png

结果

图片.png

练习3

图片.png

结果

图片.png

第四节课

手写h函数

资源管理器目录

图片.png

图片.png

图片.png

图片.png

图片.png

第五节课

感受diff算法心得

  • key很重要。key是这个节点的唯一标识,告诉diff算法,在更改前后它们是同一个DOM节点。
  • 只有是同一个虚拟节点,才能进行精细化比较,否则就是暴力删除旧的、插入新的。同一个虚拟节点:选择器相同并且key相同。
  • 只进行同层比较,不会进行跨层比较。即使是同一片虚拟节点,但是跨层了,对不起,精细化比较不diff你,而是暴力删除旧的,然后插入新的。

第六节课

diff处理新旧节点不是同一个节点时

在node_modules中的snabbdom中的src中的init.ts中找到patch函数画出流程图

图片.png

第七节课

手写第一次上树时

图片.png

图片.png

图片.png

图片.png

第八节课

手写递归创建子节点

oldVnode和newVnode的sel和key不同=>暴力删除新的,插入旧的功能已完成(将createElement.js中的页面里面的标杆pivot去掉,因为h函数的第三个参数是数组的时候需要递归,这个时候不需要用到pivot,他们是顺下来的,用appendChild上树) 图片.png

图片.png

图片.png

第九节课

完整流程图 图片.png

第十节课

手写新旧节点text不同的情况

图片.png

第十一节课

尝试书写diff更新子节点(此方法不好)

第十二节课

四种命中查找

 ①新前与旧前
 ②新后与旧后
 ③新后与旧前(此种发生了,涉及移动节点,那么新前指向的节点,移动到旧后之后)
 ④新前与旧后(此种情况发生了,涉及移动节点,那么新前指向的节点,移动到旧前之前)

命中其中一种,就不会再进行命中判断了 如果都没有命中,就需要用循环来寻找了,移动到oldStartIdx(旧前)之前。

图片.png

图片.png

图片.png

图片.png

图片.png 最后这个图有点小错误,旧节点那个E(也就是最后一个被绿色undefined挡上的)其实不用用挡上,这个时候是命中1(新前与旧前),蓝色框框的E也不对,没有这个

第十三节课

手写子节点更新策略

将patch.js中的一部分提取出来,封装为一个函数,取名叫patchVnode.js,这个函数是用来对比同一个虚拟节点的,如图

图片.png 然后以diff算法的思想比较不同之处最小化更新

图片.png

图片.png

第十四节课

继续手写子节点更新策略

继续在updateChildren.js中更新了红色的小框框(有错误,会在第十五节课改进) 图片.png

第十五节课

补充updateChild.js代码以及改正一些错误

图片.png

图片.png

图片.png

图片.png

第十六节课

小总结