Vue3+TS ref的使用

文章目录


前言

对ref的概念一直有点模糊, 整理一下吧.


一、获取DOM

最常见的用法.

1.获取domElement

以Composition API 写法为例.

引入ref, 在想要获取的DOM元素上定义ref属性赋值一个自定义名称, 然后在setup内声明一个同名变量来存储该DOM元素, 具体如下.

但并不能在setup中直接获取到DOM, setup相当于Vue2中的beforeCreate和create周期, 所以你看到我引入了onMounted.

typescript 复制代码
<div ref="diva"></div>
<img ref="imga"></img>
<canvas ref="canvasa"></canvas>
typescript 复制代码
import { defineComponent, ref, onMounted } from 'vue';

export default defineComponent({
  name: 'test',
  setup() {
    const diva = ref<HTMLDivElement | null>();
    const imga = ref<HTMLImageElement | null>();
    const canvasa = ref<HTMLCanvasElement | null>();
    onMounted(() => {
      console.log(diva);
      console.log(imga);
      console.log(canvasa);
    })
  }
})

然后一定要把ref给return出去, 不然怎么搞都是undefined.

如果获取到的元素具备子元素, 子元素也会被一同获取, 所以某些时候可以通过获取父元素的方式来获取子元素, 比如ul内有一堆li, 当然是获取ul再遍历比较好.

关于获取到的DOM如何定义类型, 可以从node_modules/typescript/lib/lib.dom.ts找到该接口:

typescript 复制代码
interface HTMLElementTagNameMap {
    "a": HTMLAnchorElement;
    "abbr": HTMLElement;
    "address": HTMLElement;
    "area": HTMLAreaElement;
    "article": HTMLElement;
    "aside": HTMLElement;
    "audio": HTMLAudioElement;
    "b": HTMLElement;
    "base": HTMLBaseElement;
    "bdi": HTMLElement;
    "bdo": HTMLElement;
    "blockquote": HTMLQuoteElement;
    "body": HTMLBodyElement;
    "br": HTMLBRElement;
    "button": HTMLButtonElement;
    "canvas": HTMLCanvasElement;
    "caption": HTMLTableCaptionElement;
    "cite": HTMLElement;
    "code": HTMLElement;
    "col": HTMLTableColElement;
    "colgroup": HTMLTableColElement;
    "data": HTMLDataElement;
    "datalist": HTMLDataListElement;
    "dd": HTMLElement;
    "del": HTMLModElement;
    "details": HTMLDetailsElement;
    "dfn": HTMLElement;
    "dialog": HTMLDialogElement;
    "dir": HTMLDirectoryElement;
    "div": HTMLDivElement;
    "dl": HTMLDListElement;
    "dt": HTMLElement;
    "em": HTMLElement;
    "embed": HTMLEmbedElement;
    "fieldset": HTMLFieldSetElement;
    "figcaption": HTMLElement;
    "figure": HTMLElement;
    "font": HTMLFontElement;
    "footer": HTMLElement;
    "form": HTMLFormElement;
    "frame": HTMLFrameElement;
    "frameset": HTMLFrameSetElement;
    "h1": HTMLHeadingElement;
    "h2": HTMLHeadingElement;
    "h3": HTMLHeadingElement;
    "h4": HTMLHeadingElement;
    "h5": HTMLHeadingElement;
    "h6": HTMLHeadingElement;
    "head": HTMLHeadElement;
    "header": HTMLElement;
    "hgroup": HTMLElement;
    "hr": HTMLHRElement;
    "html": HTMLHtmlElement;
    "i": HTMLElement;
    "iframe": HTMLIFrameElement;
    "img": HTMLImageElement;
    "input": HTMLInputElement;
    "ins": HTMLModElement;
    "kbd": HTMLElement;
    "label": HTMLLabelElement;
    "legend": HTMLLegendElement;
    "li": HTMLLIElement;
    "link": HTMLLinkElement;
    "main": HTMLElement;
    "map": HTMLMapElement;
    "mark": HTMLElement;
    "marquee": HTMLMarqueeElement;
    "menu": HTMLMenuElement;
    "meta": HTMLMetaElement;
    "meter": HTMLMeterElement;
    "nav": HTMLElement;
    "noscript": HTMLElement;
    "object": HTMLObjectElement;
    "ol": HTMLOListElement;
    "optgroup": HTMLOptGroupElement;
    "option": HTMLOptionElement;
    "output": HTMLOutputElement;
    "p": HTMLParagraphElement;
    "param": HTMLParamElement;
    "picture": HTMLPictureElement;
    "pre": HTMLPreElement;
    "progress": HTMLProgressElement;
    "q": HTMLQuoteElement;
    "rp": HTMLElement;
    "rt": HTMLElement;
    "ruby": HTMLElement;
    "s": HTMLElement;
    "samp": HTMLElement;
    "script": HTMLScriptElement;
    "section": HTMLElement;
    "select": HTMLSelectElement;
    "slot": HTMLSlotElement;
    "small": HTMLElement;
    "source": HTMLSourceElement;
    "span": HTMLSpanElement;
    "strong": HTMLElement;
    "style": HTMLStyleElement;
    "sub": HTMLElement;
    "summary": HTMLElement;
    "sup": HTMLElement;
    "table": HTMLTableElement;
    "tbody": HTMLTableSectionElement;
    "td": HTMLTableCellElement;
    "template": HTMLTemplateElement;
    "textarea": HTMLTextAreaElement;
    "tfoot": HTMLTableSectionElement;
    "th": HTMLTableCellElement;
    "thead": HTMLTableSectionElement;
    "time": HTMLTimeElement;
    "title": HTMLTitleElement;
    "tr": HTMLTableRowElement;
    "track": HTMLTrackElement;
    "u": HTMLElement;
    "ul": HTMLUListElement;
    "var": HTMLElement;
    "video": HTMLVideoElement;
    "wbr": HTMLElement;
}

