V-for就地复用原理


举个:

<div v-for="(item,index) in items">  <input />  <button @click="del(index)">delthis</button>  {{item.message}}</div>

JS局部

//data外面的itemsitems: [    {  message: "1" },  {  message: "2" },  {  message: "3" },  {  message: "4" },],//methods中的del办法del(index) {  this.items.splice(index, 1); //依据传入的index删掉items中对应数据},

成果如下



能够发现:

  • 当删掉items中的第二个对象时,输入框中的值还是2--这意味着没有删除对应的第二个节点。这是因为vue采纳虚构DOM+diff算法导致的数据凌乱。
  • vue监听到items数组中少了个元素后,会更新虚构DOM,而后应用diff算法比拟新、旧DOM树,在这个过程中,因为要计算出实在DOM树的最小变更规模,因而会尽可能复用已有的节点(如果节点类型雷同)

    此处,咱们的需要当然是不复用节点,那该如何实现呢?

    :key解决v-for导致的数据凌乱

  • 在渲染列表时,为每个元素绑定举世无双的key,这样,vue在更新经v-for渲染过的列表时,因为key值不同,会认为是不同的节点类型,不采取复用。这样就防止了数据凌乱

为什么不能应用数组下标作为key:

  • 不能应用各元素的index作为key,因为当新增或删除列表中元素时,各项索引都会变,也就是说索引对应元素变了,失去了标识的唯一性

    申明式渲染


Vue 提供一套基于 HTML 的模板语法,容许开发者申明式地将实在 DOM 与 Vue 实例的数据绑定在一起

"申明式" 的意思就是: 只须要指出指标, 而不必关怀如何实现,将实现交由vue解决

虚构DOM


  • Vdom(virtual dom),能够看作是一个应用javascript模仿了DOM构造的树形构造

    • 其中Vnode节点对应实在DOM节点
  • Vdom树用于缓存实在DOM树的所有信息

    为什么要采纳虚构DOM?

所有为了性能

“间接操作 DOM 性能差”,这是因为 ——

  1. DOM 引擎JS 引擎互相独立,但又工作在同一线程(主线程),因而JS 代码调用DOM API时必须挂起 JS 引擎、激活 DOM 引擎,实现后再转换到 JS 引擎
  2. 引擎间切换的代价会迅速积攒
  3. 强制重排DOM API调用,哪怕只改变一个节点,也会引起整个DOM树重排从新计算布局从新绘制图像会引起更大的性能耗费

所以,升高引擎切换频率(缩小DOM操作次数)减小 DOM 变更规模才是DOM 性能优化的两个关键点。


虚构 DOM +diff算法是一种可选的解决方案

基本思路:“在 JS 中缓存必要数据,计算界面更新时的数据差别,只提交最终差集”。

  • 虚构dom只用于缓存,而
  • diff算法负责--

    • 计算出‘虚构dom和目前实在DOM之间的数据差别
    • 提交最终差集
    留神:“单纯VDOM是进步不了性能的,VDOM次要作用在于它的二次形象提供了一个diff/patch和batch commit(批量提交)的机会”

watcher的节流成果:借助watcher响应式原理,使数据异步更新(滞后更新),可能实现节流成果,在一段时间内,容许屡次更新虚构DOM,而后一次性patch到实在DOM树。像是应用精灵图以缩小申请次数那样,达到优化性能的目标。

vue在监听到数据变动后,会将依赖该数据的watcher退出微工作队列,因为微工作是异步的,因而所有同步更新数据的操作,都会及时地在微工作队列中的工作更新前触发watcher响应,换个说法:执行第一次变动后的每次变动都会更新watcher中的各项依赖。这样的话,在该微工作执行结束之前的这段时间,就相当于节流中的时延了

Vdom的Diff算法


diff算法的两个外围:

  1. 两个雷同的组件产生相似的DOM构造,不同的组件产生不同的DOM构造。
  2. 同一层级的一组节点,他们能够通过惟一的key进行辨别。

