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

vue3中customRef的用法以及使用场景

1. 基本概念

customRef 是 Vue3 提供的用于创建自定义响应式引用的 API,允许显式地控制依赖追踪和触发响应。它返回一个带有 getset 函数的工厂函数来自定义 ref 的行为。

1.1 基本语法

import { customRef } from 'vue'

function createCustomRef(value) {
  return customRef((track, trigger) => {
    return {
      get() {
        track() // 追踪依赖
        return value
      },
      set(newValue) {
        value = newValue
        trigger() // 触发更新
      }
    }
  })
}

2. 常见使用场景

2.1 防抖 Ref

function useDebouncedRef(value, delay = 200) {
  let timeout
  return customRef((track, trigger) => {
    return {
      get() {
        track()
        return value
      },
      set(newValue) {
        clearTimeout(timeout)
        timeout = setTimeout(() => {
          value = newValue
          trigger()
        }, delay)
      }
    }
  })
}

// 使用示例
const searchQuery = useDebouncedRef('', 500)

// 在模板中使用
// <input v-model="searchQuery" />

2.2 节流 Ref

function useThrottledRef(value, delay = 200) {
  let lastTriggerTime = 0
  return customRef((track, trigger) => {
    return {
      get() {
        track()
        return value
      },
      set(newValue) {
        const now = Date.now()
        if (now - lastTriggerTime >= delay) {
          value = newValue
          lastTriggerTime = now
          trigger()
        }
      }
    }
  })
}

// 使用示例
const scrollPosition = useThrottledRef(0, 100)

2.3 验证 Ref

function useValidatedRef(value, validator) {
  return customRef((track, trigger) => {
    return {
      get() {
        track()
        return value
      },
      set(newValue) {
        if (validator(newValue)) {
          value = newValue
          trigger()
        } else {
          console.warn('Invalid value:', newValue)
        }
      }
    }
  })
}

// 使用示例
const age = useValidatedRef(18, (value) => {
  return Number.isInteger(value) && value >= 0 && value <= 120
})

2.4 异步 Ref

function useAsyncRef(getter) {
  let value = null
  let isLoading = true
  
  const ref = customRef((track, trigger) => {
    // 初始加载数据
    getter().then(data => {
      value = data
      isLoading = false
      trigger()
    })
    
    return {
      get() {
        track()
        return { value, isLoading }
      },
      set() {
        throw new Error('Async ref is readonly')
      }
    }
  })
  
  return ref
}

// 使用示例
const userProfile = useAsyncRef(async () => {
  const response = await fetch('/api/user')
  return response.json()
})

3. 高级应用场景

3.1 持久化 Ref

function useLocalStorageRef(key, defaultValue) {
  const storedValue = JSON.parse(localStorage.getItem(key) || 'null')
  let value = storedValue !== null ? storedValue : defaultValue
  
  return customRef((track, trigger) => {
    return {
      get() {
        track()
        return value
      },
      set(newValue) {
        value = newValue
        localStorage.setItem(key, JSON.stringify(newValue))
        trigger()
      }
    }
  })
}

// 使用示例
const theme = useLocalStorageRef('app-theme', 'light')

3.2 格式化 Ref

function useFormattedRef(value, formatter, parser) {
  return customRef((track, trigger) => {
    return {
      get() {
        track()
        return formatter(value)
      },
      set(newValue) {
        value = parser(newValue)
        trigger()
      }
    }
  })
}

// 使用示例
const price = useFormattedRef(
  1000,
  (value) => `$${value.toFixed(2)}`,
  (value) => parseFloat(value.replace('$', ''))
)

4. 实际应用示例

4.1 表单输入处理

<template>
  <div>
    <input v-model="email" />
    <p>状态: {{ email.status }}</p>
    <p>错误信息: {{ email.error }}</p>
  </div>
</template>

<script setup>
function useValidatedEmailRef(initialValue = '') {
  let value = initialValue
  let status = 'initial'
  let error = ''
  
  const validateEmail = (email) => {
    const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
    return regex.test(email)
  }
  
  return customRef((track, trigger) => {
    return {
      get() {
        track()
        return {
          value,
          status,
          error
        }
      },
      set(newValue) {
        value = newValue
        if (!newValue) {
          status = 'initial'
          error = ''
        } else if (validateEmail(newValue)) {
          status = 'valid'
          error = ''
        } else {
          status = 'invalid'
          error = '请输入有效的邮箱地址'
        }
        trigger()
      }
    }
  })
}

const email = useValidatedEmailRef()
</script>

4.2 搜索优化

