如何正确理解vue中的key详解


Posted in Javascript onNovember 02, 2019

就目前所了解的情况,key的作用有以下这些。

  • v-for遍历时,用id,uuid之类作为key,唯一标识节点加速虚拟DOM渲染
  • 响应式系统没有监听到的数据,用+new Date()生成的时间戳作为key,手动强制触发重新渲染

场景一大同小异司空见惯,场景二是下面这样的:

<div :key="rerender">
  <span>Hello Vue.js !</span>
  <complexComponent :propObj="propObj" :propArr="propArr" ></complexComponent>
</div>

refresh(){
  this.rerender = + new Date();
}

那么vue中key的相关知识点到底是怎样的呢?

  • 官方API知识点
  • 上面2个使用场景背后的原理是什么?
  • 除key外,还有其它强制更新DOM的方法吗?
  • 参考资料

官方API知识点

  • 在Vue.js中,key是6个特殊属性key, ref, is, slot, slot-scope, scope其中之一。
  • key的值可以是number,也可以是string。
  • key主要作用于Vue的virtual DOM算法,在diff new nodes list和old nodes list时,作为识别VNode的一个线索。
  • 如果不用key,Vue会用一种算法:最小化element的移动,并且会尝试尽最大程度在同适当的地方对相同类型的element,做patch或者reuse。
  • 如果使用了key,Vue会根据keys的顺序记录element,曾经拥有了key的element如果不再出现的话,会被直接remove或者destoryed。
  • 拥有同一个parent的children必须有unique keys。重复的key的导致render error。

最常用的用法一:v-for

<ul>
  <li v-for="item in items" :key="item.id">...</li>
</ul>

最常用的用法二:强制替换element或者component

  • 触发组件的lifecycle
  • 触发transition
<transition>
 <span :key="text">{{ text }}</span>
</transition>

text发生变化时,<span>会被replaced,而不会patched,因此transition会被触发。

我的理解:

text变化时,span的key发生了变化,也就是说曾经拥有了旧key的span不再出现了,当拥有新值的text作为key时,拥有了新key的span出现了,那么旧key span会被移除,旧transition也会移除,新key span触发渲染,新transition触发。

上面2个使用场景背后的原理是什么?

结合官方API的知识点,现在再来回顾文章开头提出的场景。

场景一:v-for遍历时,用id,uuid之类作为key,唯一标识节点加速虚拟DOM渲染

答案:

  • 如果不用key,Vue会用一种算法:最小化element的移动,并且会尝试尽最大程度在同适当的地方对相同类型的element,做patch或者reuse。
  • 如果使用了key,Vue会根据keys的顺序记录element,曾经拥有了key的element如果不再出现的话,会被直接remove或者destoryed。

场景二:响应式系统没有监听到的数据,用+new Date()生成的时间戳作为key,手动强制触发重新渲染

<div :key="rerender">
  <span>Hello Vue.js !</span>
  <complexComponent :propObj="propObj" :propArr="propArr" ></complexComponent>
</div>

refresh(){
  this.rerender = + new Date();
}

答案:

  • 如果使用了key,Vue会根据keys的顺序记录element,曾经拥有了key的element如果不再出现的话,会被直接remove或者destoryed。
  • refresh方法调用后,包含了span和complexComponent的div的key发生了变化,也就是说曾经拥有了旧key的div不再出现了,当拥有新值的rerender作为key时,拥有了新key的div出现了,那么旧key div会被移除,旧span和complexComponent也会移除,新key div触发渲染,新span,带着父组件新propObj和propArr的新complexComponent渲染。

思考:

  • 为什么要叫propObj和propArr?
  • 带着父组件新propObj和propArr的新complexComponent渲染。 为什么要加粗?

由于Vue.js的obj和arr存在无法检测到数据变化的情况,obj是属性的新增和删除(原因是新增和删除都没有触发setter,watcher未告诉外界更新),arr则是数组内元素重新赋值或者修改length属性(原因是没有使用改变数组本身的方法,没有触发数组原型链拦截器,watcher未告诉外界更新)。

所以!通过赋予新key的方式,移除旧key div,渲染新key div,propObj和propArr在complexComponent组件内会重新触发一次生命周期,做一次重新渲染。此时父组件的propObj和propArr js变量其实已经获取到新值了,只是没有触发DOM也好,VNode也好的重新渲染。需要通过刷新key去force update,说到forceUpdate,可以通过$forceUpdate()去手动强制更新DOM。

除key外,还有其它强制更新DOM的方法吗?

场景:父组件修改传递给子组件的数据,数组数据的更新没有按照this.$set去更新。该怎么办?

this.productImages.forEach((product) => {
 if (product.productId in this.productsState) {
  product.status = this.productsState[product.productId];
 }
});

不使用this.$set去赋值数据的不能rerender的原因是什么?

在Vue.js中,对Array的变化侦测是通过拦截原型的方式实现的。也就通过对push,pop,shift,unshift,splice,sort,reverse,fill,copyWithin去改变数组自身内容的方法做拦截,从而响应。而product.status = this.productsState[product.productId];没有触发任何改变数组自身的被监听的方法,因此不会rerender。

  • 刷新组件的key
  • $forceUpdate方法

刷新组件的key

1.这个key加在什么地方比较好?

加在this.productImages的父元素上就好。

若不涉及数据传递,也可以直接加在需要更新的element上。

2.用什么做key值?

现在是粗暴的+new Date()时间戳做key值的。

也可以用双向绑定的值作为key值,保证新旧key值不同就行。

3.key的原理是什么?

vue.js的虚拟DOM算法,在更新vNode时,需要从旧vNode列表中查找与新vNode节点相同的vNode进行更新,如果这个过程设置了属性key,过程就会快很多。

其他具体见上文。

$forceUpdate方法

