vue3 ref和reactive全家桶(小满zs vue3 笔记六)

tip1: 无响应式数据(控制台数据已经变化,但是页面无刷新),为什么要用ref和reactive

1
2
3
4
5
6
7
8
9
10
11
12
<template>
<div>认识Ref全家桶</div>
<div>{{ message }}</div>
<button @click="change">改变</button>
</template>
<script setup lang="ts">
let message: string = '无响应式数据' // console.log 打印改变了,但message没有刷新,原因不是响应式
const change = () => {
message = '改变数据'
console.log(message)
}
</script>

tip2: ref和reactive的区别

  1. ref用于原始值基本数据类型不支持对象(不是深层次的可以用ref),reactive支持对象

  2. ref定义的需要.value来访问,reactive则不需要

  3. ...

tip3: 技巧2 格式化vue3打印结构

原格式(包几层还需要在dep里的_value里取):

优化后的格式: 

tip4: 用户代码片段,自动生成模板面板,

1. 设置模板 vscode 最下面齿轮设置图标->用户代码片段->输入vue.json来设置模板,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
    "vue3": {
        "prefix": "vue3",
        "body": [
            "<template>",
            "<div>",
            "</div>",
        "</template>",
        "",
        "<script setup lang='ts'>",
        "",
        "</script>",
        "",
        "<style>",
        "",
        "</style>",
            "$2"
        ],
        "description": "Log output to console"
    }
}

tip5: 如何获取dom上元素内容

1
2
3
4
5
6
7
8
9
dom:
<div ref="div"></div>
ref的名称和获取后的一致,这里的名称是div
js:
const div = ref<HTMLDivElement>()
const change = () => {
  message.value = '改变customRef'
  console.log(div.value?.innerText) // 这样可以获取ref元素为div上的属性数据
}

一. ref, Ref (响应式数据)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template>
  <div>认识Ref全家桶</div>
  <div>{{ messageRe }}</div>
  <div>{{ messageR }}</div>
  <button @click="change">改变</button>
</template>
<script setup lang="ts">
import { ref, Ref } from 'vue'
let messageR = ref('响应式数据') // 自动推类型
// 或
let messageRe: Ref<string> = ref('响应式数据') // 通过Ref设置类型,因为只设置类型会报错,两边不一致<string> !== ref<string>,<br>需要通过Ref来设置,这样两边类型才能相等,才能改变它的值
const change = () => {
  messageR.value = '响应式数据-改变'
  messageRe.value = '响应式数据-改变Ref'
  console.log(messageR, messageRe)
}
</script>

二. isRef

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template>
  <div>isRef,是判断是否是ref类型</div>
  <div>{{ messageRe }}</div>
  <button @click="change">改变</button>
</template>
<script setup lang="ts">
import { ref, Ref, isRef } from 'vue'
let messageR = ref('响应式数据')
// 或
let messageRe: Ref<string> = ref('响应式数据')
const change = () => {
  messageR.value = '响应式数据-改变'
  console.log(messageR, messageRe, isRef(messageR), isRef(messageRe))
}
</script>

三. shallowRef(浅层次,不能改变vlaue值)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<template>
<div>
  <div>{{ message }}</div>
  <button @click="change">改变</button>
</div>
</template>
<script setup lang='ts'>
import { Ref, shallowRef } from 'vue'
type Obj = {
  name: string
}
let message: Ref<Obj> = shallowRef({
  name: '认识shallowRef'
})
const change = () => {
  message.value.name = '改变shallowRef'
  // message.value = { name: '监听改变shallowRef'}
  // console.log里的数据已经改变,但页面数据一直不刷新
  // 强制改变triggerRef, shallowRef和ref不能放在一块写,放在一块会多次调用triggerRef,ref底层就是调用triggerRef
  // triggerRef(message)
  console.log(message)
}
</script>

四. customRef,

1
track收集依赖,
1
trigger 触发依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<template>
<div ref="div">
  customRef: {{ message }}
</div>
<button @click="change">customRef更新</button>
</template>
 
<script setup lang='ts'>
import { customRef, ref } from 'vue'
let timer: any // 防抖
// T泛型
function myRef <T = any>(value: T) {
 
  // track
  return customRef((track, trigger) => {
    return {
      get () {
        track() // 收集依赖
        return value
      },
      set (newVal) { // 更新值
        // 调用接口时
        clearTimeout(timer)
        timer = setTimeout(() => {
          value = newVal
          console.log('=====触发了')
          timer = null
        }, 500);
        trigger() // 触发依赖
      }
    }
  })
}
let message = myRef<string>('customRef')
const div = ref<HTMLDivElement>()
const change = () => {
  message.value = '改变customRef'
  console.log(div.value?.innerText) // 这样可以获取ref元素为div上的属性数据
}
</script>

五. ref源码解析

