## vue的diff算法
参考回答:
>[info] 当组件创建和更新时,vue均会执行内部的update函数,该函数使用render函数生成的虚拟dom树,将新旧两树进行对比,找到差异点,最终更新到真实dom
> 对比差异的过程叫diff,vue在内部通过一个叫patch的函数完成该过程
> 在对比时,vue采用深度优先、同层比较的方式进行比对。
> 在判断两个节点是否相同时,vue是通过虚拟节点的key和tag来进行判断的
> 具体来说,首先对根节点进行对比,如果相同则将旧节点关联的真实dom的引用挂到新节点上,然后根据需要更新属性到真实dom,然后再对比其子节点数组;如果不相同,则按照新节点的信息递归创建所有真实dom,同时挂到对应虚拟节点上,然后移除掉旧的dom。
> 在对比其子节点数组时,vue对每个子节点数组使用了两个指针,分别指向头尾,然后不断向中间靠拢来进行对比,这样做的目的是尽量复用真实dom,尽量少的销毁和创建真实dom。如果发现相同,则进入和根节点一样的对比流程,如果发现不同,则移动真实dom到合适的位置。
> 这样一直递归的遍历下去,直到整棵树完成对比。
### 1. diff 的时机
当组件创建时,以及依赖的属性或数据变化时,会运行一个函数,该函数会做两件事:
- 运行`_render`生成一棵新的虚拟dom树(vnode tree)
- 运行`_update`,传入虚拟dom树的根节点,对新旧两棵树进行对比,最终完成对真实dom的更新
核心代码如下:
```js
// vue构造函数
function Vue(){
// ... 其他代码
var updateComponent = () => {
this._update(this._render())
}
new Watcher(updateComponent)
// ... 其他代码
}
```
`diff`就发生在`_update`函数的运行过程中
### 2. _update 函数在干什么
`_update`函数接收到一个`vnode`参数,这就是**新**生成的虚拟dom树
同时,`_update`函数通过当前组件的`_vnode`属性,拿到**旧**的虚拟dom树
`_update`函数首先会给组件的`_vnode`属性重新赋值,让它指向新树
![](https://img.kancloud.cn/1c/ae/1caeb7e2090f58715895ad1d33184dfb_1364x636.png)
然后会判断旧树是否存在:
- 不存在:说明这是第一次加载组件,于是通过内部的`patch`函数,直接遍历新树,为每个节点生成真实DOM,挂载到每个节点的`elm`属性上
![](https://img.kancloud.cn/42/2e/422e8c2c12f66b3c8e9cb856a7909b41_590x628.png)
- 存在:说明之前已经渲染过该组件,于是通过内部的`patch`函数,对新旧两棵树进行对比,以达到下面两个目标:
- 完成对所有真实dom的最小化处理
- 让新树的节点对应合适的真实dom
![](https://img.kancloud.cn/0b/df/0bdff335685b9fe7a1eb75bb8c65cb3e_1832x1074.png)
### 3. patch 函数的对比流程
**术语解释**:
1. 「**相同**」:是指两个虚拟节点的标签类型、`key`值均相同,但`input`元素还要看`type`属性
2. 「**新建元素**」:是指根据一个虚拟节点提供的信息,创建一个真实dom元素,同时挂载到虚拟节点的`elm`属性上
3. 「**销毁元素**」:是指:`vnode.elm.remove()`
4. 「**更新**」:是指对两个虚拟节点进行对比更新,它**仅发生**在两个虚拟节点「相同」的情况下。具体过程稍后描述。
5. 「**对比子节点**」:是指对两个虚拟节点的子节点进行对比,具体过程稍后描述
**详细流程:**
1. **根节点比较**
![](https://img.kancloud.cn/ff/1c/ff1cb4656b7010107cde345abbaf8537_1098x634.png)
`patch`函数首先对根节点进行比较
如果两个节点:
- 「相同」,进入**「更新」流程**
1. 将旧节点的真实dom赋值到新节点:`newVnode.elm = oldVnode.elm`
2. 对比新节点和旧节点的属性,有变化的更新到真实dom中
3. 当前两个节点处理完毕,开始**「对比子节点」**
- 不「相同」
1. 新节点**递归**「新建元素」
2. 旧节点「销毁元素」
2. **「对比子节点」**
在「对比子节点」时,vue一切的出发点,都是为了:
- 尽量啥也别做
- 不行的话,尽量仅改动元素属性
- 还不行的话,尽量移动元素,而不是删除和创建元素
- 还不行的话,删除和创建元素
- Svg
- 01.svgIcon组件封装
- 02.clipPathSprites小图标
- html5
- 01.属性-input
- 02.属性-contenteditable
- 03.属性-draggable
- 04.标签-语义化标签
- 05.标签-canvas
- 06.标签-svg
- 07.标签-video
- 11.API-requestAnimationFrame
- 12.API-Storage
- css3
- 01.selector
- 02.border
- 03.background
- 04.color-gradient
- 05.text
- 06.box
- 07.flex
- 08.transition
- 09.cubic-bezier
- 10.animation
- 11.step
- 12.transform-rotate
- 13.transform-scale
- 14.transform-skew
- 15.transform
- 16.matrix
- 17.性能优化
- 18.css数据类型
- demo-animation
- demo-step
- demo-transform
- JavaScript
- 异步加载JS
- 函数
- 函数基础
- 函数名
- 理解参数
- 默认参数值
- 参数扩展与收集
- 函数内部
- 函数属性与方法
- 函数表达式
- 递归
- 闭包
- 立即调用的函数表达式
- 私有变量
- 函数的应用
- JS基础
- JS语言概述
- 数据与数据类型
- 操作符
- 变量声明
- 通过变量使用原始值与引用值
- 确定类型
- 标识符查找
- 执行上下文与作用域
- 垃圾回收
- Date
- 基本引用类型
- 包装类型-Boolean
- 包装类型-Number
- 包装类型-String
- 单例内置对象-Global
- 单例内置对象-Math
- 模版字符串
- try-catch与错误对象
- 对象
- 理解对象
- 创建对象
- 原型模式
- 继承
- 类
- 符号
- 普通符号
- 共享符号
- 知名符号
- 解构
- 对象解构
- 数组解构
- 数组
- 数组概述
- 数组方法
- 定型数组
- Map和Set
- Map
- Set
- WeakMap
- WeakSet
- 迭代器和生成器
- 迭代器
- 生成器
- 迭代与扩展操作
- 代理与反射
- 代理
- 反射
- 代理模式
- 异步处理
- 事件循环
- 事件和回调函数的缺陷
- 异步处理的通用模型
- Promise的基本使用
- Promise的API
- Promise的连锁与合成
- Promise扩展
- 异步函数
- 异步函数策略
- 其他案例
- 网络请求与远程资源
- 跨源资源共享
- XMLHttpRequest对象
- FetchAPI-基本用法
- FetchAPI-常见请求模式
- FetchAPI-Headers
- FetchAPI-Request
- FetchAPI-Response
- FetchAPI-body混入
- 安全
- BOM
- 窗口
- 视口
- 导航与打开新窗口
- location对象
- 操作地址
- navigator对象
- screen对象
- history对象
- DOM
- 节点层级
- 节点层级概述
- Node类型
- Document类型
- Element类型
- Text类型
- Comment类型
- DocumentType类型
- DocumentFragment类型
- CDATASection类型
- Attr类型
- DOM编程
- 动态脚本
- 动态样式
- 操作表格
- 使用NodeList
- MutationObserver
- MutationObserver接口
- MutationObserverInit与观察范围
- 异步回调与记录队列
- 性能和内存与垃圾回收
- DOM扩展
- SelectorsAPI
- 元素遍历
- CSS类扩展
- 焦点管理
- HTMLDocument扩展
- 插入标记
- 滚动
- 专有扩展
- DOM2和DOM3
- DOM的演进
- 样式
- 元素尺寸
- 事件
- 事件流
- 事件处理程序
- 事件对象
- 事件类型
- 事件委托
- 删除事件处理程序
- 模拟事件
- 用户界面事件
- load事件
- unload事件
- resize事件
- scroll事件
- 设备事件
- orientationchange事件
- deviceorientation事件
- devicemotion事件
- 焦点事件
- 鼠标和滚轮事件
- 键盘与输入事件
- HTML5事件
- contextmenu事件
- beforeunload事件
- DOMContentLoaded事件
- readystatechange事件
- pageshow与pagehide事件
- hashchange事件
- 触摸及手势事件
- 触摸事件
- 手势事件
- vue2
- mysite项目
- 01.搭建工程
- 02.Icon组件
- 03.Pager组件
- 04.测试组件
- 05.Empty组件
- 06.ImageLoader组件
- 07.Contact组件
- 08.Menu组件
- 09.SiteAside组件
- 10.Layout组件
- 11.路由
- 12.弹出消息
- 13.获取远程数据
- 14.Home组件
- 15.vLoading指令
- 16.组件混入
- 17.组件递归
- 18.文章列表页
- 19.文章详情页-1
- 20.文章详情页-2
- 21.文章详情页-3
- 22.事件总线
- 23.文章详情页-4
- 24.文章详情页-5
- 25.图片懒加载
- 26.使用vuex
- 27.标题统一处理
- 28.关于我
- 29.项目&效果
- 30.留言板
- 31.打包结果分析
- 32.异步组件
- 33.无数据显示
- 34.404页面
- 35.vuex优化
- mysite项目后台
- 01.项目准备
- 01.核心概念
- 02.组件
- 03.插槽
- 04.动态组件
- 05.异步组件
- 06.计算属性
- 07.指令
- 08.事件处理
- 09.filter过滤器
- 10.组件生命周期
- 11.自定义指令
- 12.组件混入
- 13.$listeners
- 14.v-model
- 15.过渡和动画
- 16.keep-alive
- 17.插件
- 89.mini观察者
- 90.render
- 91.父子组件通信
- 92.虚拟dom详解
- 93.数据响应原理
- 94.diff
- 95.Vue项目优化
- 96.路由
- 97.数据共享
- 98.vuex案例
- 99.vue-cli
- vue3
- 01.搭建工程
- 02.vue3的重大变化
- 03.vite原理
- 04.效率的提升
- 05.API和数据响应式的变化
- 06.模板中的变化
- 07.组件中的变化
- 08.ReactivityApi
- 09.CompositionApi
- 10.共享数据
- 11.slot透传
- 面试题
- 网络
- js
- git
- 01.基本概览
- 02.git命令
- 03.远程分支
- 04.source tree
- 05.git部署pages
- 06.git工作流
- vscode
- 插件推荐
- 快捷键
- 正则表达式
- 正则基础
- 包管理工具
- npm
- 01.概述
- 02.包的安装
- 03.包配置
- 04.包的使用
- 05.语义版本
- 06.npm脚本
- 07.运行环境配置
- 08.其他npm命令
- 09.发布包
- yarn
- 01.yarn简介
- 02.yarn的核心命令
- 03.yarn的其他命令
- 其他包管理器
- 01.cnpm
- 02.nvm
- 03.pnpm
- 模块化
- 01.模块化发展史
- 02.CommonJS
- 03.AMD和CMD
- 04.ES6模块化
- webpack
- 核心功能
- 01.如何在浏览器端实现模块化
- 02.webpack安装与使用
- 03.模块化兼容性
- 04.编译结果分析
- 05.配置文件
- 06.devtool配置
- 07.编译过程
- 08.入口和出口
- 09.入口和出口的最佳实践
- 10.loader
- 11.plugin
- 12.区分环境
- loader-处理图片
- loader-处理样式
- plugin-添加文件列表
- 13.其他细节配置
- 计算机组成原理
- 系统总线
- 存储器
- TypeScript
- 在node中搭建ts开发环境
- 基本类型检查
- 类型别名
- 枚举
- 模块化
- 接口
- 类
- 泛型
- 类型兼容性
- 面向对象
- 装饰器
- 类型演算
- 声明文件
- TS常用配置
- 数据结构与算法
- 线性数据结构
- 排序
- 栈和队列
- 树
- 图
- 动态规划
- webApp
- 移动端基础
- 移动端适配
- 移动端事件
- 移动端手势库
- 在本地配置https证书
- h5调用本地摄像头
- h5重力加速度
- h5横竖屏切换
- h5方向变化事件
- h5获取地理信息
- 移动端常见问题
- nodejs
- nodejs-概述.md
- nodejs-全局对象
- nodejs-模块化
- nodejs-内置模块
- nodejs-文件
- nodejs-生命周期
- nodejs-组成原理
- 进程和线程
- cookie
- JWT
- CORS
- CSRF攻击
- XSS攻击
- websocket
- 缓存的基本原理
- 常见案例
- 接口文件下载xlsx
- _设置
- image