Vue 添加响应式属性的正确姿式

原文连接: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 都已被劫持。

clipboard.png

应对方法是什么

最简单的方法是:直接在 data 写清楚,也就是页面用了什么属性都必须写上。例如对于 inputvalue.aaaa,就直接在 data 里面加上 aaaa 属性。

可是...想了想,这大概不算“动态”添加了吧 😂

使用 set

向响应式对象中添加一个属性,并确保这个新属性一样是响应式的,且触发视图更新。它必须用于向响应式对象上添加新属性,由于 Vue 没法探测普通的新增属性 (好比 this.myObject.newProperty = 'hi')

Vue.set 或者 Vue 实例的 $set 都是同样的,总之就是手动触发一次劫持,以后在更新的时候就能触发视图从新渲染啦!

不过,其实 set 在一种状况下会失效,这个后面会提到...

使用 forceUpdate

这个方法算是一种曲线救国吧。

若是你不须要双向绑定,在动态新增属性时你可使用 $forceUpdate()。这个函数的做用就如其名,强制更新从新渲染。

上面说过了,虽然你设置新数据没有通知页面从新渲染,不过数据终究是改了。因此你只须要强制更新视图,就能看到数据修改后的效果。

Vue 单向绑定

<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"
/>

进行两种操做:

  1. 先在 with v-model 框输入,后在 with @input 框输入
  2. 先在 with @input 框输入,后在 with v-model 框输入

操做一,一切正常;操做二,没法更新。这就证实坊间流传的 v-model 是 @input 和 :value 的语法糖这个说法至少放在如今确定是错的(其实我往下试了几个版本,这两个操做表现都是不一致的,以为很迷惑,可是这不是重点,先不纠结了)。

那么 v-model 到底作了什么

在 stackoverflow 上通过大佬指点,上面的状况其实很容易理解,形成这个区别的重点有两个:

  1. v-model 处理对象属性会自动触发 set -> 相关源码
  2. set 对已存在的属性并不会再次让他变为“响应式” -> 相关源码

因此对于操做一,v-model 帮你把数据 set 了,天然一切正常;操做二,@input 先把属性直接静态添加了,到了 v-model 的时候 set 不会再劫持已经存在的属性。

这就引出了一个须要注意的地方,如果先直接赋值,即便再用 set 也不能再劫持这个属性了,这个可怜弱小又的属性已经没法再变成响应式了。

参考连接

相关文章
相关标签/搜索