- 示例工程
- 前端
- 移动开发
- 跨平台
【免费下载链接】uni-app
A cross-platform framework using Vue.js
导读:在 uni-app(含 uni-app x)中,
id、ref、Element、NodeRef、context、ComponentPublicInstance这六类概念都与「组件标志」有关——它们的本质都是给组件打一个标记,再通过标记拿到组件的上下文对象,进而调用对象上的方法与属性。本文以video组件为贯穿示例,逐一拆解这些概念在 Web、小程序、uni-app x 三种环境下的来源、差异与正确用法,并结合仓库中 idref.md、UniElement 文档、getElementById API 等一手资料,帮助读者在实际开发中选对获取组件的方式、避开类型断言崩溃与多端不兼容等典型坑。
一、概念总览:六个概念,一个目的
开发者会遇到很多与组件标志有关的概念:id、ref、Element、NodeRef、context、ComponentPublicInstance。
上述概念,其实都是为了给组件一个标记,通过标记拿到组件的上下文对象,然后操作这个对象的方法。但因为平台不同,制造了不同的概念:
id存在于 Web 和小程序中;NodeRef是小程序的概念(uni.createSelectorQuery()的返回类型);ref和ComponentPublicInstance是 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 HTMLVideoElement:document.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对象,支持play、pause、seek、stop、sendDanmu、playbackRate、requestFullScreen、exitFullScreen等方法,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 类概念都支持,所以id、ref、Element、NodeRef、context、ComponentPublicInstance这些概念都存在,并按「内置组件 / 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 元素对象基类,通用的元素操作方法(如getAttribute、setStyle)在Element上就可以操作。
UniVideoElement继承自UniElement,拥有 video 专用的一批方法(play、pause、seek、stop、sendDanmu、playbackRate、requestFullScreen、exitFullScreen)。
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() //泛型写法两个关键点(仓库文档均有明确说明):
- 没有
document对象:uni-app x 中getElementById挂在uni全局对象下,而不是document上; - 务必确保 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 的三种途径小结(原文档原文要点):
uni.getElementById获取栈顶页面的元素(注意无法获取 dialogPage 页面的元素);UniPage的getElementById获取指定页面的元素,通过this.$page.getElementById获取当前页面的元素;- 通过
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.fields的context字段在 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设置/获取class、style(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的原因。
六、实战建议与常见坑
- id 要唯一、存在性要先确认:
uni.getElementById不存在匹配元素时返回null,直接as强转会崩溃。优先使用泛型 + 非空断言(uni.getElementById<UniVideoElement>("vid")!),或在调用前判空。 uni.getElementById只认栈顶页面:它获取的是页面栈栈顶(不含 dialogPage)页面的元素。要拿当前页面(含 dialogPage)的元素,用getCurrentInstance()?.proxy?.$page.getElementById或this.$page.getElementById;this.$refs方式同样与页面绑定。- 获取时机在 onReady 之后:元素太早可能没有创建(App 端原生 View 也是渲染时才构建)。需要精确的排版后状态时,考虑使用异步接口
getBoundingClientRectAsync或uni.createSelectorQuery。 - uni-app x 中拿 context 认准 createXXContext:
uni.createSelectorQuery()在 uni-app x 中拿不到.context,不要用它做跨平台 context 获取。 - 类型意识:uts/ts 中必须显式类型断言(
as UniVideoElement、as ComponentPublicInstance)才能调用专有方法;ref 获取内置组件在 App/Web 平台得到的是UniElement,自定义组件才是 vue 实例。 - 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
相关推荐
uni-app x 组件全局属性与事件完全指南:id、class、data-*、ref、touch/tap 事件体系与 UniEvent 事件类型详解
uni app x 组件全局属性与事件完全指南:id、class、data 、ref、touch/tap 事件体系与 UniEvent 事件类型详解 导读 在
示例工程前端移动开发跨平台uni-app uts插件组件开发:标准模式与uni-app兼容模式双模式实践指南
uni app uts插件组件开发:标准模式与uni app兼容模式双模式实践指南 uts 插件的组件开发(简称 uts组件 )是 uni app 生态中把 A
示例工程前端移动开发跨平台uni-app x 组件体系全景指南:从内置组件到 uni-ui x 的完整分类与实战速查
uni app x 组件体系全景指南:从内置组件到 uni ui x 的完整分类与实战速查 uni app x 是 DCloud 推出的跨平台框架,其组件体系以
示例工程前端移动开发跨平台
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考