原文连接:https://ssshooter.com/2019-09...html
默认此文读者明白简单的 Vue 底层原理,对此陌生的读者能够先看:vue
此文使用的 Vue 版本是 2.0+,在线例子看这里,下面顺便也把关键代码贴出来。react
<template> <div class="hello"> <button @click="inputvalue.aaaa = 'aaaa is here'">show aaaa</button> <button @click="$forceUpdate()">forceupdate</button> {{inputvalue.aaaa}} <br /> cccc {{inputvalue.cccc}} <input v-model="inputvalue.cccc" placeholder="with v-model" /> <input @input="inputvalue.cccc = $event.target.value" :value="inputvalue.cccc" placeholder="with @input" /> <br /> bbbb {{inputvalue.bbbb}} <input v-model="inputvalue.bbbb" placeholder="with v-model" /> <input @input="inputvalue.bbbb = $event.target.value" :value="inputvalue.bbbb" placeholder="with @input" /> </div> </template> <script> export default { data() { return { inputvalue: { bbbb: '', }, } }, } </script>
最近的项目大量接触到动态新增的数据,以为必需要搞清楚到底何时 vue 会让视图更新,视图修改数据又会不会反映到数据模型。git
因而写了简单几个例子做为对比,结合一年前研究了一下可是如今忘得差很少的 Vue 原理知识,解决了这么个问题 ——github
什么状况下动态添加对象属性是安全操做(换句话说就是能够保证数据是响应式的)?api
首先解释例子中 inputvalue.aaaa
不显示的问题。数组
这要从 Vue 的响应式原理提及。在初始化的时候 Vue 会把 data 的数据递归扫描一遍,设置 setter 和 getter。安全
getter 的做用是在数据被读取时记下当前的调用者,这个调用者也就是这个数据的“订阅者”。ssh
若视图使用了某个数据,处理页面时就会调用该数据,成为该数据的一个订阅者。函数
setter 的做用是在数据被赋值时,会提醒他的订阅者该数据已更新,而后订阅者就知道要运行对应的更新操做,例如视图更新、watch 函数。
设置 getter,setter 常被称为劫持,感受也挺形象的,下面就简单用劫持指代这个行为。
既然在初始化时数据才被劫持,那么你忽然的定义 this.inputvalue.aaaa = 'aaaa is here'
显然会让 Vue 猝不及防。这个属性即便有订阅者,可是由于没有走到“劫持”这一步,因此这个属性根本意识不到他有订阅者。
其实把数据打印出来能够简单地断定这个数据是否已经被劫持。以下图,bb 没有被劫持,aa、cc 都已被劫持。
最简单的方法是:直接在 data 写清楚,也就是页面用了什么属性都必须写上。例如对于 inputvalue.aaaa
,就直接在 data 里面加上 aaaa 属性。
可是...想了想,这大概不算“动态”添加了吧 😂
向响应式对象中添加一个属性,并确保这个新属性一样是响应式的,且触发视图更新。它必须用于向响应式对象上添加新属性,由于 Vue 没法探测普通的新增属性 (好比 this.myObject.newProperty = 'hi')
Vue.set
或者 Vue 实例的 $set
都是同样的,总之就是手动触发一次劫持,以后在更新的时候就能触发视图从新渲染啦!
不过,其实 set 在一种状况下会失效,这个后面会提到...
这个方法算是一种曲线救国吧。
若是你不须要双向绑定,在动态新增属性时你可使用 $forceUpdate()
。这个函数的做用就如其名,强制更新从新渲染。
上面说过了,虽然你设置新数据没有通知页面从新渲染,不过数据终究是改了。因此你只须要强制更新视图,就能看到数据修改后的效果。
<input :value="myValue.property" @input="myValue.property = $event.target.value" />
你可能从未听过 Vue 单向绑定,可是这样作也算是一个单向绑定了 😂
当你的输入确实地改变了 myValue.property
的值,可是不会触发任何关于 myValue.property
的更新。真的须要更新的时候 forceUpdate
就能够了。
若是数组里有对象,只要单个对象符合上面操做便可,没有特别须要注意的地方。
可是老调重弹,数组更新方法仍是须要注意,你能够经过整个数组从新赋值以及 push()、pop()、shift()、unshift()、splice()、sort()、reverse() 这几个通过包裹的方法触发更新。
对比上面 cccc
的两个输入框:
<input v-model="inputvalue.cccc" placeholder="with v-model" /> <input @input="inputvalue.cccc = $event.target.value" :value="inputvalue.cccc" placeholder="with @input" />
进行两种操做:
操做一,一切正常;操做二,没法更新。这就证实坊间流传的 v-model 是 @input 和 :value 的语法糖这个说法至少放在如今确定是错的(其实我往下试了几个版本,这两个操做表现都是不一致的,以为很迷惑,可是这不是重点,先不纠结了)。
在 stackoverflow 上通过大佬指点,上面的状况其实很容易理解,形成这个区别的重点有两个:
因此对于操做一,v-model 帮你把数据 set 了,天然一切正常;操做二,@input 先把属性直接静态添加了,到了 v-model 的时候 set 不会再劫持已经存在的属性。
这就引出了一个须要注意的地方,如果先直接赋值,即便再用 set 也不能再劫持这个属性了,这个可怜弱小又的属性已经没法再变成响应式了。