Vue3.0文档学习心得--响应式核心

1.ref():接受一个内部值,返回一个响应式的、可更改的 ref 对象.此对象只有一个指向其内部值的属性 .value

使用实例:

1.1ref接收值

const count = ref(0) console.log(count.value) //  0   count.value++  console.log(count.value) // 1

打印ref对象:获取值要通过.value

1.2ref接收对象:如果将一个对象赋值给 ref,那么这个对象将通过 reactive() 转为具有深层次响应式的对象(实则是用proxy来)。这也意味着如果对象中包含了嵌套的 ref,它们将被深层地解包。若要避免这种深层次的转换,请使用 shallowRef() 来替代。

 打印job.vuale为proxy对象,不是ref对象了,因此获取type属性是job.value.type

 

2.computed():2种使用方法:(1)创建一个只读的ref对象,不可以对其进行计算操作(2)通过get和set函数,创建一个可写的计算属性ref,(不再是把所有的需要计算的变量都放在computed里面了,而是每次需要就使用一次computed())

使用实例:

(1)const count = ref(1)  const plusOne = computed(() => count.value + 1)  console.log(plusOne.value) // 2   plusOne.value++ // 错误

(2)防止直接对computed返回的变量进行赋值操作,而没有响应式修改,因此需要增加set属性【computed不再只是具有只读属性】

const count = ref(1)
const plusOne = computed({
  get: () => count.value + 1,
  set: (val) => {
    count.value = val - 1
  }
})
plusOne.value = 1  //当对computed对象进行赋值时触发set操作,从而对count进行操作
console.log(count.value) // 0

 写法:

 

3.reactive():  返回一个对象的响应式代理[proxy的实例对象]。(官方推荐最好接收对象、数组,基本类型不要用他)

(1)打印转为reactive实则为proxy包裹的对象

(2)响应式转换是“深层”的:它会影响到所有嵌套的属性。一个响应式对象也将深层地解包任何 ref 属性,同时保持响应性。

const count = ref(1)
const obj = reactive({ count })

// ref 会被解包
console.log(obj.count === count.value) // true

// 会更新 `obj.count`
count.value++
console.log(count.value) // 2
console.log(obj.count) // 2   代理响应式,不需要通过value去取值

// 也会更新 `count` ref
obj.count++
console.log(obj.count) // 3
console.log(count.value) // 3
const count = ref(1)
const obj = reactive({})

obj.count = count  //当一个ref赋值给reactive时自动解包

console.log(obj.count) // 1
console.log(obj.count === count.value) // true

(3)当访问到某个响应式数组或 Map 这样的原生集合类型中的 ref 元素时,不会执行 ref 的解包。

const books = reactive([ref('Vue 3 Guide')])
// 这里需要 .value
console.log(books[0].value)

const map = reactive(new Map([['count', ref(0)]]))
// 这里需要 .value
console.log(map.get('count').value)

(4) 若要避免深层响应式转换,只想保留对这个对象顶层次访问的响应性,请使用 shallowReactive() 作替代。返回的对象以及其中嵌套的对象都会通过 ES Proxy 包裹,因此不等于源对象,建议只使用响应式代理,避免使用原始对象。

(5)reactive和ref的区别

 

4.readonly():接受一个对象 (不论是响应式还是普通的) 或是一个 ref,返回一个原值的只读代理。

(1)只读代理是深层的:对任何嵌套属性的访问都将是只读的。它的 ref 解包行为与 reactive() 相同,但解包得到的值是只读的。

const original = reactive({ count: 0 })

const copy = readonly(original)

watchEffect(() => {
  // 用来做响应性追踪
  console.log(copy.count)
})

// 更改源属性会触发其依赖的侦听器
original.count++   

// 更改该只读副本将会失败,并会得到一个警告,但是值会做响应式更改
copy.count++ // warning!

(2)要避免深层级的转换行为,请使用 shallowReadonly() 作替代。

 

5.watchEffect():立即运行一个函数(相当于写了immediate:true),同时响应式地追踪其依赖,并在依赖更改时重新执行。(不需要指定监听谁,执行函数内所有的变量都属于监听范围内,任意读取、赋值等操作该函数都会再运行一遍)

 (1):第一个参数就是要运行的副作用函数。
const count = ref(0)

