Vue 中 computed 修改使用的属性

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Vue 中 computed 修改使用的属性相关的知识,希望对你有一定的参考价值。

参考技术A

学习 Vue 计算属性的时候有个疑问❓, 那就是在计算属性中修改使用的属性值, 会不会因为 Vue 的响应式而造成死循环

计算属性可以理解为是一个 getter 函数, 获取新值, 但是 vue 会做一层缓存, 只有改变才会重新计算

举个栗子🌰 :
假设有个属性叫 name , 现在需要一个计算属性 newName , 但是需要在取 newName 的时候再把 name 改成初始值, 由于 name 每次修改的值都不是上一次的值都会触发响应式, 理论上写一个点击方法用来修改一次 name 的值, 处理流程应该是 name --> newName --> name --> newName --> name --> newName .... 然而事实并不会如此

首先要知道流程是什么?
修改 name 的值, 会触发响应式, 那么响应式会调用 trigger 函数, trigger 函数最终会调用 newName 里的响应式副作用( ReactiveEffect )的 scheduler , 而 scheduler 可以根据 ComputedRefImpl 查到实现为 triggerRefValue(this); 也就是调用计算属性 get value 函数, 此时就是在执行 ReactiveEffect 捕捉的 getter 函数并且将该副作用设置为活跃状态的 ReactiveEffect , 也是外面写的 newName 函数, 在这里执行 newName 就会出现对 name 赋新值触发新的 trigger

可以注意的是 trigger 的 key 为正在修改的属性名称, 也就是 name , vue 使用一个 map 通过 key 为 name 来存储 ReactiveEffect 对象

回到上面, 在 newName 中触发新的 trigger , 实际上最后执行的是 triggerEffects , 在这里会对所有的副作用进行遍历, 当然在 newName 中副作用只有一个也就是通过 name 为 key 存储的 ReactiveEffect 对象, 此时它也是活跃状态的 ReactiveEffect , 根据 triggerEffects 中 effect !== activeEffect || effect.allowRecurse 这个判断, 正在执行的 effect 不等于活跃状态的 effect 或者允许递归才会调度 scheduler , 由于当前的 effect === activeEffect, 所以不会执行 newName 的 scheduler 也就不会去执行

结果: vue 内部处理的可能出现死循环的情况, 估计以前某个版本会出现死循环, 到现在 eslint 都会提示 Unexpected side effect in "newName" computed property. 错误

Vue2 computed中的setter

文章目录


前言

Vue2 computedsetter的使用方法, 在什么情况下使用setter, 对于setter的一些个人见解.


一、可写计算属性setter

计算属性默认仅能通过计算函数得出结果。当你尝试修改一个计算属性时,你会收到一个运行时警告。

computed计算属性内直接写的话是默认编辑getter, 但是getter部分严格遵守computed最佳实践风格(见第二章), 在这种限制下可能会使需求的完成受阻.
我的理解是, setter的存在并非为了在最佳实践规则上开个口子退而求其次的妥协以完成目标, 而是通过对setter的使用可以实现在最佳实践风格的基础上达到目的, 比如在计算属性被赋值的解决方案中使用setter, 可以在操作data数据的情况(即违反不产生副作用规则的情况)下避免标红.
呃, 计算属性被 “赋值” 这个说法对我造成过一些误导, 就个人而言, 我感觉这更像是在 “传参”, 虽然这看起来的确是赋值操作.

看起来是这样的:

// 在某个函数里
this.计算属性 = '参数'

但是computed对此的应对:

computed: 
  计算属性: 
    get() ,
    // setter
    set(newValue)  // newValue为赋的值
      // 函数体
    
  

是作为参数传入setter函数中执行, 而不是直接把这个计算属性变成变量那样的随意重赋值的东西.


二、为何使用setter

Vue中有些computed属性使用的不推荐方案, 但是有时为了完成需求又需要这些不推荐的方案.
setter可以解决这些情况, 让computed在那几种特殊情况下依旧能以最佳实践方案运作.

1.需要修改计算属性值

Vue并不推荐 并且 不建议直接修改计算属性值:

从计算属性返回的值是派生状态。可以把它看作是一个“临时快照”,每当源状态发生变化时,就会创建一个新的快照。更改快照是没有意义的,因此计算属性的返回值应该被视为只读的,并且永远不应该被更改——应该更新它所依赖的源状态以触发新的计算。

我的理解是, 我们应该通过主动改变那些源状态(就是影响本次computed计算结果的源)以驱动computed去计算需要的值, 而不是在不断变动并不稳定的computed返回值上直接做更改.
这点的体现是如果去除computed中的setter后依旧直接为计算属性赋值, 那么将会触碰这条红线引发报错:

 Computed property "xxx" was assigned to but it has no setter.

2.不可避的计算属性副作用

我逐渐习惯Eslint了.
Eslist下的computed(我是说它的getter内)禁止进行对data数据的赋值操作, 会标红计算属性不应有副作用, 这里的副作用也包括DOM更改和网络请求等等.

计算属性的计算函数应只做计算而没有任何其他的副作用,这一点非常重要,请务必牢记。举例来说,不要在计算函数中做异步请求或者更改 DOM!一个计算属性的声明中描述的是如何根据其他值派生一个值。因此计算函数的职责应该仅为计算和返回该值。在之后的指引中我们会讨论如何使用监听器根据其他响应式状态的变更来创建副作用。

如果在setter中进行则不会有这种报错情况, 不过我还是不会在computed里给data赋值的.


三、使用setter

<template>
  <div id="demo">
    <button @click="changeComputed">changeComputed</button>
    <input type="text" v-model="firstName" />
    <input type="text" v-model="lastName" />
  </div>
</template>

<script>
export default 
  data() 
    return 
      firstName: "John",
      lastName: "Doe",
    ;
  ,
  computed: 
    fullName: 
      get() 
        return this.firstName + " " + this.lastName;
      ,
      set(newValue) 
        this.firstName = newValue;
        this.lastName = newValue.split(" ");
      
    
  ,
  methods: 
    changeComputed() 
      this.fullName = 'bai X'
    
  
;
</script>

在这个过程中通过setter修改源状态驱动computedgetter再次触发生成了新的值, 遵循了避免直接修改计算属性值的建议.
但是如果setter并没有对计算属性getter中的源状态造成影响, getter将不会重新执行, 即 并非触发setter就一定会触发getter, 他们两个是相互独立的, 对应计算属性的两种用法, 如果你对计算属性进行写入操作, 那就先过setter, 如果本次setter对源状态有影响, 那么也过一次getter, 如果只是调用计算属性, 那么只过getter.

至于"计算函数不应有副作用", 我们的确在computed中对data数据进行了修改, 这似乎并未能避免…


总结

以上是关于Vue 中 computed 修改使用的属性的主要内容,如果未能解决你的问题,请参考以下文章

Vue2 computed中的setter

Vue2 computed中的setter

Vue computed计算属性 理解

vue 计算属性computed get 怎么把当前操作的dom传进去

Vue计算属性和监听属性

vue 如何给computed一个默认值