<template>
  <div>
    <input v-model="searchQuery" />
    <div v-if="searchQuery.isLoading">加载中...</div>
    <ul v-else>
      <li v-for="result in searchQuery.results" :key="result.id">
        {{ result.title }}
      </li>
    </ul>
  </div>
</template>

<script setup>
function useSearchRef(initialValue = '') {
  let value = initialValue
  let results = []
  let isLoading = false
  
  const performSearch = async (query) => {
    if (!query) {
      results = []
      return
    }
    
    isLoading = true
    try {
      const response = await fetch(`/api/search?q=${query}`)
      results = await response.json()
    } catch (error) {
      console.error('Search failed:', error)
      results = []
    } finally {
      isLoading = false
    }
  }
  
  return customRef((track, trigger) => {
    return {
      get() {
        track()
        return {
          value,
          results,
          isLoading
        }
      },
      set(newValue) {
        value = newValue
        performSearch(newValue).then(() => trigger())
      }
    }
  })
}

const searchQuery = useSearchRef()
</script>

5. 最佳实践

5.1 性能优化

// 避免不必要的触发
function useOptimizedRef(value) {
  return customRef((track, trigger) => {
    return {
      get() {
        track()
        return value
      },
      set(newValue) {
        // 只在值真正改变时触发更新
        if (value !== newValue) {
          value = newValue
          trigger()
        }
      }
    }
  })
}

5.2 错误处理

function useSafeRef(value, errorHandler = console.error) {
  return customRef((track, trigger) => {
    return {
      get() {
        try {
          track()
          return value
        } catch (error) {
          errorHandler(error)
          return null
        }
      },
      set(newValue) {
        try {
          value = newValue
          trigger()
        } catch (error) {
          errorHandler(error)
        }
      }
    }
  })
}

6. 注意事项

  1. 避免过度使用
// ❌ 不要为简单的值使用 customRef
const simpleValue = customRef((track, trigger) => ({
  get() {
    track()
    return value
  },
  set(newValue) {
    value = newValue
    trigger()
  }
}))

// ✅ 使用普通的 ref
const simpleValue = ref(value)
  1. 保持响应性
// 确保在需要的时候调用 track 和 trigger
function useCustomRef(value) {
  return customRef((track, trigger) => ({
    get() {
      track() // 不要忘记 track
      return value
    },
    set(newValue) {
      value = newValue
      trigger() // 不要忘记 trigger
    }
  }))
}
  1. 内存管理
// 清理副作用
function useCustomRef(value) {
  let cleanup = null
  
  return customRef((track, trigger) => ({
    get() {
      track()
      return value
    },
    set(newValue) {
      // 清理之前的副作用
      if (cleanup) {
        cleanup()
      }
      
      value = newValue
      // 设置新的副作用
      cleanup = setupSideEffect(value)
      trigger()
    }
  }))
}

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

相关文章:

  • BLE透传方案,IoT短距无线通信的“中坚力量”
  • 可以称之为“yyds”的物联网开源框架有哪几个?
  • 深度学习项目--基于LSTM的糖尿病预测探究(pytorch实现)
  • C++ list 容器用法
  • @RabbitListener处理重试机制完成后的异常捕获
  • Android - 通过Logcat Manager简单获取Android手机的Log
  • LeetCode题练习与总结:两个字符串的删除操作--583
  • 9.4 GPT Action 开发实践:从设计到实现的实战指南
  • PoolingHttpClient试验
  • 独立游戏开发赚钱吗?
  • 从0到1:C++ 开启游戏开发奇幻之旅(一)
  • 重构(1)if-else
  • webview_flutter_android 4.3.0使用
  • java 字符串日期字段格式化前端显示
  • 并发操作下如何加锁,自动释放锁,异常情况可以主动释放锁
  • gitee——报错修改本地密码
  • 51单片机开发:独立键盘实验
  • 数据结构:log-structed结构MemTableSSTable
  • 代码工艺:实践 Spring Boot TDD 测试驱动开发
  • C#常考随笔2:函数中多次使用string的+=处理,为什么会产生大量内存垃圾(垃圾碎片),有什么好的方法可以解决?
  • SocketCAN
  • WebSocket 心跳机制:确保连接稳定与实时性
  • 【Rust自学】15.5. Rc<T>:引用计数智能指针与共享所有权
  • ubuntu 更新24LTS中断导致“系统出错且无法恢复,请联系系统管理员”
  • 【MySQL】--- 复合查询 内外连接
  • 使用scikit-learn中的KNN包实现对鸢尾花数据集或者自定义数据集的的预测