你怎么理解vue中的diff算法?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了你怎么理解vue中的diff算法?相关的知识,希望对你有一定的参考价值。

参考技术A diff算法是虚拟 DOM 的必然产物,通过新旧虚拟 DOM 对比,将变化的地方更新在真实 DOM 上,另外也需要 diff 高效的执行对比过程,从而降低时间复杂度
vue2中为了降低 watcher 力度,每个组件只有一个 watcher 与之对应,只有引入 diff 才能精确的找到发生变化的地方
diff 之所以发生变化,是引入组件数据的变化调用了 set 方法,导致 watcher能够监控到数据的变化,导致其触发 updateComponent 中 pathVnode方法,在 pathVnode 函数中对对比上一次渲染结果和新渲染结果.两个节比较的过程就是发生diff的过程.整个更新过程叫做 path 过程,也叫打补丁的过程
diff 过程整体遵从深度优先,同层比较的策略;两个节点之间比较会根据它们是否拥有子节点或者文本节点做不同的操作.比较两组节点是算法的重点.首先假设头尾节点相同做 4 次比较尝试,如果这 4 中方式都没有找到,就按照通用方式遍历查找.查找结束在按照情况处理剩下的节点;借助 key,通常可以精确的找到相同的节点,因此整个 path 过程是非常高效的

vue面试题理解12-11

1、diff算法

diff全称是difference 中文就是差异的意思,diff算法顾名思义就是用来找到差异点 这就需要我们要有一个本体和一个参考物,用来进行对比,这就是diff算法的本质
比较本体和参照物这种方法就是diff 所以 说我们有多种diff方法
多种diff方法就是称为diff算法的实现逻辑
在前端中 diff算法的本体和参照物都是虚拟的dom

虚拟dom: 本质就是一个对象 改对象描述了一个ui节点所对应的一些必要信息
常用的方法是把真实dom虚拟成一个对象:
注:一般是用对象,也可以使用其他方式来描述
描述一个真实DOM的东西就是虚拟dom

为什么要使用diff算法

虚拟DOM存在的意义:

我们在改变真实dom的结构的时候一次一次改变的花销是比较大的 如果使用虚拟dom先演示改变 最后在统一进行一次性的改变,这样的极大的减少回流和重绘,diff只用更新对应的节点 不用更新全部节点 真实dom需要描述的节点太多了,

使用虚拟dom时更新流程:
diff去比较虚拟dom–找出差异点[需要更新的一个虚拟dom]----真实dom–render
真实dom:
diff比较真实dom-找出差异点【需要更新的真实don】–render

前端的diff算法其实就是在比较虚拟dom的差异,或者说虚拟dom树的差异,这就是整个diff算法的一个概念

手写vue中 diff算法

diff算法在vue中的逻辑书写在一个命为patch的函数中,改函数承担着将新旧虚拟dom进行对比并应用差异点的职责,patch在计算机科学里有打补丁的意思,VUE团队把整个vue app定义为一个程序,而每一次的找不同以及将不同应用到真实dom中。称之为打补丁

patch的作用是找出差异点并且应用到真实的dom上去
1、如果两个都是文本节点 就看文本值是否相同,如果不同则直接应用新的文本值(这是最不消耗性能的)
2、如果oldNode存在 newNode没有了 那代表需要卸载,直接移除对应的dom。(会带来一定的性能消耗,这是没办法的)
3、如果newNode有,oldNode没有,那就是新增,需要直接新增dom(这也没办法改变)
4、如果两个节点都不为空 而且还是可以复用的同一个节点,那就直接去比较子元素,这种情况下绝对不可能是文本节点。两个人tag。key都一直的话就没有任何比较的意义了。
5、如果两个节点完全不同 那也是以新的vnode为准,旧的直接不要

2、vue组件化理解

总体回答思路:组件化定义,优点,使用场景和注意事项展开成熟 同时说一些vue组件化的一些特点

先上总结:

