最近在项目中遇到了一个问题,不知道为什么,所以最后通过动手做demo实践、查文档的方式解决了,这里做一个总结。
例1
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>vue</title> <script src="/UploadFiles/2021-04-02/vue.js">这里的实现目的很明确 --- 我希望在点击li时先检测是否存在,当然是不存在的,所以就将值设置为1, 如果再次点击,就让数字累加。
但是出现的问题是: 点击之后数字并没有在view层更新,而通过console打印发现数据确实更新了,只是view层没有及时的检测到, 而我一直以来的想法就是: 既然vue实现的时数据双向绑定,那么在model层发生了变化之后为什么就没有在view层更新呢"htmlcode">
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>vue</title> <script src="/UploadFiles/2021-04-02/vue.js">这时,我再测试时就发现,这里的model层发生了变化时,view层就能及时、有效的得到更新。
而数组为什么不可以呢?
于是在文档上的一个不起眼的地方找到了下面的说明:
其中最重要的一句话就是 --- 如果对象是响应式的,确保属性被创建后也是响应式的,同时触发视图更新,这个方法主要用于避开Vue不能检测到属性被添加的限制。
那么什么情况下Vue是不能检测到属性被添加呢? 根据参考链接,我们在文档上看到了很好的说明 --- 深入响应式原理
首先,我们要了解Vue是如何实现数据的双向绑定的!
把一个普通 JavaScript 对象传给 Vue 实例的 data 选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter。Object.defineProperty 是仅 ES5 支持,且无法 shim 的特性,这也就是为什么 Vue 不支持 IE8 以及更低版本浏览器的原因。
知识补充:
访问器属性不包含数据值,他们包含一对getter函数和setter函数(这两个函数不是必须的)。在读取访问器属性时,会调用getter函数,这个函数负责返回有效的值;在写入访问器属性是,会调用setter函数并传入新值,这个函数负责决定如何处理数据。
访问器属性不能直接定义,必须是用Object.defineProperty()来定义。
下面是一个例子:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>vue</title> </head> <body> <script> var book={ _year:2004, edition:1 }; Object.defineProperty(book,"year",{ get:function(){ return this._year; }, set:function(newValue){ if(newValue>2004){ this._year=newValue; this.edition+=newValue-2004; } } }); console.log(book.year); // 2004 在读取访问器属性时会调用get函数 book.year=2005; // 在给访问器属性赋值时会调用set函数 console.log(book.edition); // 2 </script> </body> </html>这个例子应该可以很好的理解访问器属性了。
所以,当对象下的访问器属性值发生了改变之后(vue会将属性都转化为访问器属性,之前提到了), 那么就会调用set函数,这时vue就可以通过这个set函数来追踪变化,调用相关函数来实现view视图的更新。
每个组件实例都有相应的 watcher 实例对象,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的 setter 被调用时,会通知 watcher 重新计算,从而致使它关联的组件得以更新。
即在渲染的过程中就会调用对象属性的getter函数,然后getter函数通知wather对象将之声明为依赖,依赖之后,如果对象属性发生了变化,那么就会调用settter函数来通知watcher,watcher就会在重新渲染组件,以此来完成更新。
OK!既然知道了原理,我们就可以进一步了解为什么出现了之前数组的问题了!
变化检测问题
收到现代JavaScript浏览器的限制,其实主要是 Object.observe() 方法支持的不好,Vue不能检测到对象的添加或者删除。然而Vue在初始化实例时就对属性执行了setter/getter转化过程,所以属性必须开始就在对象上,这样才能让Vue转化它。
所以对于前面的例子就不能理解了 --- 数组中index都可以看做是属性,当我们添加属性并赋值时,Vue并不能检测到对象中属性的添加或者删除,但是其的确是添加或删除了,故我们可以通过console看到变化,所以就没有办法做到响应式; 而在第二个例子中,我们是在已有的属性的基础上进行修改的,这些属性是在最开始就被Vue初始化实例时执行了setter/getter的转化过程,所以说他们的修改是有效的,model的数据可以实时的在view层中得到相应。
补充知识: 什么是 Object.observe() ?
在介绍之前,不得不残忍的说,尽管这个方法可以在某些浏览器上运行,但事实是这个方法已经废弃!
概述: 此方法用于异步地监视一个对象的修改。当对象的属性被修改时,方法的回调函数会提供一个有序的修改流,然而这个接口已经从各大浏览器移除,可以使用通用的proxy 对象。
方法:
Object.observe(obj, callback[, acceptList])其中obj就是被监控的对象, callback是一个回调函数,其中的参数包括changes和acceptList,
changes一个数组,其中包含的每一个对象代表一个修改行为。每个修改行为的对象包含:
- name: 被修改的属性名称。
- object: 修改后该对象的值。
- type: 表示对该对象做了何种类型的修改,可能的值为"add", "update", or "delete"。
- oldValue: 对象修改前的值。该值只在"update"与"delete"有效。
acceptList在给定对象上给定回调中要监视的变化类型列表。如果省略, ["add", "update", "delete", "reconfigure", "setPrototype", "preventExtensions"] 将会被使用。
var obj = { foo: 0, bar: 1 }; Object.observe(obj, function(changes) { console.log(changes); }); obj.baz = 2; // [{name: 'baz', object: <obj>, type: 'add'}] obj.foo = 'hello'; // [{name: 'foo', object: <obj>, type: 'update', oldValue: 0}] delete obj.baz; // [{name: 'baz', object: <obj>, type: 'delete', oldValue: 2}]如上所示: 但是chrome也是不支持的,浏览器的兼容性如下:
参考文档: Object.ovserve()
推荐阅读文章: Object.observe() 引爆数据绑定革命
解决方法
使用 Vue.set(object, key, value) 方法将响应属性添加到嵌套的对象上。 还可以使用 vm.$set 实例方法,这也是全局 Vue.set 方法的别名。
例3
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>vue</title> <script src="/UploadFiles/2021-04-02/vue.js">这样,我们就可以实现最终的目的了!
第二部分
上面一部分是指在data下的数组,而如果是在store中的数组,一般可以这样:
[ADD_ONE] (state, index) { if ( typeof state.numbers[index] == "undefined") { Vue.set(state.numbers, index, 1) } else { Vue.set(state.numbers, index, ++state.numbers[index]) } }即使用 Vue.set() 的方式来改变、增加。
注意:这里是确定index的增加和减少,所以用 Vue.set() 的方式
减的方式如下:
[REMOVE_ONE] (state, index) { Vue.set(state.numbers, index, --state.numbers[index]); }第四部分
如果是在store的actions中我们需要对stroe中的数组进行填充,方法如下:
state内容:
kindnames: []Mutations内容:
[ADD_KIND_NAME] (state, name) { state.kindnames.push(name); }注意: 这里直接使用push的方式
当然,除了push,我们还可以shift等各种方式。
actions的内容:
commit(ADD_KIND_NAME, state.items[index++].name);这里,state.items[index++].name获取到的是一个一个的字符串。
最后,看到数据如下:
注:同样可以参考文档 --- 细节与最佳实践
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
华山资源网 Design By www.eoogi.com
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。
更新日志
- 中国武警男声合唱团《辉煌之声1天路》[DTS-WAV分轨]
- 紫薇《旧曲新韵》[320K/MP3][175.29MB]
- 紫薇《旧曲新韵》[FLAC/分轨][550.18MB]
- 周深《反深代词》[先听版][320K/MP3][72.71MB]
- 李佳薇.2024-会发光的【黑籁音乐】【FLAC分轨】
- 后弦.2012-很有爱【天浩盛世】【WAV+CUE】
- 林俊吉.2012-将你惜命命【美华】【WAV+CUE】
- 晓雅《分享》DTS-WAV
- 黑鸭子2008-飞歌[首版][WAV+CUE]
- 黄乙玲1989-水泼落地难收回[日本天龙版][WAV+CUE]
- 周深《反深代词》[先听版][FLAC/分轨][310.97MB]
- 姜育恒1984《什么时候·串起又散落》台湾复刻版[WAV+CUE][1G]
- 那英《如今》引进版[WAV+CUE][1G]
- 蔡幸娟.1991-真的让我爱你吗【飞碟】【WAV+CUE】
- 群星.2024-好团圆电视剧原声带【TME】【FLAC分轨】