# UVUE DOM

每个uvue页面,在内存中都有一个 DOM(文档对象模型)。它和浏览器的 DOM规范 类似。

DOM 是页面元素内容的结构数据。DOM 模型用一个逻辑树来表示一个页面文档,树的每个分支的终点都是一个节点,每个节点都对应一个节点对象(UniElement)。

实际上 uvue 的template、数据绑定,在底层调用的也是 DOM API。

在浏览器中,开发者一旦跳过vue框架直接操作dom,vue框架将无法管理相应dom,开发者需要注意两端的冲突。

为了减少冲突,目前不支持通过 DOM API 创建和删除 DOM 树中的元素。只支持获取元素UniElement。

DOM在Web、Android、iOS、鸿蒙、微信小程序均存在。微信小程序受微信限制,只实现了部分DOM API,请注意兼容性表格。

HBuilderX4.0 开始DOM元素的类型统一调整为 UniElement 对象。废弃之前 HBuilderX 3.91 的 Element对象 和 HBuilderX 3.91以前的 INode 对象。

# 使用场景

通常情况下,使用 uvue 框架的数据绑定来操作更新页面组件就可以。但有2个场景,需要使用 DOM API。

  1. 跟手动效

    响应触屏事件更新组件的位置,要想不掉帧,需要保证16毫秒绘制一帧。

    uvue的data更新,有一套diff机制,每次触发data更新,会多几毫秒的耗时。

    此时推荐通过 DOM API 跳过 vue 框架直接操作组件的样式。

  2. Draw API

    Android和iOS的原生view,有一些底层的高性能绘制能力,这些API的调用,需要先获取到 UniElement 对象,然后再调用其方法。

性能章节,对这2个场景有详细的阐述。

# DOM元素对象

在操作DOM元素对象前,需要先获取 UniElement 对象,可通过 uni.getElementByIdthis.$refs 获取。

# 通过uni.getElementById获取DOM元素

app-uvue 页面中可以为页面元素节点设置 id 属性,然后通过 uni.getElementById 获取 DOM 元素对象。

但注意这个方法只能获取栈顶页面的element。如需绑定调用的页面,需使用下方的this.$refs

首先需要为组件设置 id 属性值:

<!-- id 属性值为 myView,后续可以通过此值查找 -->
<view id="myView" class="container">
	<text>Hello World</text>
</view>

在页面onReady 后(太早组件可能没有创建),通过 uni.getElementById 获取。如果长期使用,可以保存在vue的 data 中。

组合式 API

选项式 API

const color = ref('red')
const myView = ref<UniElement | null>(null)

onReady(() => {
	myView.value = uni.getElementById('myView')
})

# 通过this.$refs获取DOM元素

uvue页面中可以通过 vue 框架中的组件实例对象 this.$refs 获取 DOM 元素对象。

首先需要为组件设置 ref 属性值,它类似于id:

<!-- ref 属性值为 myView,后续可以通过此值查找 -->
<view ref="myView" class="container">
	<text>Hello World</text>
</view>

在页面onReady 后(太早组件可能没有创建),通过 this.$refs 获取。如果长期使用,可以保存在vue的 data 中。

组合式 API

选项式 API

const color = ref('red')
const myView = ref<UniElement | null>(null)

onReady(() => {
	// 通过模板 ref 获取时,myView.value 即为组件对象
})

# 操作DOM元素对象

获取DOM元素对象Elment后,可通过其属性或方法操作组件,完整API参考UniElement对象文档

如通过UniElement对象的 style 属性更新组件的样式:

this.myView?.style?.setProperty('background-color', 'red');

# 示例

以下是完整的操作示例:

组合式 API

选项式 API

<template>
	<!-- #ifdef APP -->
	<scroll-view style="flex:1;">
	<!-- #endif -->
		<view id="myView" ref="myView" class="container">
			<text>Hello World</text>
		</view>
		<button class="button" @tap="updateElement">操作UniElement</button>
	<!-- #ifdef APP -->
	</scroll-view>
	<!-- #endif -->
</template>

