共计 3958 个字符,预计需要花费 10 分钟才能阅读完成。
V-for 就地复用原理
举个🌰:
<div v-for="(item,index) in items">
<input />
<button @click="del(index)">delthis</button>
{{item.message}}
</div>
JS 局部
//data 外面的 items
items: [{ 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 性能差”,这是因为 ——
- DOM 引擎 、JS 引擎 互相独立,但又工作在同一线程(主线程),因而 JS 代码调用 DOM API 时必须 挂起 JS 引擎、激活 DOM 引擎,实现后再转换到 JS 引擎
- 引擎间切换的代价 会迅速积攒
- 强制 重排 的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 算法的两个外围:
- 两个雷同的组件产生相似的 DOM 构造,不同的组件产生不同的 DOM 构造。
- 同一层级的一组节点,他们能够通过惟一的 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 算法中的概念