diff算法的复杂度

  • 比拟两棵虚构DOM树的差别是Virtual DOM算法最外围的局部,这也是所谓的 VirtualDOM的diff 算法。两个树的齐全的diff 算法是一个工夫复杂度为O(n^3)的问题。
  • 然而在前端当中,你很少会逾越层级地挪动DOM元素。所diff算法只会对同一个层级的元素进行比照。上面的div只会和同一层级的div比照,第二层级的只会跟第二层级比照。这样算法复杂度就能够达到O(n)

    比拟时是否复用的逻辑

当页面的数据发生变化时,Diff算法只会比拟同一层级的节点:
  • 如果节点类型不同,间接干掉旧的节点,创立并插入新的那个节点,不会再比拟这个节点当前的子节点了。
  • 如果节点类型雷同,则会间接复用该节点,从新设置该节点的属性,从而实现节点的更新。

    当某一层有很多雷同的节点时,也就是列表节点时,Diff算法的更新过程默认状况下也是遵循以上准则。

比方--咱们心愿能够在B和C之间加一个F

Diff算法默认执行起来是这样的:

  • 老的Vdom树的该层上有6个节点,新的Vdom树上有7个类型雷同的节点,那么就顺次复用实在DOM树该层上的对应的前6个节点,在最初再新建一个节点,赋予之前节点E的属性。
  • 即把C更新成F,D更新成C,E更新成D,最初再插入E,是不是很没有效率?

所以咱们须要应用key来给每个节点做一个惟一标识,这样vue会把他们当做是不同的节点,因而不会复用,diff算法会间接创立新的节点,并插入正确的地位

key的作用

  • key的作用次要是为了高效的更新虚构DOM。
  • 也可防止间接复用v-for进去的节点,防止数据凌乱
  • 另外vue中在应用雷同标签名元素的过渡切换时,也会应用到key属性,其目标也是为了让vue能够辨别它们,否则vue只会替换其外部属性而不会触发过渡成果。

patch到实在DOM

模仿实现

如何将vnode(右边)变成实在的DOM元素(左边)

实现如下:

let nodes = {  tag: "ul",  attrs: {    id: "list",  },  children: [    {      tag: "li",      attrs: {        class: "item",      },      children: ["Item 1"],    },  ],};//实现办法:递归遍历function createElement(vnode) {  var tag = vnode.tag;  var attrs = vnode.attrs || {};  var children = vnode.children || [];  if (!tag) {    return null;  }  var elem = document.createElement(tag);  var attrName;  for (attrName in attrs) {    if (attrs.hasOwnProperty(attrName)) {      elem.setAttribute(attrName, attrs[attrName]);    }  }  for (let i = 0; i < children.length; i++) {    let childVnode = children[i];    if (typeof childVnode === "object" ||      childVnode.constructor === Object    ) {      elem.appendChild(createElement(childVnode));    } else {      let text = document.createTextNode(childVnode);      elem.appendChild(text);      break;    }  }  return elem;}let elem = createElement(nodes);console.log(elem);

PS:

vue 在patch时,在一个update 办法外面调用createElment()办法,通过虚构节点创立实在的 DOM 并插入到它的父节点中;

相当于打补丁到实在DOM


最初,举个栗子梳理一下:

让 Vue 将name的数据和<p>标签绑定在一起:

<p>Hello {{ name }}</p>
让咱们梳理一下vue对这个节点p和数据所做的所有
  • Vue 会把这些模板编译成一个渲染函数render
  • 该函数被调用后会渲染并且返回一个虚构的 DOM 树. 这个 "树" 的职责就是形容以后视图应处的状态。
  • 之后再通过一个Patch 函数,计算和旧虚构dom树的差集,并通过打补丁的形式将差集中的虚构节点更新到实在 DOM树。
  • 在整个过程中, Vue 借助数据劫持和订阅者模式实现监听状态、依赖收集、依赖追踪告诉变动等。 会侦测在渲染过程中所依赖到的数据起源,以实现双向绑定,自动更新状态。

参考:

理解一下v-for原理

Vue2.0 v-for 中 :key 到底有什么用?

Vue--patch | 学Vue 看这个就够了

https://www.zhihu.com/question/324992717/answer/690011952

vue考点 —— Diff算法

既然用 virtual dom 能够进步性能,为什么浏览器不间接自带这个性能呢?--水歌 | 知乎

你不晓得的React 和 Vue 的20个区别【面试必备】

vue diff算法 patch

diff算法中的概念