Vue响应式进阶

本文介绍了Vue中与响应式相关的高级概念,包括shallowRef如何只让第一层数据响应式,triggerRef用于强制渲染组件,customRef允许自定义get和set方法,以及shallowReactive和shallowReadonly对第一层数据的处理。同时,讨论了toRaw和markRaw在处理原始数据和防止转换为响应式数据时的作用。

Vue响应式进阶

shallowRef()、triggerRef()

shallowRef()

ref() 不同,浅层 ref 的内部值将会原样存储和暴露,并且不会被深层递归地转为响应式。只有对 .value 的访问是响应式的。

shallowRef() 常常用于对大型数据结构的性能优化或是与外部的状态管理系统集成。

shallowRef():只有第一层数据变了组件才会重新渲染,深层数据变了组件不会重新渲染,watch也监听不到变化。

简而言之就是第一层是响应式数据,非第一层数据不是响应式数据

triggerRef()

上面的shallowRef()的作用是只有第一层数据变了才会重新渲染,那如果某种情况需要改变深层的某个数据怎么办?这时候就可以用triggerRef()改变深层数据时强制渲染组件。

shallowRef()和triggerRef()是一对死对头,前者是深层数据改变的时候不让重新渲染组件,后者则是深层数据改变的时候偏偏就要重新渲染组件。

<script setup>
import { shallowRef, triggerRef } from 'vue'
const state = shallowRef({ count: 1 })

// shallowRef: 只有第一层数据变了组件才会重新渲染,深层数据变了组件不会重新渲染

// 按钮触发事件
function change() {
  // 数据会变,组件不会重新渲染,页面上显示的还是1
  state.value.count = 2

  // 数据会变,组件会重新渲染,页面上显示的会变成2
  // state.value = { count: 2 }

  // 强制更新state 组件会重新渲染,页面上显示会变成2
  triggerRef(state)  
}
</script>

<template>
  <div>
    {{ state.count }}
    <button @click='change'>改变</button>
  </div>
</template>

customRef()

customRef():创建一个可以自己指定get和set方法的ref数据

customRef() 传入一个工厂函数,工厂函数中会有track和trigger两个参数,这两个参数都是函数。track是在get方法中触发,trigger是在set方法中触发。

track和trigger两个函数的作用就是将数据变成响应式,触发其他的方法,比如只有触发了track和trigger两个函数组件才会重新渲染,页面数据才会变。不然别的地方是不知道什么时候获取了数据,什么时候改变了数据。

<script setup>
import { customRef } from 'vue'

let value = "高启强"
// customRef()中传入一个工厂函数,这个工厂函数接受 track 和 trigger 两个函数作为参数
const myRef = customRef((track, trigger) => {
  // 返回一个带有 get 和 set 方法的对象
  return {
    get() {
      console.log('获取数据');
      track()
      return value
    },
    set(newValue) {
      console.log('改变数据');
      value = newValue
      trigger()
    }
  }
})

function change() {
  console.log(myRef.value);
  // 获取数据
  // 高启强

  myRef.value = '高启兰'
  console.log(myRef.value);
  // 改变数据
  // 获取数据
  // 高启兰
}
</script>

<template>
  <div>
    {{ myRef }}

    <button @click='change'>修改</button>
  </div>
</template>

shallowReactive()、shallowReadonly()

和shallowRef()方法作用一样,shallowReactive()也是监听第一层数据,不一样的是数据是reactive数据,不是ref数据。shallowReadonly()是readonly数据,和前两数据刚好相反,只有第一层是不可修改的。

toRaw()、markRaw()

toRaw()

toRaw()方法的作用就是将数据打回原形,把身上的其他Vue监听方法都去掉,恢复到最原始的js数据。

toRaw() 可以返回由 reactive()readonly()shallowReactive() 或者 shallowReadonly() 创建的代理对应的原始对象

<script setup>
import { toRaw, reactive } from 'vue'

// 原始数据
const obj = {
  name: '孙悟空'
}

// reactive数据
const obj1 =  reactive(obj)
console.log(obj1 === obj);     // false

// 使用toRaw获取到原始数据
const obj2 = toRaw(obj1)
console.log(obj2 === obj);     // true
</script>

markRaw()

<script setup>
import { markRaw, reactive, isReactive } from 'vue'

// 原始数据
const obj = {
  name: '孙悟空'
}

// 将原始数据保护起来,不能转为其他数据
const markObj = markRaw(obj)

// 将保护起来的数据转为reactive数据(这里不会转成功,因为已经被markRaw保护过了)
const obj1 = reactive(markObj)

// 不是reactive数据
console.log(isReactive(obj1));  // false
// 和原始数据一样
console.log(obj1 === obj);      // true

