You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue选项式API与组合式API中ref行为统一实现方案

Vue组合式API复刻选项式API的单组件激活行为

需求

将Vue选项式API中ref的行为复刻到组合式API,实现点击按钮时仅激活被点击的单个组件/元素。

选项式API实现(单次激活单个组件)

在选项式API中,通过this.$refs.copy绑定DOM元素,能实现每次仅激活当前点击的组件,代码如下:

<template>
  <button ref="copy" class="copy-button">
    <img v-if="state === 'copied'" src="/images/icons/verifiedCopy.svg" />
    <img v-else src="/images/icons/unverifiedCopy.svg" />
  </button>
</template>

<script>
import Clipboard from 'clipboard'
export default {
  data() {
    return {
      state: 'init'
    }
  },
  mounted() {
    const copyCode = new Clipboard(this.$refs.copy, {
      target(trigger) {
        return trigger.parentElement?.previousElementSibling
      }
    })
    copyCode.on('success', (event) => {
      event.clearSelection()
      this.state = 'copied'
      window.setTimeout(() => {
        this.state = 'init'
      }, 2000)
    })
  }
}
</script>

组合式API当前问题(激活所有组件)

当前组合式API代码会导致所有CopyButton组件同时激活,问题代码如下:

<template>
  <button ref="copy" class="copy-button">
    <img v-if="state === 'copied'" src="/images/icons/verifiedCopy.svg" />
    <img v-else src="/images/icons/unverifiedCopy.svg" />
  </button>
</template>

<script setup lang="ts">
import Clipboard from 'clipboard'
import { ref, onMounted } from 'vue'

const state = useState(() => 'init')
const copy = ref<HTMLElement | null>(null)

onMounted(() => {
  setTimeout(() => {
    const copyCode = new Clipboard(copy.value!, {
      target(trigger) {
        return trigger.parentElement?.previousElementSibling!
      }
    })

    copyCode.on('success', (event) => {
      event.clearSelection()
      state.value = 'copied'
      window.setTimeout(() => {
        state.value = 'init'
      }, 2000)
    })
  }, 55)
})
</script>

解决方案

问题核心是误用了全局状态的useState,导致所有组件共享同一个状态值。修正方案如下:

<template>
  <button ref="copy" class="copy-button">
    <img v-if="state === 'copied'" src="/images/icons/verifiedCopy.svg" />
    <img v-else src="/images/icons/unverifiedCopy.svg" />
  </button>
</template>

<script setup lang="ts">
import Clipboard from 'clipboard'
import { ref, onMounted, onUnmounted } from 'vue'

// 用Vue内置ref创建组件独立状态,替代全局的useState
const state = ref<'init' | 'copied'>('init')
const copy = ref<HTMLElement | null>(null)
let copyCode: Clipboard | null = null

onMounted(() => {
  if (copy.value) {
    copyCode = new Clipboard(copy.value, {
      target(trigger) {
        return trigger.parentElement?.previousElementSibling
      }
    })

    copyCode.on('success', (event) => {
      event.clearSelection()
      state.value = 'copied'
      window.setTimeout(() => {
        state.value = 'init'
      }, 2000)
    })
  }
})

// 组件卸载时销毁Clipboard实例,避免内存泄漏
onUnmounted(() => {
  copyCode?.destroy()
})
</script>

关键修正点

  1. 替换状态创建方式:把全局共享的useState换成Vue内置的ref,确保每个组件实例拥有独立的state,点击时只会修改当前组件的状态。
  2. 移除不必要延迟:onMounted执行时ref已经完成DOM绑定,不需要额外的setTimeout初始化Clipboard。
  3. 添加清理逻辑:在onUnmounted中销毁Clipboard实例,避免内存泄漏,这是选项式API实现中缺失的优化。

内容的提问来源于stack exchange,提问作者DAYVSON SILVINO DA SILVA

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 11:50:45