如何在 VueJs 组件中正确引用 HTMLElement

Posted

技术标签:

【中文标题】如何在 VueJs 组件中正确引用 HTMLElement【英文标题】:How to correctly reference HTMLElement in VueJs component 【发布时间】:2021-08-01 22:54:06 【问题描述】:

我有一个简单的组件,它包含文本区域。我还有另一个简单的组件,它呈现一个按钮。单击按钮时,我想将焦点设置到文本区域。

这个简化的例子失败了:

    <template>
      <MyCommand @resize="testResize" />
    
      <TextArea ref="refElement" />
    </template>

    <script lang="ts">
    // ...

    export default defineComponent(
      name: 'SimpleComponent',
      setup(props, context) 
        const refElement = ref<htmlElement | null>(null)

        const testResize = () => 
          console.log('resize test')

          if (refElement.value !== null) 
            refElement.value.focus()
          
        

        return 
          refElement,
          testResize,
        
          
    </script>

TextArea 是一个很简单的组件,一些输入规范化,过于简单了:

    <template>
      <textarea v-model.trim="value" />
    </template>

我在控制台中得到“resize test”,所以 testResize 方法正在运行,但 refElement 为空。

【问题讨论】:

可能是TextArea 的问题,您使用的是哪个库?看看如何将 ref 传递给 TextArea @Naren 没有库,只是简单的组件,用于包装 textarea 输入并进行一些输入规范化。你是对的 - 当我尝试使用简单的 textarea 而不是组件时,它可以工作。任何提示如何在组件内引用包装的输入? 【参考方案1】:

当引用组件而不是 HTML 元素时,组件类型应该是 DefineComponent 而不是 HTMLElement

可以通过 $el 属性引用被包裹的元素:


    <template>
      <MyCommand @resize="testResize" />
    
      <TextArea ref="refElement" />
    </template>

    <script lang="ts">
    // ...

    export default defineComponent(
      name: 'SimpleComponent',
      setup(props, context) 
        const refElement = ref<DefineComponent>()

        const testResize = () => 
          console.log('resize test')

          if (refElement.value) 
            refElement.value.$el.focus()
          
        

        return 
          refElement,
          testResize,
        
          
    </script>

我不确定这是否是“最佳实践”,在我看来它是一种 hack。如果有人知道更好的解决方案,请发表评论。

【讨论】:

【参考方案2】:

您的退货中缺少refElement

return 
  testResize, refElement

更新

如果您正在处理一个组件,它会变得有点棘手。虽然您可以使用refElement.value.$el,但我想说这不是一个好主意。这仅在组件具有第一个子组件textarea 时才有效。这将导致一个脆弱的实现,如果你需要在某个时候改变它,它就会崩溃。恕我直言,您最好将 ref 作为道具传递给子组件。这也不是最佳实践,因为您应该向下传递道具并向上发出事件,但这将是实现的相当大的开销。不过,将ref 作为道具传递也有其自身的问题。如果模板中有ref,它会自动从 ref/propxy 转换为一个值。为了解决这个问题,您可以在设置中的函数refElement: () =&gt; refElement 中传递道具(不能这样做模板)。当然,YMMV,但这是我选择的路径。

const app = Vue.createApp(
  setup(props, context) 
    const refElement = Vue.ref(null)

    const testResize = () => 
      if (refElement.value !== null) 
        refElement.value.focus()
      
    

    return 
      testResize,
      refElement: () => refElement
    
  
);


app.component("text-area", 
  template: `<textarea ref="taRef"></textarea></div></div>`,
  props: 
    textarearef: 
      type: Function
    
  ,
  setup(props) 
    const taRef = props.textarearef()
    return 
      taRef
    
  
)

app.mount("#app");
<script src="https://unpkg.com/vue@3.0.3/dist/vue.global.prod.js"></script>

<div id="app">
  <text-area :textarearef="refElement"></text-area>
  <button @click="testResize">?</button>
</div>

【讨论】:

谢谢,你说得对,我也要返回 refElement。不幸的是,这似乎只是问题的一部分——您的示例使用 textarea 输入(效果很好),但我正在尝试引用包含 textarea 的组件。任何提示如何引用包装的输入?我已经更新了我的问题。 更新答案。

以上是关于如何在 VueJs 组件中正确引用 HTMLElement的主要内容,如果未能解决你的问题,请参考以下文章

VueJs 上 $route 监视函数中的组件范围不正确

如何将 vuetify 组件正确地动态传递到 vueJs 组件中

从 VueJS 的父组件中引用子组件的索引

如何使用 Vue 类组件访问 VueJS 3 和 Typescript 中的 HTML 引用?

如何在 VueJS 组件中使用通用脚本

如何切换 VueJs 组件的可见性?