1、组件是独立和可复用的代码组织单元。组件系统是vue核心特性之一,它使开发者使用小型,独立和通常可复用的组件构建大型应用;
2、组件化开发能大幅度提高应用开发效率,测试性 复用性等
3、组件化按照分类有:页面组件,业务组件,通用组件
4、vue的组件是基于配置的,我们一般编写的是组件的配置而非组件,框架后续会生成其构造函数,他们基于VueComponent。扩展与vue
5、vue中常用的组件化技术有:属性prop。自定义事件,插槽等,主要用于组件通信,扩展等
6、组件应该是高内聚,低耦合的
7.遵循单向数据流的原则

定义:

把一些用户在程序中独立的功能和模块单独提出来,切分成更小的块,这些块有单独的逻辑和功能,能够实现更好的复用,
全局定义组件:

在源码中使用的是extend方法 传入的是当前的自定义组件的配置选项
首先1、创建vueComponent的类/2、继承于vue的子类,3、选项合并,把vue已经存在的组件和当前的组件进行一些合并 会有一些校验;’

单文件组件定义:(用的比较多)

做的事情:vue-loader会编译template为render渲染函数
style 里面就用cssloader等处理成真正的css文件
最终达到的目标是导入一个js对象,平时写的vue最后导入js对象

优缺点

维护性 测试性 复用性
源码:在mountComponent里面 组件与watcher里面有一个一一对应的方式,
这个方法是组件的实例,在执行¥mount里面函数的时候,都会有一个watcher,组件中数据发生变化,只会调用这个组件中的数据和内容,可以减少渲染函数,打补丁就减少了,提取渲染比较多的组件,优化了性能;

组件实现

两点:

构造函数 extend.js中
实例化及挂载 createElm()
全局的组件是立即执行,甚至在当前实例化之前就执行了; 局部组件在后续某个运行的时候才去执行(没有全局声明),那个这个时候是什么时候呢。虚拟dom应该欧应该根节点,肯定不是一个组件,应该是vue的根实例,那他就不可能是自定义组件。vue实例化挂载之后,会有一次初步渲染的过程,一定会创建一个根实例,就会对应一个真实的dom,这个方法是 createElm()
creatComponent:
首先看当前虚拟dom的data中是否会定义一个虚拟钩子
创建组件实例(子组件的初始化),实例化的过程是自上而下的,
创建完成并挂载$mount挂载

3、vue设计原则的理解

定义和特点:渐进式javaScript框架
易用,灵活和高效

渐进式javascript框架:
vue被设计为可以自底向上逐层应用。vue的核心库值关注视图层,不仅易于上手,HIA便于第三方库或者既有项目整合,另一方面,当与现代的工具链以及各种支持类库结合使用时
vue能完全为复杂的单页面提供驱动

4、vue要求组件模板只有一个根元素

1、在实例化vue的时候 ,需要指定el属性来填写spa入口
2、在单文件组件template下的元素div,其实就是树状数据结构的根
3、diff算法要求的 源码,patch.js里patchVnode()

如果设置多个入口 vue不知道哪一个才是入口。
template标签:(h5新增)

1、隐藏性 该标签不会显示在页面任何地府,即便里面有多少内容 它永远都是隐藏的状态 设置了display:none
2、任意性:该标签可以写在任何地方,甚至是head body script里面
3、无效性:该标签里的任何Html内容都是无效的,不会起任何作用。只能在innerHTML来获取到里面的内容

比较新旧节点的时候先从根节点开始 根节点只能是一个 然后再遍历后面子节点

5、vue优化

1、路由懒加载
2、keep-alive缓存页面

3、使用v-show复用DOM

4、v-for遍历避免使用v-if
在计算属性中提前过滤这个数组

5、长列表性能优化

列表纯粹数据展示,不会有任何变化,就不需要做响应化

大数据长列表,可采用虚拟滚动 只渲染部分区域的内容
利用第三方的vue-virtual-scroller vue-virtual-scroll-list

6、事件的销毁
vue组件销毁的时候,会自动解绑它全部指令以及事件监听 ,但仅限于组件本身的事件,添加在组件上的事件是不会进行解绑的,需要手动去销毁;