只能在父组件调用这个方法,手动通知vue实例重新渲染。

// $forceUpdate源码
Vue.prototype.$forceUpdate = function () {
 const vm: Component = this
 if (vm._watcher) {
  vm._watcher.update()
 }
}
// update源码
/**
 * Subscriber interface.
 * Will be called when a dependency changes.
 */
update () {
 /* istanbul ignore else */
 if (this.lazy) {
  this.dirty = true
 } else if (this.sync) {
  this.run()
 } else {
  queueWatcher(this)
 }
}

1.$forceUpdate可以更新的原理分析

product.status = this.productsState[product.productId];以后,其实此时dep已经发生变化了,但是Vue.js数组响应式的实现由于是拦截原型链方法的方式,没有检测到这个变化,所以不会自动rerender,没有触发update。因此我们通过$forceUpdate的方式,调用包含dep的watcher上的update方法,从而做到rerender。

2.可以在子组件监听事件,父组件发送事件然后只刷新子组件吗?

不可以。

因为dep是父组件的watcher和dep,并不是子组件,是父组件的this.productImages没有被检测到并实时更新,并不是子组件的问题。

参考资料

https://vuejs.org/v2/api/#key
https://vuejs.org/v2/api/#vm-...
https://vuejs.org/v2/guide/co...

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
jQuery解决下拉框select设宽度时IE 6/7/8下option超出显示不全
May 27 Javascript
用javascript添加控件自定义属性解析
Nov 25 Javascript
jQuery打字效果实现方法(附demo源码下载)
Dec 18 Javascript
理解Angular数据双向绑定
Jan 10 Javascript
基于jQuery实现音乐播放试听列表
Apr 14 Javascript
BootStrap表单宽度设置方法
Mar 10 Javascript
基于jQuery实现瀑布流页面
Apr 11 jQuery
详解使用Vue Router导航钩子与Vuex来实现后退状态保存
Sep 11 Javascript
vue-cli之router基本使用方法详解
Oct 17 Javascript
关于在vue 中使用百度ueEditor编辑器的方法实例代码
Sep 14 Javascript
vue-i18n结合Element-ui的配置方法
May 20 Javascript
vite+vue3.0+ts+element-plus快速搭建项目的实现
Jun 24 Vue.js
Javascript 关于基本类型和引用类型的个人理解
Nov 01 #Javascript
javascript 关于赋值、浅拷贝、深拷贝的个人理解
Nov 01 #Javascript
vue iview的菜单组件Mune 点击不高亮的解决方案
Nov 01 #Javascript
解决vue项目刷新后,导航菜单高亮显示的位置不对问题
Nov 01 #Javascript
vue实现点击追加选中样式效果
Nov 01 #Javascript
vue 导航内容设置选中状态样式的例子
Nov 01 #Javascript
解决vue-router 二级导航默认选中某一选项的问题
Nov 01 #Javascript
You might like
php PDO实现的事务回滚示例
2017/03/23 PHP
js类中获取外部函数名的方法
2007/08/19 Javascript
Jquery Ajax学习实例4 向WebService发出请求,返回实体对象的异步调用
2010/03/16 Javascript
一个JavaScript递归实现反转数组字符串的实例
2014/10/14 Javascript
javascript实现简单的省市区三级联动
2015/05/14 Javascript
html+js实现简单的计算器代码(加减乘除)
2016/07/12 Javascript
微信小程序 本地数据存储实例详解
2017/04/13 Javascript
node.js(express)中使用Jcrop进行图片剪切上传功能
2017/04/21 Javascript
JS中使用gulp实现压缩文件及浏览器热加载功能
2017/07/12 Javascript
详解如何使用webpack在vue项目中写jsx语法
2017/11/08 Javascript
Vue瀑布流插件的使用示例
2018/09/19 Javascript
angular6的响应式表单的实现
2018/10/10 Javascript
小程序数据通信方法大全(推荐)
2019/04/15 Javascript
手写Vue弹窗Modal的实现代码
2019/09/11 Javascript
js实现超级玛丽小游戏
2020/03/18 Javascript
Vue 修改网站图标的方法
2020/12/31 Vue.js
[02:40]DOTA2英雄基础教程 巨牙海民
2013/12/23 DOTA
[01:21]2018DOTA2亚洲邀请赛4.5采访 打DOTA2也能有女朋友?
2018/04/06 DOTA
[00:38]TI珍贵瞬间系列(二):笑
2020/08/26 DOTA
python实现的用于搜索文件并进行内容替换的类实例
2015/06/28 Python
在Django的form中使用CSS进行设计的方法
2015/07/18 Python
对python中的for循环和range内置函数详解
2018/04/17 Python
PyQt5实现类似别踩白块游戏
2019/01/24 Python
在django admin中添加自定义视图的例子
2019/07/26 Python
Python基于BeautifulSoup和requests实现的爬虫功能示例
2019/08/02 Python
Django如何使用第三方服务发送电子邮件
2019/08/14 Python
如何给Python代码进行加密
2020/01/10 Python
win10下opencv-python特定版本手动安装与pip自动安装教程
2020/03/05 Python
如何实现更换Jupyter Notebook内核Python版本
2020/05/18 Python
mac系统下安装pycharm、永久激活、中文汉化详细教程
2020/11/24 Python
2分钟教你实现环形/扇形菜单(基础版)
2020/01/15 HTML / CSS
魔声耳机官方网站:Monster是世界第一品牌的高性能耳机
2016/10/26 全球购物
初中优秀教师事迹材料
2014/08/18 职场文书
2015年初中元旦晚会活动总结
2014/11/28 职场文书
Vue监视数据的原理详解
2022/02/24 Vue.js
Nginx如何配置多个服务域名解析共用80端口详解
2022/09/23 Servers