当前位置: 首页 > article >正文

学习vue3第六节(vue3 中 computed watch watchEffect)

1、computed

计算属性:computed 原理:不管是 Vue2 中还是在 Vue 3 中,计算属性会基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。 在对赋值求值或者大数据求值会起到一定性能优化的作用。

在 Vue 中计算属性会计算出一个新的属性,并将这个属性挂载到 vm(Vue 实例上)。而相对的还有侦听器 watch,侦听器是监听 vm 上已经存在的响应式属性,所以可以用侦听器监听计算属性。

计算属性本质是一个惰性求值的观察者,具有缓存性,只有当依赖发生改成时,才会重新求值。侦听器是当依赖数据发生改变时就会执行回调。

在使用场景上,计算属性适合在一个数据被多少数据影响时使用,而侦听器适合在一个数据影响多个数据。接下来我们就通过源码来看看 computed 的实现原理。

而在vue3 中,computed 有两种形式:
1、直接接收一个getter函数,并且getter()函数会返回一个不可改变的响应式ref对象,注意是不可改变的,
2、接受一个对象,对象中包含 get()、set() 方法,通过get()、set()进行读写ref对象

computed / watch / watchEffect 含义
监听数据的异同
监听一个属性,多个属性,ref对象,reactive对象

<template>
  <div class="watch-container">

    年龄:{{ age }}
    <br>
    姓名:{{ name }}
    <br>
    职业:{{ person.job }}
    <br>
    爱好:{{ person.love }}
  </div>
  <div>
    otherPerson==={{ JSON.stringify(otherPerson) }}
    年龄:{{ otherPerson.age.value }}
  </div>
  <button @click="handleChangeAge()">change age</button>
  <div>==============</div>
  <div>personAllName:{{ personAllName }}</div>
  <div>lastName: {{ lastName }}</div>
  <br>
  <button type="primary" @click="handleChangeLastName">change person laset name</button>

</template>
<script setup>
import { ref,  reactive, computed} from 'vue'
const age = ref(0)
const name = ref('Andy')
const person = reactive({
  job: '搬砖',
  love: '音乐'
})
// 1、传入getter 函数
const otherPerson = computed(() => {
  return {
    age,
    name,
    ...person
  }
},
{
  onTrack(e) {
    // 当 otherPerson.age 被追踪为依赖时触发
    debugger
  },
  onTrigger(e) {
    // 当 otherPerson.age 被更改时触发
    debugger
  }
}
)
const handleChangeAge = () => {
  age.value++
  console.log('====')
}

// 2、 传入对象,可以进行读写操作
let firstName = ref('zhang')
let lastName = ref('Andy')
const personAllName = computed({
  get() {
    return `${firstName.value} ${lastName.value}`
  },
  set(otherPersonName) {
    lastName.value = otherPersonName
  }
})
const handleChangeLastName = () => {
  personAllName.value = '下一个肖申克' + "$"
}
</script>

实现方式上,vue3与vue2中的computed() 都是基于响应式依赖进行缓存的,
但是 vue3 中的computed需要依赖于 ref() 和 reactive() 响应属性的值来实现,而effect则是依赖于computed() 来实现的

当我们在调用 computed 方法时,就会在这里需要统一做下区分,同时调用实现类 ComputedRefImpl,这个方法比较简单,接下来我们重点分析下类 ComputedRefImpl。

<script>
function computed(getterOrOptions, ...) {
  let getter;
  let setter;
  const onlyGetter = isFunction(getterOrOptions);
  if (onlyGetter) {
    getter = getterOrOptions;
    setter = () => {
      console.warn('Write operation failed: computed value is readonly');
    }
    ;
  } else {
    getter = getterOrOptions.get;
    setter = getterOrOptions.set;
  }
  const cRef = new ComputedRefImpl(getter, setter, onlyGetter || !setter, isSSR);
  ...
  return cRef;
}

// ComputedRefImpl 类
class ComputedRefImpl {
  constructor(getter, _setter, isReadonly, isSSR) {
    this._setter = _setter;
    this.dep = undefined;
    this.__v_isRef = true;
    this._dirty = true;
    // 创建effect对象,生成watcher监听函数,并将值赋给实例的 effect属性, 
    this.effect = new ReactiveEffect(getter, () => {
      if (!this._dirty) {
        this._dirty = true;
        triggerRefValue(this);
      }
    });
    this.effect.computed = this;
    this.effect.active = this._cacheable = !isSSR;
    // 根据传入是否有setter函数来决定是否只读;设置ref是否为只读对象,
    this["__v_isReadonly" /* IS_READONLY */] = isReadonly;
  }
  get value() {
    const self = toRaw(this);
    trackRefValue(self);
    if (self._dirty || !self._cacheable) {
      self._dirty = false;
      self._value = self.effect.run();
    }
    return self._value;
  }
  set value(newValue) {
    this._setter(newValue);
  }
}
</script>

若需要computed 实际案例,请留言说明具体业务需求,在线提供代码

2、watch

侦听器,只要监听的响应式属性|对象|数组发生变化就会触发回调函数:

watch(
  source,
  callback,
  {
    deep:true,
    immediate:true,
    flush:'pre'|'post'|'async',
    onTrack(){
      // 调试
    },
    onTrigger(){
      debugger
    },
    once: true // 是否只执行一次
  }
)

侦听的数据源 source :
1、响应式对象reactive;
2、一个ref属性(包括computed),
3、一个getter()函数;
4、多个数据源组成的数组;

<script setup>
import { ref, reactive, watch } from 'vue'
const firstName = ref('张')
const lastName = ref('Andy')

const person = reactive({
  name: 'Andy',
  job: '专业搬砖'
})
</script>

// 监听单个ref

watch(firstName, (newValue, oldValue) => {
  console.log('firstName===',newValue, oldValue)
})

// 监听reactive对象

watch(person, (newValue, oldValue) => {
  // 此时newValue === oldValue // true 是相当的
  // 因为他们是同一个对象
})

// 若要监听reactive对象中的某个属性,需要如下:
// 若要监听 person中job改变,使用getter函数

watch(
  () => person.job, // 第一个参数需要是 函数,
  (newValue, oldValue) => {
    // 此时可以监听到 newValue 与 oldValue 不相等
  },
  {
    deep: true, // 默认false, 是否启用深度监听,与vue2 语法一样,
    immediate: true // 默认false, 是否立即执行,在页面初始化时候需要执行回调函数时,可以添加此属性,第一次调用时旧值是 undefined,
    flush: 'pre'|'post' | 'async', //默认是pre, 如果想在侦听器回调中能访问被 Vue 更新之后的所属组件的 DOM,可以使用 post,如果想同步执行可以使用 async;(分别有对应的语法糖,watchPostEffect(),watchAsyncEffect(), 下节说明)
    onTrack(){},
    onTrigger(){},
    once: false, // 默认false 是否只执行一下 v3.4 之后才有
  }
)

// 监听多数据源

watch(
  [lastName, ()=> person.name], 
  ([lastNameNew, personNameNew], [lastNameOld, personNameOld]) => {
    // 此时回调函数中需要是对应的新旧值数组,顺序与 source 中数组属性顺序一致;
})

// 停止侦听器

const stopWatch = watch(firstName, () => {
	//
})

// 执行 stopWatch() 即可停止侦听器;

// 清除副作用,比如深度监听、立即执行时候,里面有请求后台的接口,我们希望的是使用最新的数据参数,进行处理逻辑??? 超出三界之外,后续补充!!!

3、watchEffect

// watchEffect 立即运行一个函数,同时响应式的追踪其依赖,并且能够在依赖发生变化时候,重新执行函数,可以监听页面中所有ref、reactive声明的属性、对象、等响应式变量;
// 动态新加的属性 同样会监听到,收集所有依赖监听执行;
//

let stopWatchEffect = watchEffect(() => {
// 第一个参数:回调函数
}, {
// 第二参数:
  flush: 'pre', 'post', 'sync', // 默认是pre,参考watch对应用法
  onTrack(e) { // 调试
    debugger
  },
  onTrigger(e) {
    debugger
  }
},
)

// watch 需要显性的指定需要监听的对象,属性,可以自己配置对应的属性,如是否立即执行(immediate: true),是否需要深度监听(deep: true)
// watchEffect 则会自动收集需要响应式对象属性,并且会在对象或属性发生变化时候,立即执行副作用函数,而不需要手动配置;

欢迎各位大神在线批评指导!!!


http://www.kler.cn/a/274428.html

相关文章:

  • harmony UI组件学习(1)
  • 用SparkSQL和PySpark完成按时间字段顺序将字符串字段中的值组合在一起分组显示
  • 前端yarn工具打包时网络连接问题排查与解决
  • 搜索召回:倒排召回
  • HTTP—03
  • App自动化之dom结构和元素定位方式(包含滑动列表定位)
  • 有什么小程序适合个人开发?
  • Aigtek超声功率放大器产品介绍
  • 力扣1. 两数之和
  • 腾讯云服务器多少钱1个月?2024一个月收费阿济格IE吧
  • 数据结构:详解【顺序表】的实现
  • PlantUML Integration 编写短信服务类图
  • 深入挖掘C语言之——枚举
  • Redis数据结构对象中的对象共享、对象的空转时长
  • 【Godot4.2】2D导航01 - AStar2D及其使用方法
  • python企业编码管理的程序(附源码)
  • 微信小程序接口请求出错:request:fail url not in domain list:xxxxx
  • 代码随想录算法训练营第53天 | 1143.最长公共子序列 ,1035.不相交的线 ,53. 最大子序和
  • 5.1.4、【AI技术新纪元:Spring AI解码】Amazon Bedrock
  • ASP .Net Core ILogger日志服务
  • MongoDB聚合运算符:$getField
  • 点云配准9:Colored-ICP的Open3D实现
  • Echarts折线图x轴不显示全部数据的解决办法,亲测有效
  • 电脑数据安全新利器:自动备份文件的重要性与实用方案
  • JupytetNotebook常用的快捷键
  • 【Vue3】Vue3中的编程式路由导航 重点!!!