Vue3-笔记002-Ref与Reactive

Ref

接受一个内部值并返回一个响应式且可变的 ref 对象。ref 对象仅有一个 .value property,指向该内部值。

ref案例

<template>
  <div>
    <button @click="changeMsg">change</button>
    <div>{{ message }}</div>
  </div>
</template>
 
<script setup lang="ts">
let message: string = "我是message"
const changeMsg = () => {
   message = "change msg"
}
</script>

以上案例是无法改变 message 的值,因为 message 不是响应式的无法被 vue 跟踪到。需要改成 ref 才能实现响应式变化。

<template>
    <div>
        <button @click="change">change</button>
        <div>{{ name }}</div>
    </div>
</template>

<script setup lang='ts'>
import { ref } from 'vue'

const name = ref('wangjw')
const change = () => {
    name.value = "change msg";
    console.log('name', name);
}
</script>

ref与Ref

ref 会根据初始化时的值推导其类型。

import { ref } from 'vue' 
// 推导出的类型:Ref<number> 
const year = ref(2020) 
// => TS Error: Type 'string' is not assignable to type 'number'. 
year.value = '2020'

ref 内的值指定一个更复杂的类型,可以通过使用 Ref 这个类型。

import { ref } from 'vue'
import type { Ref } from 'vue'

// string类型 或者 number类型
const year: Ref<string | number> = ref('2020')
// 可以修改
year.value = 2020

在调用 ref() 时传入一个泛型参数,可以覆盖默认的推导行为。

// 得到的类型:Ref<string | number>
const year = ref<string | number>('2020')
// 可以修改
year.value = 2020

指定了一个泛型参数没有给出初始值,那么最后得到的就将是一个包含 undefined联合类型

// 推导得到的类型:Ref<number | undefined>
const n = ref<number>()

ifRef

判断是不是一个ref对象。

import { ref, Ref, isRef } from 'vue'

let message: Ref<string | number> = ref("我是message")
let notRef: number = 123
const changeMsg = () => {
  message.value = "change msg"
  console.log(isRef(message)); // true
  console.log(isRef(notRef)); // false
}

shallowRef

创建一个跟踪自身 .value 变化的 ref,但不会使其值也变成响应式的。
ref 深层次的响应;shallowRef 浅层次的响应。

注意:ref和shallowRef不能放在一块书写,会影响shallowRef,使他的值改变,造成视图的更新。

// 修改其属性是非响应式的,以下的书写方式是不会改变视图的
<template>
  <div>
    <button @click="changeMsg">change</button>
    <div>{{ message }}</div>
  </div>
</template>
 
<script setup lang="ts">
import { Ref, shallowRef } from 'vue'

type Obj = {
  name: string
}
let message: Ref<Obj> = shallowRef({
  name: "张三"
})
const changeMsg = () => {
  message.value.name = '李四'
}
</script>

// 下面的修改value的方式是可以被监听到的,从而改变视图
<script setup lang="ts">
import { Ref, shallowRef } from 'vue'

type Obj = {
  name: string
}
let message: Ref<Obj> = shallowRef({
  name: "张三"
})
const changeMsg = () => {
  message.value = { name: "李四" }
}
</script>

triggerRef

强制更新页面DOM。

customRef

自定义ref。
customRef 是个工厂函数要求我们返回一个对象,并且实现 getset 适合去做防抖之类的业务。

<template>
  <div>
    {{ name }}
  </div>
  <hr>
  <button @click="change">修改 customRef</button>
</template>
 
<script setup lang='ts'>
import { ref, reactive, onMounted, shallowRef, customRef } from 'vue'
 
function myRef<T = any>(value: T) {
  let timer:any;
  return customRef((track, trigger) => {
    return {
      get() {
        track()
        return value
      },
      set(newVal) {
        clearTimeout(timer)
        timer =  setTimeout(() => {
          console.log('触发了set')
          value = newVal
          trigger()
        },500)
      }
    }
  })
}
const name = myRef<string>('张三')
const change = () => {
  name.value = '李四'
}
</script>

dom元素的ref

<template>
	<div ref="dom">我是dom元素</div>
</template>

<script setup lang="ts">
import { ref } from 'vue'

const dom = ref<HTMLDivElement>()

// 放到可读到元素的方法中即可打印。
console.log(dom.value?.innerText)
</script>

Reactive

与ref的共同点