</script>
<think>好的,我需要总结Vue3中响应式机制的进阶知识点和实用技巧。首先,我得回忆一下用户提供的引用内容,看看哪些部分相关。引用3提到Vue3使用Proxy实现响应式,还有Composition API和Tree-shaking。引用4讨论了虚拟DOM的优化,比如Block Tree和Patch Flags。引用2提到了性能优化,比如代码分割和响应式数据的使用优化。 用户需要的是进阶内容,所以得深入一些。首先,Proxy的优势和局限可能需要详细解释,比如它如何处理数组和嵌套对象。然后是依赖收集和触发机制,比如track和trigger函数的作用。接着是性能优化部分,比如用markRaw跳过响应式,或者使用shallowRef处理大型数据。组合式API的自定义hook也是一个实用技巧,比如封装数据获取逻辑。 还要考虑响应式系统的调试技巧,比如用onTrack和onTrigger来追踪依赖变化。另外,可能需要比较Vue2的defineProperty和Vue3的Proxy的区别,但用户可能已经知道基础,所以重点放在进阶上。引用4中的Patch Flags和Block Tree虽然属于渲染优化,但和响应式系统相关,可以提到如何通过响应式优化减少不必要的渲染。 实用技巧方面,可以推荐使用computed和watchEffect的最佳实践,比如避免在computed中产生副作用。还有使用ref和reactive的选择场景,比如基本类型用ref,对象用reactive。另外,自定义响应式转换,比如用自定义的ref来处理特殊需求。 需要确保回答结构清晰,分点列出,每个部分都有具体的例子或技巧。同时,引用相关的引用内容,比如提到Proxy时引用3,性能优化时引用2和4。最后生成相关问题时要围绕Vue3响应式系统的高级应用和优化方法。</think>### Vue3 响应式系统进阶知识点与实用技巧总结 --- #### 一、核心机制进阶 1. **Proxy 响应式原理深度解析** Vue3 使用 ES6 Proxy 代理对象,可拦截 13 种操作(如 `get/set/deleteProperty`),相比 Vue2 的 `Object.defineProperty` 具备以下优势: - 自动检测深层对象/数组变化 - 支持动态新增/删除属性(无需 `Vue.set/delete`) ```javascript const obj = reactive({ a: 1 }) obj.b = 2 // 自动触发响应 ``` - **局限**:需注意原始对象不可被代理(如 `Date` 实例),需用 `ref` 包装[^3] 2. **依赖收集与触发机制** 通过 `track()` 和 `trigger()` 实现细粒度依赖追踪: ```javascript effect(() => { track(obj, 'value') // 收集依赖 console.log(obj.value) }) obj.value = 5 // trigger() 触发更新 ``` - **优化技巧**:使用 `markRaw` 跳过不必要的响应式转换[^3] --- #### 二、性能优化策略 1. **层级控制响应式深度** - `shallowRef/shallowReactive` 创建浅层响应对象 ```javascript const largeList = shallowRef([]) // 仅跟踪 .value 变化 ``` - 配合 `triggerRef()` 手动触发深层更新[^2] 2. **响应式数据冻结** 使用 `Object.freeze()` 或 `markRaw` 阻止非必要响应: ```javascript const config = markRaw({ immutable: true }) // 永不触发更新 ``` 3. **计算属性高级用法** - 带 setter 的计算属性: ```javascript const fullName = computed({ get: () => `${firstName.value} ${lastName.value}`, set: (val) => [firstName.value, lastName.value] = val.split(' ') }) ``` - **性能提示**:避免在 `computed` 中执行耗时操作[^4] --- #### 三、组合式 API 进阶技巧 1. **自定义 Hook 封装** 将响应式逻辑封装为可复用单元: ```javascript // useFetch.js export function useFetch(url) { const data = ref(null) const error = ref(null) watchEffect(async () => { try { data.value = await fetch(url.value).then(res => res.json()) } catch (e) { error.value = e } }) return { data, error } } ``` 2. **Effect 作用域管理** 使用 `effectScope` 批量控制副作用: ```javascript const scope = effectScope() scope.run(() => { watch(query, () => fetchData()) watch(filter, () => applyFilter()) }) scope.stop() // 一键清除所有监听 ``` --- #### 四、调试与异常处理 1. **响应式追踪调试** 使用 `onTrack/onTrigger` 调试依赖变化: ```javascript watchEffect( () => { /* ... */ }, { onTrack(e) { console.log('追踪到依赖:', e.target) }, onTrigger(e) { console.log('触发更新:', e.target) } } ) ``` 2. **内存泄漏防护** - 在 `onUnmounted` 中清理手动创建的 `watch/effect` - 使用 `WeakMap/WeakSet` 存储临时响应数据 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值