7、图片懒加载
对于图片过多的页面,为了加速页面加载速度,多以很多时候我们需要将页面没有出现在可视区域内的图片先不做加载,等到滚动可视化后再销毁
vue-lazyload 插件

9、第三方插件按需加载

10、无状态组件标记为函数式组件
展示型组件,没有实例 所以耗费资源比较小,没有状态,只是展示数据

12、子组件分隔
切割独立的子树,单独分开,自己负责自己的组件,不需要全部页面重新渲染

13、变量的本地化(不频繁的使用this)

14、SSR 服务端渲染

6、vue3新特性

1.更快

虚拟DOM重写 写出影响性能的代码编译阶段就会提示
优化solot的生成
静态树的提升 静态属性的提升 (把静态树属性 标记起来,就不需要重新去渲染和比较了)

2、更小:通过摇树优化核心库体积
3、更容易维护: TypeScript+模块化
4、更加友好:跨平台,更容易与web android ios使用
5、更容易使用

改进支持TS支持,编译器提供强有力的类型检查和错误及警告
更好的调试支持
独立的响应化模块
CompositionAPI

虚拟DOM重写
期待更多编译时提示来减少运行时开销 使用更有效的代码来创建虚拟节点
组件快速路径+单个调用+子节点类型检测
跳过不必要的条件分支
JS引擎更容易优化

优化slots生成
vue3中可以单独重新渲染父级和子级
确保实例正确的跟踪依赖关系
避免不必要的父子组件重新渲染

静态树提升
使用静态树的提升 编译器能见够检测什么是静态的 然后将其提升起来 从而降低渲染成本
即使多次出现也能正常工作

静态属性提升
vue3打补丁的时候将跳过这些属性不会改变的节点

基于Proxy的数据响应式
vue2的响应式系统使用的是Object.defineProperty 的getter seteer。vue3将使用ES2015Proxy作为其观察机制,这将会带来变化:

组件化实例初始化的速度提高100%
使用Proxy节省以前一半的内存开销,加快速度,但是存在低浏览器版本大的不兼容
为了继续支持IE11 vue3将发布一个支持旧观察者和新Proxy版本的构建

高维护性
ts支持,许多包被解耦 更加模块化 在源码中发现 compiler-ssr
reactivity 响应式,模块 不引入vue也能用

7、watch和computed的区别

1、定义和语义的区别
watch是一个监听器 用来监听响应数据的变化 是一个对象 第一个参数是改变后的新值 第二个是改变和之前的旧值
computed是计算属性。通过其他的数据算出一个新的数据 好处是把新的数据缓存下来,调用缓存的数据 提高了可复用性;调用内部函数的时候不需要加小括号,

2、 功能区别:
watch更通用 computed派生功能很多都能实现 计算属性底层来自于watch

3、用法区别: computed更简单 更高效 优先使用
有些必须watch 比如值变化要和后端交互

4、使用场景:
watch需要在数据变化时执行异步或开销比较大的操作时使用,简单讲就是一条数据影响多条数据时候 比如:搜索数据
computed 对于任何复杂逻辑或一个数据属性在他所依赖的属性发生变化的时候 也要发生变化 就是一个属性受多个属性影响的时候,列如购物车商品结算

8、vue生命周期的理解的

vue实例从创建运行渲染销毁期间伴随着各种事件,这就是生命周期,钩子函数就是这些事件的别名
之前记的时候是四个阶段八个钩子函数 今天就详细看一下

实例创建
DOM渲染
数据更新
销毁实例
八个钩子函数:
beforeCreat 创建前
created 创建后
beforeMount渲染前
mounted渲染后
beforeUpdata 更新前
updated 更新后
beforeDestroy 销毁前
destroy 销毁后


以上是关于你怎么理解vue中的diff算法?的主要内容,如果未能解决你的问题,请参考以下文章

Vuejs351- 带你解析vue2.0的diff算法

从Vue2源码看diff算法

从Vue2源码看diff算法

阐述一下你对虚拟DOM和Dom-Diff理解?

web前端diff 算法深入一下?

React的diff算法详解