【问题标题】:How to correctly reference HTMLElement in VueJs component如何在 VueJs 组件中正确引用 HTMLElement
【发布时间】: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 而不是组件时,它可以工作。任何提示如何在组件内引用包装的输入?

标签: vue-component vuejs3 ref


【解决方案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 的组件。任何提示如何引用包装的输入?我已经更新了我的问题。
    • 更新答案。
    猜你喜欢
    • 1970-01-01
    • 2016-12-14
    • 2018-02-04
    • 2020-11-20
    • 2017-12-27
    • 2021-06-10
    • 2020-10-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多