v-for
的 key 必须是唯一值,才可以让 Vue 在更新 v-for
所产生的列表时,能准确更新节点。相反,如果使用 index 作为 key,或者不绑定 key,Vue 就会以该节点的位置作为 key,有机会因为错误套用之前渲染过的节点而造成错误。
另外,Vue 官方不建议同时使用 v-for
和 v-if
,因为两者在执行上的优次不同,而且有机会浪费渲染效能。
以下会再作详情解说。
关於 Vue 更新 v-for
的所产生的画面,有几个重点:
v-for
渲染的元素,并非移动 DOM 来完成。因为「重用节点」、「只更新需要更新的节点」这两个优势,所以很多人才说:绑 key 可以提升 v-for 的渲染效能。Vue 官方文件提到,如果没绑 key,就会用最小移动并且尽量原地修改的手法来更新资料。
但接下来的例子,会发现其实不论你是没有绑 key,还是只绑 index 当作 key。一样会出现同样问题。这次我以绑定 index 作为示范。例子是参考了这里的讨论再作调整。
先分享完整程序码:
https://codesandbox.io/s/todo-list-bang-index-zuo-wei-key-sfs9u?file=/src/App.vue
情况是:
对我们来说,待办列表由这样:
变成了:
但 Vue 是使用遍历来检查每个节点。因此会做以下的事:
最可怕的是,虽然第二个节点的资料(Watch Netflix)被勾选,但它的 completed 其实是 false:
再沿用以上情况,最後我取消勾选在完成区域里的 "Buy dinner",会变成以下结果:
"Buy dinner" 仍然是勾选状态。原理同上,因为对 Vue 来说,第二个节点就是有勾选状态,你不过是换掉了文字内容,但第二个节点是存在的,并没有被移除,所以会重用第二个节点的画面。
要避免以上情况,就不能利用 index 来记录一个节点的画面状态,而是使用唯一值。当我们使用唯一值,Vue 官方文件说明会做以下的事:
key 特殊 attribute 主要用做 Vue 的虚拟 DOM 算法的提示,以在比对新旧节点组时辨识 VNodes。...使用 key 时,它会基于 key 的顺序变化重新排列元素,并且那些使用了已经不存在的 key 的元素将会被移除/销毁。
关於虚拟 DOM 的意思,可参考此系列的文章:
什麽是 Virtual DOM?Vue 如何利用 Virtual DOM?
换言之,如果现在我以 id 当作唯一值,并成为每个节点的 key。从Vue 的文档中,我们知道当 Vue 遍历节点时,会做两件事:
回到例子,这次问题就被解决了。
修改後的程序码:
https://codesandbox.io/s/todo-list-bang-id-zuo-wei-key-pfy1n?file=/src/App.vue
主要修改部分:
<li v-for="todo in incompletedList" :key="todo.id">
<input type="checkbox" v-model="todo.completed" :id="todo.id" />
<label :for="todo.id">{{ todo.title }}</label>
</li>
当我勾选了 "Buy dinner":
Vue 就会做以下的事:
id: 1
的新旧节点,判断是否有变化,有的话就重新渲染作更新。即使 "Buy dinner" 被勾选了,勾选的画面只会套用在 id: 2
的节点上。因此 "Watch Netflix" 不会呈现勾选状态,因为它的 id 是 3。id: 2
的节点销毁,并建立 id: 3
的节点。Vue 官方文件有提到,不建议同时使用v-if
和 v-for
。
注意:
v-for
优先於 v-if
v-if
优先於 v-for
因为其中一个语法会优先被执行,加上效能的问题。所以 Vue 官方不建议同时使用。
以 Vue 2 为例,意思是先跑 v-for
呈现每笔资料,之後每笔资料都会套用 v-if
。如果有 1000 笔资料,只有 1 笔是因为 v-if
而不会被渲染,那麽就浪费了 999 个 v-if
的计算。
像以下官方例子:
<li v-for="todo in todos" v-if="!todo.isComplete">
{{ todo }}
</li>
每个 todo
都会被绑上 v-if
,并计算是否要作显示。
Vue 官方 style guide 建议两种常用做法:
computed
把资料处理好,再跑 v-for
渲染已处理好的资料。v-if
移到外层,内层用 v-for
。第一种做法,之前的例子就有用到:
<ul>
<li v-for="todo in completedList" :key="todo.id">
<input type="checkbox" v-model="todo.completed" />
{{ todo.title }}
</li>
</ul>
data() {
return {
todos: [
{
id: "1",
title: "Write blog",
completed: false,
},
...
],
};
},
computed: {
completedList() {
return this.todos.filter((item) => item.completed);
},
},
第二种做法在以上的例子就不能用,因为每个事项都有自己的完成状态,没法统一。
v-for
渲染的画面,Vue 使用「原地更新」的方法来作更新,尽量重用已经渲染过的节点。v-for
需要绑定 key, 因为 Vue 会判断该节点内容是否有变,以及会纪录所有 key 的顺序并作出更新。v-if
和 v-for
,因为两者执行时有优次之分,而且有机会浪费渲染效能。vue中v-if和v-for不建议同时使用的坑
Vue2.0 v-for 中 :key 到底有什么用?
重新认识 Vue - 1-6 条件判断与列表渲染
<<: Day26:Flow 的运算子 - buffer()
当我们在 GoDaddy 上申请好网域之後,就接着要把 GoDaddy 上的 DNS 转址到我们的服...
IT business is one of the most famous in the busin...
没有答案 也抓不住时间 独自成长 希望谁能发现 窗外阳光 季节又转一圈 遥远记忆 回不到的雨天 万...
在昨天建置vue-cli插件时我们有新增vuex和vue-router,所以今天要先来介绍vue-r...
Service是应用程序元件之一,它用於背景处理与使用者介面无关的长时间任务,即便切换到其他应用程序...