uni-app 组件标志全解析:id、ref、Element、NodeRef、context 与 ComponentPublicInstance 实战指南
2026/9/19 10:36:38 网站建设 项目流程
  • 示例工程
  • 前端
  • 移动开发
  • 跨平台

【免费下载链接】uni-app

A cross-platform framework using Vue.js

项目地址:https://gitcode.com/gh_mirrors/un/uni-app
点击查看免费下载

导读:在 uni-app(含 uni-app x)中,idrefElementNodeRefcontextComponentPublicInstance这六类概念都与「组件标志」有关——它们的本质都是给组件打一个标记,再通过标记拿到组件的上下文对象,进而调用对象上的方法与属性。本文以video组件为贯穿示例,逐一拆解这些概念在 Web、小程序、uni-app x 三种环境下的来源、差异与正确用法,并结合仓库中 idref.md、UniElement 文档、getElementById API 等一手资料,帮助读者在实际开发中选对获取组件的方式、避开类型断言崩溃与多端不兼容等典型坑。

一、概念总览:六个概念,一个目的

开发者会遇到很多与组件标志有关的概念:idrefElementNodeRefcontextComponentPublicInstance

上述概念,其实都是为了给组件一个标记,通过标记拿到组件的上下文对象,然后操作这个对象的方法。但因为平台不同,制造了不同的概念:

  • id存在于 Web 和小程序中;
  • NodeRef是小程序的概念(uni.createSelectorQuery()的返回类型);
  • refComponentPublicInstance是 vue 的概念;
  • Element(在 uni-app x 中统一为UniElement)是 DOM 的概念,Web 与 uni-app x 的 App 端都存在。

uni-app 作为跨端框架,上述概念「皆而有之」;uni-app x 更是 Web、小程序、vue 三类概念同时支持。理解它们各自的服务对象(内置组件还是自定义 vue 组件),是正确选择获取方式的前提。

二、Web 端:从 document.getElementById 到 vue 的 ref

内置 HTML 标签:id + document.getElementById

Web 端 html 内置标签有id属性,通过document.getElementById("")可以返回一个Element,进而调用Element的方法。以video为例:

<video id="vid" src="uni.mp4"></video> <script> document.getElementById("vid").play() //js写法 (document.getElementById("vid") as HTMLVideoElement).play(); //ts写法。只有HTMLVideoElement才有play方法 </script>

注意 ts 写法中的as HTMLVideoElementdocument.getElementById的静态返回类型是宽泛的HTMLElement,而play()HTMLVideoElement上的专有方法,必须通过类型断言收窄到具体类型才能调用。这一点在 uni-app x 的 uts 中同样成立(见后文as UniVideoElement)。

vue 自定义组件:ref + this.$refs 获取组件实例

vue 框架中组件有ref属性。选项式中通过this.$refs可以返回一个组件实例ComponentPublicInstance;组合式则是定义一个与模板 ref 同名的ref()变量。得到组件实例后可以进一步调用组件的属性和方法。

对于 html 内置元素,在 Web 中一般不通过 ref 获取——ref 更多是服务于自定义的 vue 组件。

假使有一个 vue 组件,其有一个方法getSome(),那么用法是:

template 区:

<component1 ref="c1"></component1>

script 区:

this.$refs.c1.getSome() //js写法 this.$refs["c1"].getSome() //与上一行等价 (this.$refs["c1"] as ComponentPublicInstance).getSome() //ts或uts写法,必须明确类型才能调用其方法和属性

第三种写法强调了 uts 的规则:必须显式标注类型(as ComponentPublicInstance),编译器才能确认getSome()方法存在。

uni-app x 中 ref 获取内置组件的特殊行为

在 uni-app x 中,ref获取内置组件时,App 和 Web 平台得到的是UniElement(而非 vue 组件实例);只有获取非内置组件(即自定义 vue 组件)时才得到 vue 组件实例。这一点是本主题在 uni-app x 中最重要的认知差异,详见下文「uni-app x」章节。

三、小程序端:createXXContext 与 createSelectorQuery

内置组件:id + uni.createXXContext

小程序的内置组件有id属性,通过uni.createXXXContext系列 API 可以获取组件上下文对象,例如uni.createVideoContext()

template 区:

<video id="vid" src="uni.mp4"></video>

script 区:

uni.createVideoContext('vid').play()