<script setup lang="uts">
	const color = ref('red')
	const myView = ref<UniElement | null>(null)

	onReady(() => {
		myView.value = uni.getElementById('myView')
		// 也可以通过模板 ref 获取 myView.value
	})

	function updateElement() {
		color.value = 'red' == color.value ? 'blue' : 'red'
		myView.value?.style?.setProperty('background-color', color.value)
	}
</script>

<style>
	.container {
		align-self: center;
		align-items: center;
		justify-content: center;
		background-color: lightgray;
		width: 100%;
		height: 200px;
	}
	.button {
		margin: 10px auto;
	}
</style>

以上例子仅为演示DOM API的使用,实际上点击按钮修改背景色这种简单场景,使用数据绑定更简单,class绑定到一个data上,动态修改data即可。

# 通过 DrawableContext 绘制View

uni-app x 在 app 端提供 DrawableContext 绘制内容到 uvue 页面的view标签上。可用于绘制文本、形状等内容。

# 获取 DrawableContext 对象

DrawableContext 可通过view组件节点对象(UniElement)的getDrawableContext()方法获取

组合式 API

选项式 API

<template>
	<view ref="drawable" class="drawableView"></view>
</template>

<script setup lang="uts">
	const drawable = ref<UniElement | null>(null)

	onReady(() => {
		var ctx = drawable.value!.getDrawableContext()!
	})
</script>

注意
DrawableContext 对象需等元素渲染到页面后才能正常使用,要在页面 onReady 生命周期后获取。

# 绘制内容

通过 DrawableContext 提供的 Draw API 绘制文本、形状等内容

组合式 API

选项式 API

<script setup lang="uts">
	const drawable = ref<UniElement | null>(null)

	onReady(() => {
		var ctx = drawable.value!.getDrawableContext()!
		ctx.moveTo(50, 40)
		ctx.lineTo(200, 40)
		ctx.stroke()
	})
</script>

# 更新到画布

DrawableContext 在调用 API 之后不会主动更新到画布上,需要主动调用update()方法更新。

组合式 API

选项式 API

<script setup lang="uts">
	const drawable = ref<UniElement | null>(null)

	onReady(() => {
		var ctx = drawable.value!.getDrawableContext()!
		ctx.moveTo(50, 40)
		ctx.lineTo(200, 40)
		ctx.stroke()
		ctx.update()
	})
</script>

# 清除画布内容

如果清除已经绘制的内容重新绘制,需要调用reset()方法清除内容再进行绘制。

组合式 API

选项式 API

<script setup lang="uts">
	const drawable = ref<UniElement | null>(null)

	function refreshDrawable() {
		var ctx = drawable.value!.getDrawableContext()!
		ctx.reset()
	}
</script>

# 示例

以下是完整的操作示例:

组合式 API

选项式 API

<template>
	<view ref="drawable" class="drawableView"></view>
	<button class="button" @tap="refreshDrawable">重新绘制</button>
</template>

<script setup lang="uts">
	const change = ref(false)
	const drawable = ref<UniElement | null>(null)

	onReady(() => {
		var ctx = drawable.value!.getDrawableContext()!
		ctx.moveTo(50, 40)
		ctx.lineTo(200, 40)
		ctx.stroke()
		ctx.update()
	})

	function refreshDrawable() {
		var ctx = drawable.value!.getDrawableContext()!
		ctx.reset()
		change.value = !change.value
		if(change.value) {
			ctx.strokeStyle = "#FF0000"
			ctx.lineWidth = 10
		}
		ctx.moveTo(50, 40)
		ctx.lineTo(200, 40)
		ctx.stroke()
		ctx.update()
	}
</script>

<style>
	.drawableView {
		width: 750rpx;
		height: 750rpx;
		background-color: #FFFFFF;
	}
	.button {
		margin: 10px auto;
		width: 50%;
	}
</style>

# 注意事项

由于排版和渲染是异步的,在修改 DOM 后,立刻使用 DOM 的同步接口获取 DOM 状态可能获取到的是排版之前的,如果需要及时准确的获取到排版之后的 DOM 状态需要使用 uni.createSelectorQuery