packages -> reactivity -> ref.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// 构造多种场景类型
export function ref<T extends Ref>(value: T): T
export function ref<T>(value: T): Ref<UnwrapRef<T>>
export function ref<T = any>(): Ref<T | undefined>
export function ref(value?: unknown) {
  // 创建ref ,传入当前value及false
  return createRef(value, false)
}
function createRef(rawValue: unknown, shallow: boolean) {
// 判断如果是Ref时直接返回
  if (isRef(rawValue)) {
    return rawValue
  }
// 否则创建,rawValue当前value值, shallow false
  return new RefImpl(rawValue, shallow)
}
// 创建
class RefImpl<T> {
  private _value: T
  private _rawValue: T
 
  public dep?: Dep = undefined
  public readonly __v_isRef = true
// 接收,如果是true时直接返回value,否则执行toReactive
  constructor(value: T, public readonly __v_isShallow: boolean) {
    this._rawValue = __v_isShallow ? value : toRaw(value)
    this._value = __v_isShallow ? value : toReactive(value)
  }
// 收集依赖
  get value() {
    trackRefValue(this)
    return this._value
  }
// 触发依赖
  set value(newVal) {
    const useDirectValue =
      this.__v_isShallow || isShallow(newVal) || isReadonly(newVal)
    newVal = useDirectValue ? newVal : toRaw(newVal)
    if (hasChanged(newVal, this._rawValue)) {
      this._rawValue = newVal
      this._value = useDirectValue ? newVal : toReactive(newVal)
      triggerRefValue(this, newVal)
    }
  }
}

shallowRef, 直接传的是true,所以它的值不会变

六. reactive, readonly只读,shallowReactive(只到第一个属性那里.a = {},会被reactive影响)

 1. 表单提交,只能触发对象,字符串和number不支持

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<template>
<div>
  reactive:
  <form>
    <div>{{ form.name }}</div>
    <div>{{ form.age }}</div>
    <input v-model="form.name"/>
    <input v-model="form.age"/>
    <!-- 不写.prevent 会触发本身的组件属性来刷新 -->
    <button @click.prevent="submit">提交表单</button>
  </form>
</div>
</template>
 
<script setup lang='ts'>
import { reactive } from 'vue';
type Ages = {
  name: string,
  age: number
}
let form = reactive<Ages>({
  name: 'li li',
  age: 32
})
 
const submit = () => {
  console.log(form)
}
</script>

 2. 数组添加,reactive proxy 不能直接赋值,否则破坏响应式对象,可以作为一个属性去赋值解决

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<template>
<div>
  reactive:
  <form>
    <ul>
      <li v-for="(item, index) in items" :key="index">{{ item }}</li>
    </ul>
    <!-- 不写.prevent 会触发本身的组件属性来刷新 -->
    <button @click.prevent="submit">提交表单</button>
  </form>
</div>
</template>
 
<script setup lang='ts'>
import { reactive } from 'vue';
type Obj = {
  array?: Array<string>
}
const items = reactive<Obj>({
  array: []
})
const readOnlyItems = readOnly(items)
const submit = () => {
  // 如果是接口返回的话
  const data = ['aa', 'bb', 'cc']
  // 直接赋值会报错,
  // items = data
  // 1. push
  // items.push(...data)
  // 2. 可以为它增加一个属性
  items.array = data  console.log(items)
}
</script>
      // 直接修改会报警告,可以修改items.array,不能修改readOnlyItems的array,因为是只读的
     readOnlyItems.array = data

七. reactive源码

packages -> reactivity -> reactive.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
export function reactive<T extends object>(target: T): UnwrapNestedRefs<T>
export function reactive(target: object) {
  // if trying to observe a readonly proxy, return the readonly version.
// 判断是否是只读的代理,如果是直接返回
  if (isReadonly(target)) {
    return target
  }
// 创建reactive对象
  return createReactiveObject(
    target,
    false,
    mutableHandlers,
    mutableCollectionHandlers,
    reactiveMap
  )
}
function createReactiveObject(
  target: Target,
  isReadonly: boolean,
  baseHandlers: ProxyHandler<any>,
  collectionHandlers: ProxyHandler<any>,
  proxyMap: WeakMap<Target, any>
) {
// 判断是否是object对象类型, 否则报警告,reactive是不能处理string,number常规类型的
  if (!isObject(target)) {
    if (__DEV__) {
      console.warn(`value cannot be made reactive: ${String(target)}`)
    }
    return target
  }
  // target is already a Proxy, return it.
  // exception: calling readonly() on a reactive object
  if (
    target[ReactiveFlags.RAW] &&
    !(isReadonly && target[ReactiveFlags.IS_REACTIVE])
  ) {
    return target
  }
  // target already has corresponding Proxy
// 判断是否存在,如果存在直接返回
  const existingProxy = proxyMap.get(target)
  if (existingProxy) {
    return existingProxy
  }
  // only specific value types can be observed.
// 判断是否在白名单中,如果在直接返回
  const targetType = getTargetType(target)
  if (targetType === TargetType.INVALID) {
    return target
  }
// 创建代理对象
  const proxy = new Proxy(
    target,
    targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
  )
  proxyMap.set(target, proxy)
  return proxy
}

  

posted @   TheYouth  阅读(355)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· 单线程的Redis速度为什么快?
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
点击右上角即可分享
微信分享提示