以仓库中的 createVideoContext 文档 为证,该方法返回VideoContext对象,支持playpauseseekstopsendDanmuplaybackRaterequestFullScreenexitFullScreen等方法,Web、微信小程序、Android、iOS、HarmonyOS 均可用(微信小程序 4.41 起)。它还有一个可选第二参数component:当页面/组件中id重复、或需要定位到特定页面的 video 时,可传入ComponentPublicInstance缩小查找范围,例如选项式中uni.createVideoContext("video1", this),组合式中uni.createVideoContext("video1", getCurrentInstance()!.proxy!)

createSelectorQuery + NodesRef.context 异步获取

小程序还提供第二种方式:uni.createSelectorQuery()传入选择器,拿到返回的NodesRef,再通过.context()异步获取组件的上下文对象:

uni.createSelectorQuery().select('.the-video-class').context(function(res){ console.log(res.context) // 节点对应的 Context 对象。如:选中的节点是 <video> 组件,那么此处即返回 VideoContext 对象 }).exec()

createSelectorQuery本质是小程序的 API,源于小程序未开放 DOM、且视图层与逻辑层分离,于是提供一个异步 API 让逻辑层有限地获取 DOM 能力。在仓库的 createSelectorQuery 文档 中可以看到其限制:selector 仅支持#the-id.a-class两种语法;fields参数中的context字段在 Android、iOS 平台为x(不支持)。

两种方式的取舍

一般推荐使用第 1 种方法(createXXContext)。这种方式简洁并且可以跨平台;而createSelectorQuery().context()这类写法不跨平台,跨平台获取组件 context 应使用uni.createXXContext()

vue 自定义组件

uni-app 编译到小程序时,vue 组件的用法与 Web 相同,即ref+this.$refs获取ComponentPublicInstance,可回看上文 vue 自定义组件章节。

四、uni-app x:三类概念的融合

uni-app x 中,Web、小程序、vue 这 3 类概念都支持,所以idrefElementNodeRefcontextComponentPublicInstance这些概念都存在,并按「内置组件 / vue 自定义组件」分为两大类。

内置组件的获取方式矩阵

| 获取方式 | API / 写法 | 特点 | 适用组件 | | :- | :- | :- | :- | | Element 方式 |uni.getElementById()| 获取页面栈栈顶页面(不含 dialogPage)的元素 | 所有内置组件 | | Element 方式 |UniPage.getElementById()/this.$page.getElementById| 与指定页面绑定 | 所有内置组件 | | Element 方式 |this.$refs['vid'] as UniElement| 与调用页面绑定 | 所有内置组件 | | context 方式 |uni.createVideoContext()等 createXXContext | 为对齐小程序 API,仅部分组件提供 | video 等 | | NodeRef 方式 |uni.createSelectorQuery()| 只能拿到 NodesRef,无法取到 context| — |

要点:内置组件都支持 Element。为了与小程序 api 拉齐,部分组件同时支持 context,如video;不涉及小程序 api 拉齐的组件未提供 context,比如<unicloud-db>组件只有 Element。

Element 方式:uni.getElementById

uni-app x 提供了 uni.getElementById 获取UniElement类型。UniElement是所有组件的 DOM 元素对象基类,通用的元素操作方法(如getAttributesetStyle)在Element上就可以操作。

UniVideoElement继承自UniElement,拥有 video 专用的一批方法(playpauseseekstopsendDanmuplaybackRaterequestFullScreenexitFullScreen)。

template 区:

<video id="vid" src="uni.mp4"></video>

script 区:

(uni.getElementById("vid") as UniVideoElement).play() //注意没有document对象,getElementById方法在uni下。务必确保vid存在,否则as会崩溃 uni.getElementById<UniVideoElement>("vid")!.play() //泛型写法

两个关键点(仓库文档均有明确说明):

  1. 没有document对象:uni-app x 中getElementById挂在uni全局对象下,而不是document上;
  2. 务必确保 id 存在uni.getElementById在元素不存在时返回null,若直接as UniVideoElement强转再调用方法会崩溃。推荐使用3.93+支持的泛型写法uni.getElementById<UniVideoElement>("vid")(配合!非空断言),或先判空再调用。泛型写法对「组件自带方法」的场景尤其有用,例如unicloud-db组件可通过泛型指定其专属元素类型后调用专用方法。

另一个容易踩坑的语义:uni.getElementById获取的是**页面栈栈顶(不包括 dialogPage)**的页面元素,而不是执行本方法代码所在的页面的元素。如果 A 页面被栈顶的 B 页面盖住,在 A 页面执行uni.getElementById会访问到 B 页面的元素。在 get-element-by-id.md 的示例中,navigateTo跳转后,通过ref仍能取到本页元素,而uni.getElementById('text')已取不到——正是这个原因。

Element 方式:UniPage.getElementById 与页面绑定

UniPage 的 getElementById 用于获取指定页面的元素。通过this.$page.getElementById可以获取当前页面的元素:

// 选项式 API const curPage = this.$page // 组合式 API const currentInstance = getCurrentInstance() const curPage = currentInstance?.proxy?.$page curPage.getElementById('vid') // 获取当前页面内的元素

组合式 API 中getCurrentInstance()?.proxy?.$page这种方式可以获取到 dialogPage 页面,因而「在当前页面获取 UniElement」的通用写法是getCurrentInstance()?.proxy?.$page.getElementById。在仓库的 get-current-pages.md 示例代码中,checkGetElementById正是通过page.getElementById('check-get-element-by-id-btn')验证当前页面元素获取,并通过element.getPage()校验元素所属页面。

Element 方式:this.$refs as Element

this.$refs获取到的内置组件,通过as也可以转换为 Element。与uni.getElementById相比,this.$refs方式与调用页面绑定,不受页面栈顶变化影响:

script 区:

(this.$refs['vid'] as UniVideoElement).play(); //但一般ref用于vue自定义组件

获取 Element 的三种途径小结(原文档原文要点):

  1. uni.getElementById获取栈顶页面的元素(注意无法获取 dialogPage 页面的元素);
  2. UniPagegetElementById获取指定页面的元素,通过this.$page.getElementById获取当前页面的元素;
  3. 通过this.$refs获取到 vue 实例后as为 Element。

对应地,在 UVUE DOM 文档 中也给出了两条标准路径:设置id后用uni.getElementById获取,或设置ref后用this.$refs获取(需as转换),且均建议在页面onReady之后获取(太早组件可能没有创建),长期使用可保存到 vue 的 data 中。

context 方式:uni.createVideoContext

uni-app x 中同样支持小程序风格的 context 方式:

script 区:

uni.createVideoContext("vid")!.play()

createVideoContext的返回类型为VideoContext,兼容性覆盖 Web 4.0、微信小程序 4.41、Android、iOS 4.11、HarmonyOS 4.61(见 create-video-context.md)。

NodeRef 方式:支持 createSelectorQuery 但拿不到 context

uni-app x 虽然支持uni.createSelectorQuery()API,传入选择器可以拿到返回的NodesRef,但无法继续获取.context子对象,无法通过这种方式拿到 context。

这一点在 create-selector-query.md 中得到了印证:NodesRef.fieldscontext字段在 Android、iOS 平台标注为x(不支持);NodesRef.context(callback)方法注明「uni-app x 暂仅支持获取 EditorContext」,且兼容性表格显示 Web 4.0、微信小程序 4.41 可用,Android/iOS/HarmonyOS 要到 5.04 才支持。也就是说,在 uni-app x 中拿 video 等内置组件的 context,请直接使用uni.createVideoContext

vue 自定义组件:与 Web 保持一致

uni-app x 中 vue 组件的用法与 Web 相同,即ref+this.$refs获取ComponentPublicInstance,再调用组件的属性和方法,具体回看上文对应章节。

五、UniElement 与 UniVideoElement:底层对象能力

在 uni-app x 中,获取到的 Element 统一为UniElement对象(HBuilderX 4.0 起,废弃了此前 3.91 的Element对象和更早的INode对象,见 UVUE DOM 文档)。完整能力见 UniElement 对象文档,这里归纳最常用的部分。

UniElement 常用属性

| 属性 | 类型 | 说明 | | :- | :- | :- | | id | string | 只读,当前元素的标识符 | | isConnected | boolean | 只读,元素是否与 DOM 树连接 | | attributes | Map<string, any> | 只读,元素上所有属性集合 | | classList | Array<string> | 只读,class 属性动态集合 | | dataset | any | 只读,自定义数据属性(data-*)集合;5.21 起全平台调整为 UniDOMStringMap 类型 | | children / firstChild / lastChild / parentElement / nextElementSibling | UniElement 等 | 只读,DOM 树结构关系 | | offsetLeft / offsetTop / offsetWidth / offsetHeight | number | 只读,布局位置与尺寸(逻辑像素) | | style | CSSStyleDeclaration | 只读,样式对象(各端计算规则有差异,App 端包含选择器样式,Web/小程序端仅 style 属性设置的样式) | | scrollWidth / scrollHeight / scrollLeft / scrollTop | number | 滚动相关内容,仅 scroll-view、list-view 等可滚动组件支持 | | tagName | string | 只读,元素标签名 | | uniPage | UniPage | 只读,元素所属页面对象 |

UniElement 常用方法

  • getAttribute(key)/setAttribute(key, value)/hasAttribute(key)/removeAttribute(key):属性的读取与修改。注意 HBuilderX 3.93 起setAttribute只能保存 string 类型值,非 string 数据请走dataset;App 平台不支持用setAttribute设置/获取classstyle(style 请通过element.style操作)。
  • setAnyAttribute(key, value)/getAnyAttribute(key):与上面功能等同,但 value 支持任意类型(Web、微信小程序不支持)。
  • appendChild/insertBefore:元素树操作(仅支持已有元素的移动,目前不支持通过 DOM API 创建和删除 DOM 树中的元素)。
  • getBoundingClientRect():同步获取元素大小及其相对于窗口的位置,返回DOMRect;另有异步版本getBoundingClientRectAsync()
  • getAndroidView()/getAndroidActivity():App-Android 平台获取原生 View / Activity(可配合泛型,如getAndroidView<WebView>()直接拿到安卓底层 WebView 对象;安卓蒸汽模式下需在 uts 插件中执行,且元素渲染后(建议 onReady)才可能拿到非 null)。
  • getDrawableContext():App 端绘制能力(Draw API)的入口,绘制后需调用update()更新到画布,重绘前用reset()清除。

UniVideoElement 专属方法

UniVideoElement继承自UniElement(见 univideoelement.md),在通用能力之上追加了 video 专用方法:play()pause()seek(position)stop()sendDanmu(danmu)playbackRate(rate)(支持倍率 0.5/0.8/1.0/1.25/1.5)、requestFullScreen(direction)(direction 取 0/90/-90)、exitFullScreen()。这正是在 uni-app x 中uni.getElementById<UniVideoElement>("vid")!.play()能直接调用play的原因。

六、实战建议与常见坑

  1. id 要唯一、存在性要先确认uni.getElementById不存在匹配元素时返回null,直接as强转会崩溃。优先使用泛型 + 非空断言(uni.getElementById<UniVideoElement>("vid")!),或在调用前判空。
  2. uni.getElementById只认栈顶页面:它获取的是页面栈栈顶(不含 dialogPage)页面的元素。要拿当前页面(含 dialogPage)的元素,用getCurrentInstance()?.proxy?.$page.getElementByIdthis.$page.getElementByIdthis.$refs方式同样与页面绑定。
  3. 获取时机在 onReady 之后:元素太早可能没有创建(App 端原生 View 也是渲染时才构建)。需要精确的排版后状态时,考虑使用异步接口getBoundingClientRectAsyncuni.createSelectorQuery
  4. uni-app x 中拿 context 认准 createXXContextuni.createSelectorQuery()在 uni-app x 中拿不到.context,不要用它做跨平台 context 获取。
  5. 类型意识:uts/ts 中必须显式类型断言(as UniVideoElementas ComponentPublicInstance)才能调用专有方法;ref 获取内置组件在 App/Web 平台得到的是UniElement,自定义组件才是 vue 实例。
  6. DOM API 的使用边界:日常数据驱动的更新仍应使用 vue 数据绑定;DOM API 主要服务于「跟手动效」(16ms 一帧的渲染要求下跳过 vue diff 直接操作样式)与「Draw API」(Android/iOS 底层高性能绘制)这两类场景。同时注意 uvue 的 template、数据绑定底层本身也调用 DOM API,跳过框架直接操作 DOM 时需留意与 vue 管理的冲突——目前仓库实现上不支持通过 DOM API 创建/删除 DOM 树元素,仅支持获取元素(详见 UVUE DOM 文档)。

七、进一步阅读

  • idref.md(本文主题原始文档)
  • uni.getElementById API
  • UniPage / getCurrentPages
  • UniElement 对象文档(含 UniVideoElement 等各组件专属元素类型)
  • UVUE DOM 总览(含 DOM 使用场景与获取方式示例)
  • uni.createVideoContext
  • uni.createSelectorQuery(NodesRef 与 context 兼容性说明)

此外,仓库的 examples/hello-uvue 示例工程pages/API目录下集中了大量 DOM/Element 操作演示页(如 getElementById、元素属性与样式读写、getBoundingClientRectAsync 等),适合对照本文内容上手验证。

  • 示例工程
  • 前端
  • 移动开发
  • 跨平台

【免费下载链接】uni-app

A cross-platform framework using Vue.js

项目地址:https://gitcode.com/gh_mirrors/un/uni-app
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询