Template Refs

  • 定义和使用

    使用 Composition API 时, reactive refstemplate refs 的概念是统一的。
    为了获得对模板中元素或组件实例的引用,我们可以像往常一样声明 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 在 render 上下文中进行公开,并将其绑定到 div 作为其 ref="root"。在虚拟 DOM 修补算法中,如果 VNode 的 ref 键对应于渲染上下文上的 ref,则将 VNode 的对应元素或组件实例分配给该 ref 的值。这是在虚拟 DOM 挂载/修补过程中执行的,因此模板引用将仅在初始渲染后获得分配的值。
    用作模板 ref 的 ref 的行为与其他任何 ref 一样:它们是 reactive 响应式的,可以传递到组合函数中(或从中返回)。
  • JSX的用法

    export default {
      setup() {
        const root = ref(null)
    
        return () =>
          h('div', {
            ref: root
          })
    
        // with JSX
        return () => <div ref={root} />
      }
    }
    
  • v-for 内部用法

    在内部使用时,Composition API 模板引用没有特殊处理 v-for。而是使用函数 ref 来执行自定义处理:
    <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([])
    
          // make sure to reset the refs before each update
          onBeforeUpdate(() => {
            divs.value = []
          })
    
          return {
            list,
            divs
          }
        }
      }
    </script>
    
  • watch 模板 ref

    watch 模板 ref 是否发生更改可以替代使用前面的示例中演示的生命周期挂钩。
    但是生命周期挂钩的主要区别在于 watch(),watchEffect() 效果是在安装或更新 DOM 之前运行的,因此当观察者运行效果时,模板 ref 尚未更新:
    <template>
      <div ref="root">
        This is a root element
      </div>
    </template>
    
    <script>
    import { ref, watchEffect } from 'vue'
    
    export default {
      setup() {
        const root = ref(null)
        watchEffect(() => {
          //此效果会在DOM更新之前运行,因此,
          //模板ref尚不包含对该元素的引用。
          console.log(root.value) // => null
        })
    
        return {
          root
        }
      }
    }
    </script>
    
    因此,应该使用 flush:'post' 选项定义使用模板引用的观察者;这将在DOM更新后运行效果,并确保 模板ref 与 DOM 保持同步并引用正确的元素。
    <template>
      <div ref="root">
        This is a root element
      </div>
    </template>
    
    <script>
    import { ref, watchEffect } from 'vue'
    
      export default {
        setup() {
          const root = ref(null)
    
          watchEffect(() => {
            console.log(root.value) // => <div></div>
          }, 
          {
            flush: 'post'
          })
    
          return {
            root
          }
        }
      }
    </script>