模板引用

@Template Refs
Overview
HTML 原生开发中,经常需要获取元素|节点,并进一步处理;更多信息,请访问 HTML - DOM
早期流行的 JQuery 也是通过操作 DOM 实现
Vue 中更多的是通过数据驱动的方式实现交互,但有时候避免不了仍然需要操作 DOM
通过 ref 标识获取/引用 DOM 节点或组件实例
更多使用,请访问 模板引用 - Template RefsAPI - ref
需引入包 useTemplateRef 再使用;早期版本使用 ref 包
import { useTemplateRef } from 'vue'
Application
焦点管理:当需要自动聚焦到输入框或其它表单元素时
与第三方库交互:例如集成某些需要直接 DOM 节点的 JavaScript 库(如Chart.js)
执行动画:当需要对元素执行复杂的动画效果,而这些效果难以通过 CSS 实现时
文件上传:触发文件选择器等原生控件的操作
测量尺寸:获取元素的宽度、高度等信息用于布局调整
直接获取会失败,应在组件 挂载完毕 onMounted 再获取
使用时,应判断是否为空
该属性并不显示在结构中,F12 检查元素时,不可见
获取普通 DOM 节点
拿到 DOM 节点,就拿到了该元素的所有信息
<script setup>
    import { useTemplateRef, onMounted } from 'vue'
    let dom = useTemplateRef('null')
    onMounted(() => {
        console.log(dom);
        console.log(dom.value);
        console.log(dom.value.innerText);
        console.log('nodes', dom.value.childNodes[0]);
        console.log('children', dom.value.children[0]);
        console.log('classList', dom.value.classList);
        console.log('className', dom.value.className);
        console.log('style', dom.value.style);
        console.log('getAttribute', dom.value.getAttribute('data-ind'));
        console.log('getAttribute', dom.value.getAttribute('style'));
        console.log('getAttribute', dom.value.getAttribute('class'));
    })
</script>

<template>
    <div ref="dom" 
        class="base active"  
        style="color: #f40;padding: 10px;"
        data-ind="100" >
            hi,there 
            <span>88</span>
    </div>
</template>
    
<style scoped></style>
[] 单击获取焦点、自动聚焦
<script setup>
    import { useTemplateRef, onMounted } from 'vue';
    const ipt = useTemplateRef(null)
    const focus = () => ipt.value.focus()
    onMounted(() => {
        ipt.value.focus()
    })
</script>

<template>
    <div class="home">
        <input type="text" ref="ipt">
        <button @click="focus">click for focus</button>
    </div>
</template>
[] 页面/视图滚动到某个位置
[] 页面滚动时,变换顶部导航样式,如添加阴影
为 <header> 指定 ref,在页面加载时使用,并监听页面的滚动事件
页面卸载时,移除事件监听
结构略
import { onMounted, onUnmounted, useTemplateRef } from 'vue'

const header = useTemplateRef(null)

const fn = () => {
  header.value.classList.toggle('fixed', window.scrollY > 100)
}

onMounted(() => {
  window.addEventListener('scroll', fn)
})

onUnmounted(() => {
  window.removeEventListener('scroll', fn)
})
获取组件
返回组件实例
默认情况下,组件内部的属性和方法对外部不公开,所以获取到的 ref 仅仅是一个代理对象,看不到组件的内部细节
组件需要显示的暴露属性或方法 defineExpose
defineExpose 应该写在暴露的对象后面
<template>
    <div @click="getTitle">only for test - {{ title }}</div>
</template>
    
<script setup>
    import { ref } from 'vue';
    let title = ref(18)
    function getTitle() {
        console.log(title.value);
    }
    defineExpose({
        title,
        getTitle
    })
</script>
refs can be useful in certain situations where direct access to a child component's internal state or methods is necessary.
尽量避免通过 ref 引用组件,请组件的通信机制实现 props 和emit,详见后续组件内容
Remember that accessing a child component's data directly using refs can make your components tightly coupled. Consider using props and events for communication between components
[] 自定义搜索组件,单击时,显示并自动获取焦点
搜索组件参考设计如下
<script setup>
    import { ref, onMounted } from 'vue';
    let sql = ref(null)
    onMounted(() => {
        sql.value.focus()
    })
</script>

<template>
    <input type="text" ref="sql">
</template>
父组件设计如下
<script setup>
    import { ref } from 'vue';
    import Search from './components/Search.vue'
    let isShow = ref(false)
    const showBox = () => {
        isShow.value = true
    }
</script>

<template>
    <button @click="showBox">click</button>
    <Search v-if="isShow"></Search>
</template>
[] 列表渲染
思路同事件代理
[] 单击打开弹窗;更多信息,请访问 实操 - Modal
父组件获取子组件|弹窗组件实例,执行弹窗的方法
子组件|弹窗组件定义方法并暴露;方法利用 bool 将弹窗组件的隐藏状态变为显示
多ref
列表渲染中指定 ref,将获取多个元素
必须声明为数组 [] 形式,否则无法遍历
<div v-for="(item,index) in 10" ref="navRef">{{item}}</div>
const navRef = ref([])
onMounted(() => {
    navRef.value.forEach(item => {
    console.log(item.innerHTML);
    })
})
不建议每个子节点都标注 ref 属性
建议获取父节点,再根据节点关系获取子节点
Summary
用于获取 DOM 节点
需引入 ref
单个元素声明为 null;多个元素声明为 []
使用时,应判定元素是否就绪