ref和reactive都是将一个数据改变为响应式数据。

与ref的不同点

ref支持所有数据类型,reactive仅支持引用类型。比如 Array、Object、Map、Set等。
ref取值或赋值都需要使用.value,但是reactive不需要。

数组的异步赋值问题

reactive的数组异步赋值是通过proxy去代理的,直接去赋值会将其覆盖,使其响应式失效,导致页面不去更新。

解决方案一:使用push

import { reactive } from 'vue'

let person = reactive<number[]>([])
setTimeout(() => {
  const arr = [1, 2, 3]
  person.push(...arr)
  console.log(person);
},1000)

解决方案二:使用解构(包裹一层对象)

type Person = {
  list?:Array<number>
}

let person = reactive<Person>({
   list:[]
})
setTimeout(() => {
  const arr = [1, 2, 3]
  person.list = arr;
  console.log(person);
},1000)

readonly

将一个reactive值变为只读的。

import { reactive,readonly } from 'vue'

const number = reactive({count:1})
const numberCopy = readonly(number)

// numberCopy是无法修改的
// numberCopy.count++

// numberCopy会受原始数据的影响,即number变化时,numberCopy也会变化。
number.count++

shallowReactive

只能对浅层的数据有影响。如果是深层的数据只会改变值,不会改变视图。
浅层:即只会影响到第一层,并发生视图改变。
备注:如果和reactive同时书写改变,会被影响导致深层的数据也改变视图。

toRef

toRef 只能修改对象的值,所以对于一个非响应式对象数据,是不会更新视图的,只会改变值。
如果改变的原始对象是响应式的,则会改变值并更新视图。

语法:toRef(对象,对象的一个属性)

obj = {
	name:'张三'
}
const tempObj = toRef(obj,'name')

应用:需要一个变量等同于另一个响应式对象的某个属性,改变这个变量,响应式对象的该属性也同步变化。对于深层次的对象,防止每次使用都用 obj.abc.def.ghe 这种形式来使用。

本质:

// 如果是ref对象直接返回,否则调用 ObjectRefImpl 创建一个类ref对象。

export function toRef<T extends object, K extends keyof T>(
  object: T,
  key: K,
  defaultValue?: T[K]
): ToRef<T[K]> {
  const val = object[key]
  return isRef(val)
    ? val
    : (new ObjectRefImpl(object, key, defaultValue) as any)
}

// 类ref对象只是做了值的改变,并未处理"收集依赖"和"触发依赖的过程"。
// 所以普通对象无法更新视图。

class ObjectRefImpl<T extends object, K extends keyof T> {
  public readonly __v_isRef = true
 
  constructor(
    private readonly _object: T,
    private readonly _key: K,
    private readonly _defaultValue?: T[K]
  ) {}
 
  get value() {
    const val = this._object[this._key]
    return val === undefined ? (this._defaultValue as T[K]) : val
  }
 
  set value(newVal) {
    this._object[this._key] = newVal
  }
}

toRefs

批量创建ref对象,一般用于结构reactive对象,方便使用。

import { reactive, toRefs } from 'vue'

const obj = reactive({
   name: '张三',
   year: 18
})
let { name, year } = toRefs(obj)
year.value++
console.log(name, year);

本质:

// 把reactive对象的每一个属性都变成了ref对象,循环调用了toRef。

export type ToRefs<T = any> = {
  [K in keyof T]: ToRef<T[K]>
}
export function toRefs<T extends object>(object: T): ToRefs<T> {
  if (__DEV__ && !isProxy(object)) {
    console.warn(`toRefs() expects a reactive object but received a plain one.`)
  }
  const ret: any = isArray(object) ? new Array(object.length) : {}
  for (const key in object) {
    ret[key] = toRef(object, key)
  }
  return ret
}

toRaw

将响应式对象转化为普通对象。

本质:

// 通过 ReactiveFlags 枚举值,取出proxy对象的原始对象。

export const enum ReactiveFlags {
  SKIP = '__v_skip',
  IS_REACTIVE = '__v_isReactive',
  IS_READONLY = '__v_isReadonly',
  IS_SHALLOW = '__v_isShallow',
  RAW = '__v_raw' // 原始对象
}
 
export function toRaw<T>(observed: T): T {
  const raw = observed && (observed as Target)[ReactiveFlags.RAW]
  return raw ? toRaw(raw) : observed
}
  • 29
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值