watchEffect(() => console.log(count.value))
// -> 输出 0  立即执行!

count.value++
// -> 输出 1

(2)第一个参数副作用函数的参数也是一个函数,用来注册清理回调。清理回调会在该副作用下一次执行前被调用,可以用来清理无效的副作用,例如等待中的异步请求 (参见下面的示例)。

watchEffect(async (onCleanup) => {
  const { response, cancel } = doAsyncWork(id.value)
  // `cancel` 会在 `id` 更改时调用
  // 以便取消之前
  // 未完成的请求
  onCleanup(cancel)
  data.value = await response
})

(3)第二个参数是一个可选的选项,可以用来调整副作用的刷新时机或调试副作用的依赖。

默认情况下,侦听器将在组件渲染之前执行。设置 flush: 'post' 将会使侦听器延迟到组件渲染之后再执行。详见回调的触发时机。在某些特殊情况下 (例如要使缓存失效),可能有必要在响应式依赖发生改变时立即触发侦听器。这可以通过设置 flush: 'sync' 来实现。然而,该设置应谨慎使用,因为如果有多个属性同时更新,这将导致一些性能和数据一致性的问题。

watchEffect(() => {}, {
  flush: 'post',
  onTrack(e) {
    debugger
  },
  onTrigger(e) {
    debugger
  }
})

(4)返回值是一个用来停止该副作用的函数

const stop = watchEffect(() => {})

// 当不再需要此侦听器时:
stop()

 

6.watchPostEffect():watchEffect() 使用 flush: 'post' 选项时的别名。

7.watchSyncEffect():watchEffect() 使用 flush: 'sync' 选项时的别名。

 
8.watch():侦听一个或多个响应式数据源,并在数据源变化时调用所给的回调函数,与watchEffect不同的是懒加载,不是立即执行。
(1)第一个参数是侦听器的源。这个来源可以是以下几种:
  • 一个函数,返回一个值
  • 一个 ref
  • 一个响应式对象
  • ...或是由以上类型的值组成的数组

const state = reactive({ count: 0 }) //当使用 getter 函数作为源时,回调只在此函数的返回值变化时才会触发。如果你想让回调在深层
watch(                   //变更时也能触发,你需要使用 { deep: true } 强制侦听器进入深层级模式。在深层级模式时,
  () => state,             //在深层级模式时,如果回调函数由于深层级的变更而被触发,那么新值和旧值将是同一个对象。
  (newValue, oldValue) => {
  // newValue === oldValue
  },
  { deep: true }
)

const count = ref(0)   //监听ref的基本类型不可以加.value,监听ref对象需要加.value,因为默认解析后是还包裹了proxy对象,
.value后则是直接解析proxy或者使用deep模式 watch(count, (count, prevCount)
=> { /* ... */ })
watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {  //监听多个来源用数组格式存放,第二个参数函数的第一个数组为
  /* ... */                               //存放对应的newVal,第二个数组为存放对应的oldVal
})

1.情况:直接监听响应式对象,不需要通过deep,会自动触发深层模式,并且无法获取正确的oldValue(监听整个reactive对象时,对象里面不管嵌套多少层对象都可以直接监听到,不需要开启deep模式)

2.监听对象中的某个属性使用函数的形式,此时可以获取到oldValue

3.监听对象的多个属性

4.特殊情况:监听对象内的一个对象需要用deep模式

(2)第二个参数是在发生变化时要调用的回调函数。这个回调函数接受三个参数:新值、旧值,以及一个用于注册副作用清理的回调函数。该回调函数会在副作用下一次重新执行前调用,可以用来清除无效的副作用,例如等待中的异步请求。(例如watchEffer使用)

当侦听多个来源时,回调函数接受两个数组,分别对应来源数组中的新值和旧值。

(3)第三个可选的参数是一个对象,支持以下这些选项:

  • immediate:在侦听器创建时立即触发回调。第一次调用时旧值是 undefined
  • deep:如果源是对象,强制深度遍历,以便在深层级变更时触发回调。参考深层侦听器
  • flush:调整回调函数的刷新时机。参考回调的刷新时机及 watchEffect()
  • onTrack / onTrigger:调试侦听器的依赖。参考调试侦听器

posted on 2022-12-12 12:05  ChoZ  阅读(100)  评论(0编辑  收藏  举报

导航