zoukankan      html  css  js  c++  java
  • 理解Vue中的diff算法原理

    关于真实的DOM

    重绘和重排

    ** 重绘不一定需要重排,重排必然会导致重绘**
    重排: 渲染树需要重新计算

    1. 页面渲染初始化
    2. 元素的尺寸改变(盒模型的几何属性,如: 外边距、内边距、边框宽度、宽、高)
    3. 添加、删除可见Dom
    4. 元素的位置改变
    5. 浏览器窗口尺寸的改变
    6. 获取某些属性时,如:offsetTop,offsetLeft,offsetWidth,offsetHeight,scrollTop,scrollLeft
      注意:
    • 如果在body最前面插入一个元素,会导致整个文档的重新渲染。但是在最后插入一个元素,则不会影响到当前的元素。

    重绘:
    当页面中元素样式的改变并不影响它在文档流中的位置(如:color, background-color, visibility等),浏览器会将新样式赋予给元素并重新绘制。

    优化策略:

    1. 将多次改变样式属性的操作合并成一次操作,尽量使用操作class
    2. 将多次重排的元素设置为absolute或fixed,脱离文档流,这样不会影响到其他元素. 如:有动画效果的元素
    3. 在内存中多次操作节点,完成后添加到文档中去。如:渲染列表,可以在内存中构建整个列表的html片段,在一次性的添加到文档中去,而不是循环添加每一行
    4. 由于display属性为none的元素不在渲染树中,对隐藏的元素操作不会引发其它元素的重排。如果要对一个元素进行复杂操作时,可以先隐藏它,操作完成之后再显示,这样只会在显示和隐藏的时候触发两次重排
    5. 在需要经常取的,引起浏览器重排的属性值时,要缓存到变量
    6. 图片载入的时候设置宽高

    虚拟DOM

    指的是用 JS 对象的形式,来模拟页面上的DOM嵌套关系.

    // 虚拟DOM
    createElement(
    	// { String | Object | Function }
    	// HTML标签名、组件选项对象、或者async函数,必填
    	'div',
    	// { object } 对应的数据对象,可选
    	{
    		attrs: {
    			id: 'app'
    		}
    	},
    	// { string | Array }
    	// 字节虚拟节点,也可以使用字符串来生成“文本虚拟节点” , 可选
    	[
    		'你好,虚拟DOM',
    		createElement("h1", "新闻头条"),
    		createElement(MyComponent, {
    			props: {
    				total: 100
    			}
    		})
    	]
    )
    

    模板转换成视图的整个过程

    • Vue.js 通过编译将模板转换哼渲染函数(render),执行渲染函数就可以得到一个虚拟DOM
    • 在对模型进行操作的时候,会操作对应的Dep中的Watcher对象,Watcher对象会调用对应的update来修改视图。这个过程主要是将新旧虚拟DOM进行差异对比。
      简单点讲:在vue的实现上,Vue将模板编译成虚拟DOM渲染函数。结合Vue自定义的响应系统,在状态改变时,Vue能够智能地计算出重新渲染组件的最小代价并应用到DOM操作上。

    流程:
    模板 -> 渲染函数 -> 得到Vnode -> Vnode和oldVnode进行对比 -> 渲染成真实的DOM

    渲染函数: 是用来生成虚拟DOM的。Vue推荐使用模板来构建我们的应用界面,在实现中Vue布局模板编译成渲染函数。
    vnode虚拟节点: 它可以代表一个真实的DOM节点,通过createElement方法将vnode渲染成DOM节点;虚拟节点可以理解成节点描述对象,描述了应该怎样取创建真实的DOM节点
    patch: 将vnode渲染成真实的DOM,patch过程是对比新旧虚拟节点之间有那些不同,然后根据对比结果找出需要更新的节点进行更新。实际作用是在现有DOM上进行打补丁的形式来实现更新视图的目的。

    diff算法

    diff过程的整体策略:同层比较,深度优先,就近复用

    patch方法:用于 比较 新旧节点的不同,然后更新的函数

    • 没有旧节点,说明是页面刚开始初始化的时候 ,此时,根本不需要对比,直接新建
    • 新旧虚拟Dom树的根节点完全一样才会调用patchVnode方法进行打补丁
    • 新旧虚拟Dom树的根节点不一样,直接创建新节点,删除旧节点

    patchVnode方法:

    • 如果是非文本节点或注释节点
      • 如果都有子节点,并且不完全一直,则调用 updateChildren 进行diff
      • 如果只有新节点有子节点,旧节点没有,不用比较,直接全部新建到父节点
      • 如果新节点没有子节点,老节点有子节点,直接删除
      • 如果老节点是文本节点,则情况内容
    • 如果新旧文本节点不相等时,更新新节点的文本内容

    子节点不一致时,调用updateChildren方法

    • 头头相同
    • 尾尾相同
    • 旧头新尾
    • 旧尾新头
    • 如果都不匹配,则尝试查找具有相同key的节点
      • 如果没有找到,说明是新节点,直接创建
      • 如果找到了,比较两个具有相同的key的新节点是不是同一个节点
      • 不设key,只会进行头尾两端的相互比较,设key后,除了头尾两端的比较外,还会从用key生成的对象进行对比
      • 如果key相同,但节点不同,则创建一个新的节点

    设置新旧节点的头尾指针,新旧头尾指针进行比较,循环向中间靠拢,根据情况调用patchVnode进行patch重复流程,调用

    参考网站
    https://juejin.cn/post/6881907432541552648#heading-1
    https://www.infoq.cn/article/udlcpkh4iqb0cr5wgy7f

  • 相关阅读:
    hmac
    struct模块-黏包的解决方法
    PHPCMS快速建站系列
    Notepad++搜索中的正则应用
    用var 变量=函数名 方式调用函数时如何传值的问题
    ThInkPHP中的常量
    css cursor 的可选值(鼠标的各种样式)
    JS实现用键盘控制DIV上下左右+放大缩小与变色
    PHP定义数组常量
    FormData实现文件上传实例
  • 原文地址:https://www.cnblogs.com/yuxi2018/p/14593825.html
Copyright © 2011-2022 走看看