获取不同的标签就定义为<对应类型 | null>, 在某些周期是null.


2.获取组件

与获取DOM的方法相同, 但类型定义有所区别.
InstanceType<T> 是 ts 自带的类型, 能够直接获取组件完整的实例类型.

typescript 复制代码
const 自定义 = ref<InstanceType <typeof 组件名>>();
typescript 复制代码
import { defineComponent, ref, onMounted } from 'vue';
import Footer from "../components/Footer.vue";

export default defineComponent({
  name: 'test',
  setup() {
    const footer = ref<InstanceType <typeof Footer>>();
    onMounted(() => {
      console.log(footer.value);
    });
  }
})

依然需要在onMounted获取.


3.v-bind与ref

如果用v-bind指令向ref绑定一个函数, 该函数内可从参数获取到DOM:

html 复制代码
<div :ref="setRefAction"></div>
typescript 复制代码
const setRefAction = (el: any) => {
  console.log(el)
};

二、响应式处理

ref() 也可以像 reactive() 一样对值进行响应式处理, 不过ref可以额外接受基础类型的响应式处理, reactive只能接受对象类型.

复制代码
可以将 ref 看成 reactive 的一个变形版本,这是由于 reactive 内部采用 Proxy 来实现,
而 Proxy 只接受对象作为入参,这才有了 ref 来解决值类型的数据响应,如果传入 ref 的是一个对象,
内部也会调用 reactive 方法进行深层响应转换.

看来对象类型用ref做响应式处理会产生额外的工作量.


ref可以接受非对象类型:

typescript 复制代码
const state = ref<Boolean>(true);

但就像访问获取到的DOM一样, 访问ref值需要xxx.value.

接收对象类型时会返回Proxy对象, 因为这还是reactive的处理结果, 尝试直接从这个proxy对象中访问属性会导致TS报错:

typescript 复制代码
const obj = ref<Object>({ a: 1, b: 2}); // Proxy<Object>{ a: 1, b: 2 };
console.log(obj.value.a)

"obj.value中不含有该属性", 但其实是有的, 将obj的类型断言为any可以正常访问.


总结

--

相关推荐
ai_xiaogui4 分钟前
PanelAI前端全面升级!私有化部署AI面板控制台+生态市场一键管理详解
前端·人工智能·comfyui一键部署·生态市场算力共享·ai面板控制台·panelai私有化部署·大模型前端管理
Jelena157795857926 分钟前
1688.item_get_app接口:包装尺寸重量信息深度解析
开发语言·前端·python
酉鬼女又兒14 分钟前
零基础快速入门前端DOM核心知识点详解与蓝桥杯Web赛道备考指南(可用于备赛蓝桥杯Web应用开发)
前端·职场和发展·蓝桥杯
daols8820 分钟前
vue甘特图vxe-gantt实现点击任务条弹出编辑表单
前端·vue.js·甘特图·vxe-gantt
Fairy要carry23 分钟前
项目05-手搓Agent之任务通信+任务编排的实现
服务器·前端·网络
忘忧记23 分钟前
pytest进阶参数化用法
前端·python·pytest
予你@。36 分钟前
vue 使用html2canvas + jsPDF 将html导出为pdf (延伸问题)
vue.js·pdf·html
github_czy38 分钟前
FastAPI 流式响应核心原理解析(含前端断开感知)
前端·fastapi
Lana学习中39 分钟前
[AI编程]纯前端JS实现评论区自动截图&生成 PDF
前端·javascript·pdf·vibe coding
鹏程十八少41 分钟前
7. Android Shadow插件化原理深挖(下):Transform字节码插桩与“零Hook”的底层实现与宿主通信全流程
android·前端·面试