||

简单但是很容易被忽略的Vue小Tips-动态绑定

平时在开发Vue应用过程中,会遇到很多看似很简单,但却能深刻体现Vue原理的一些小问题。例如,在对Vue的 data 数据动态赋值的过程中,可能会遇到无法双向绑定的问题。

问题

首先来描述一下这个问题。

其实这个问题很简单。我们平时在开发 Vue 应用时,很多时候我们并不确定 data 中的某个对象具体的数据结构,而是会通过接口的返回值,或者其他方式来获取,再将获取的对象绑定到 data 上,以便实现这个对象的响应式。

代码上来看,大约就是下面的例子:

<script>
export default {
  data() {
    return {
      formData: ''
    }
  },
  create() {
    this.init();
  }
  methods: {
    init() {
      callSomeApi().then(res => this.formData = res.data);
    }
  }
}
</script>

上面的代码经常遇到,开发者的本地,就是想让组件初始化的时候先调用一个接口,将接口返回值的data对象绑定到组件的data数据上,实现整个对象的响应式。

这段代码也运行正常,并无问题。本文要说的,是基于这个代码的一个引申问题,请继续看下面的代码:

{
  methods: {
    init() {
      callSomeApi().then(res => this.formData = res.data);
      this.setOtherData();
    },
    setOtherData() {
      callAnotherApi().then(res => this.formData.other = res.data);
    }
  }
}

这不是一段好的代码,但抛开代码好坏不谈,单看功能,这段代码是有问题的。

开发者本意,是希望 formData对象又两个接口的返回值拼装而成,然后整个 formData 对象实现响应式,但实际上,只有 init 接口中第一次赋值的部分是响应式的,后续的 formData.other 无法响应式。

这便是本文要介绍的问题。

归因

这个问题对很多不了解 Vue 响应式原理,以及 Vue2 中对数据动态绑定特性的开发者来说,一时摸不着头脑,明明 formData 中对象的各个字段都有值,但偏偏 other 字段及其后续的数据无法实现响应式。

其实问题很简单:

this.formData.other 访问的是 formData 的 getter 属性,而在 Vue 中,getter 是用来收集依赖的,不用来处理响应式。

所以,上面的代码只是把 other 字段挂在了 formData 上,other 字段本身并不是观察者,所以无法实现响应式。

解决

明白了原因,解决起来就比较简单了,只需要把 getter 访问变成 setter 访问就可以了。

我接触的开发者,使用了不够友好的方案,但总归是解决了,方案如下:

{
  methods: {
    init() {
      callSomeApi().then(res => this.formData = res.data);
      this.setOtherData();
    },
    setOtherData() {
      callAnotherApi().then(res => this.formData = { ...this.formData, other: res.data });
    }
  }
}

上面的代码,是将 formData 自身进行可枚举解构,然后合并新接口的 other 字段,组成一个新的对象,重新赋值给 this.formData ,这样一来,对 this.formData 的赋值就变成了访问 setter ,进而可以实现数据的响应式。

另一种方式是使用 then 链,将上述两个接口的返回值进行合并,最后再一次性赋值给 this.formData 进行动态绑定,实现响应式,代码示例如下:

{
  methods: {
    init() {
      let reactiveData = null;
      callSomeApi().then(res => {
        reactiveData = res.data;
        return this.setOtherData();
      })
      .then(res => {
        this.formData = { ...reactiveData, other: res.data};
      });
    }
  }
}

相比前者,这种方法只需要实现一次 setter 访问,减少了多余的观察者检查,起到一定的优化作用。

类似文章