zoukankan      html  css  js  c++  java
  • 浅析setup如何通过ref获取子组件实例中的DOM结构/数据/方法及获取子组件实例数据都是空的处理(defineExpose API 的使用)、Vue3模板引用refs、在组合式API中使用template refs、for循环中如何获取及重置refs、如何监听模板引用

    一、通过 ref 获取子组件实例中的DOM结构数据及方法

      setup 怎么获取子组件的 ref ?在 Vue3 中,如果要在父组件拿到子组件(子组件的DOM结构、数据、方法),可以通过 ref。即在父组件中定义响应式数据 ref(null) ,并绑定给子组件,在需要的时候,通过定义的响应式变量即可获取。获取后,即获取了子组件的一切,可以看到子组件的DOM结构,也可以看到子组件中对外暴露的的数据和方法,并且可以直接调用。

    <ExpertFormVue ref="ExpertRef"></ExpertFormVue>
    const ExpertRef = ref() // 通过 ref 绑定子组件
    function getSonComponent () { // 通过 ref 获取子组件
        // 获取子组件的数据
        console.log(ExpertRef.value)
        console.log(ExpertRef.value.msg)
    }

      这里面涉及 2 个问题:

    1、如何通过 ref 获取子组件实例

      ref 用法1:响应式数据。ref 用法2:模板ref,获取dom元素节点(重点

    1const a = refnull);
    2、在template中定义ref
    <div ref='a'>*********</div>
    3、setup中获取对应节点【在onMounted里】;
    4、将a return出去;

      但是这时候你可能会发现,你无法获取这个节点,这是什么原因呢?其实还是生命周期的问题,结合官方文档可知:原来的created没有了,setup充当了原来的created。

      所以在setup的时候,dom元素还没有被创建,一切都处于混沌状态,只有setup完毕了HTML才能完整构建,才能真正访问到value值,所以自然无法获取到dom节点,要想解决这个问题,就要配合钩子函数 onMounted ,在dom挂载完毕后再进行获取

    2、为什么获取子组件实例中的数据都是空?  ——  defineExpose API 的使用

      问题:开始的时候,我像上面写的,怎么都拿不到子组件实例的数据和方法等。

      原因:其实写法是对的,只不过拿到的引用是空的。子组件需要明确使用 expose 方法暴露出接口之后,才能在父组件获取到接口引用。未暴露接口的情况下,引用的始终是一个空对象。

    // 子组件导出方法,才能在父组件拿到子组件的实例里使用
    defineExpose({
      initExp
    })

      在 vue3.x 的 setup 语法糖中定义的变量默认不会暴露出去,这时使用 definExpose({ })来暴露组件内部属性给父组件使用,在父组件中直接修改子组件的属性,子组件也会相应更新。

    // 子组件
    <script setup>
        let aaa = ref("aaa")
        defineExpose({ aaa });
    </script>
    
    // 父组件
    <Chlid ref="child"></Chlid>
    <script setup>
        let child = ref(null)
        child.value.aaa //获取子组件的aaa
    </script>

    二、官网:模板引用

      尽管存在 prop 和事件,但有时你可能仍然需要直接访问 JavaScript 中的子组件。为此,可以使用 ref attribute 为子组件或 HTML 元素指定引用 ID。例如:

    <input ref="input" />

      例如,你希望在组件挂载时,以编程的方式 focus 到这个 input 上,这可能有用(可以看到直接使用 this.$refs.input 就获取到了 input Dom)

    const app = Vue.createApp({})
    app.component('base-input', {
      template: `
        <input ref="input" />
      `,
      methods: {
        focusInput() {
          this.$refs.input.focus()
        }
      },
      mounted() {
        this.focusInput()
      }
    })

      此外,还可以向组件本身添加另一个 ref,并使用它从父组件触发 focusInput 事件:

    <base-input ref="usernameInput"></base-input>
    
    this.$refs.usernameInput.focusInput()

      需要注意的是:$refs 只会在组件渲染完成之后生效(也就是生命周期 onMounted 之后)。这仅作为一个用于直接操作子元素的“逃生舱”——你应该避免在模板或计算属性中访问 $refs

    三、官网:在组合式 API 中使用 template refs

      在使用组合式 API 时,响应式引用模板引用的概念是统一的。为了获得对模板内元素或组件实例的引用,我们可以像往常一样声明 ref 并从 setup() 返回:

    <template> 
      <div ref="root">This is a root element</div>
    </template>
    <script>
      import { ref, onMounted } from 'vue'
      export default {
        setup() {
          const root = ref(null)
          onMounted(() => {
            // DOM 元素将在初始渲染后分配给 ref
            console.log(root.value) // <div>This is a root element</div>
          })
          return {
            root
          }
        }
      }
    </script>

      这里我们在渲染上下文中暴露 root,并通过 ref="root",将其绑定到 div 作为其 ref。在虚拟 DOM 补丁算法中,如果 VNode 的 ref 键对应于渲染上下文中的 ref,则 VNode 的相应元素或组件实例将被分配给该 ref 的值这是在虚拟 DOM 挂载/打补丁过程中执行的,因此模板引用只会在初始渲染之后获得赋值

      作为模板使用的 ref 的行为与任何其他 ref 一样:它们是响应式的,可以传递到 (或从中返回) 复合函数中

    1、v-for 中的用法(for循环中如何获取及重置refs)

      组合式 API 模板引用在 v-for 内部使用时没有特殊处理。相反,请使用函数引用执行自定义处理

    <template>
      <div v-for="(item, i) in list" :ref="el => { if (el) divs[i] = el }">
        {{ item }}
      </div>
    </template>
    <script>
      import { ref, reactive, onBeforeUpdate } from 'vue'
      export default {
        setup() {
          const list = reactive([1, 2, 3])
          const divs = ref([])
          // 确保在每次更新之前重置ref
          onBeforeUpdate(() => {
            divs.value = []
          })
          return {
            list,
            divs
          }
        }
      }
    </script>

    2、侦听模板引用:侦听模板引用的变更可以替代前面例子中演示使用的生命周期钩子。

      但与生命周期钩子的一个关键区别是,watch()watchEffect() 在 DOM 挂载或更新之前运行副作用,所以当侦听器运行时,模板引用还未被更新

    const root = ref(null)
    watchEffect(() => {
      // 这个副作用在 DOM 更新之前运行,因此,模板引用还没有持有对元素的引用。
      console.log(root.value) // => null
    })

      因此,使用模板引用的侦听器应该用 flush: 'post' 选项来定义,这将在 DOM 更新运行副作用,确保模板引用与 DOM 保持同步,并引用正确的元素

    watchEffect(() => {
      console.log(root.value) // => <div>This is a root element</div>
      }, 
      {
        flush: 'post'
      })
  • 相关阅读:
    shell函数使用
    laravel调试神器tinker
    laravel 5.1 单元测试 Cannot modify header information 错误
    angular 使用rxjs 监听同级兄弟组件数据变化
    angular 有关侦测组件变化的 ChangeDetectorRef 对象
    XML文件操作类--创建XML文件
    (收藏)C#实现截屏
    (转)C#操作PPT
    (转).NET代码混淆实践
    (整理)RPC
  • 原文地址:https://www.cnblogs.com/goloving/p/15514097.html
Copyright © 2011-2022 走看看