README.md 7.2 KB
Newer Older
D
DCloud_LXH 已提交
1
# UVUE DOM
D
DCloud_LXH 已提交
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21

App-uvue的每个页面,在内存中都有一个 DOM(文档对象模型)。它和浏览器的 [DOM规范](https://www.w3.org/DOM/?spm=a2c7j.-zh-docs-api-weex-variable.0.0.2a5537c6FrgbYp) 类似。

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

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

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

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

> 低版本使用过 INode 对象,从HBuilderX 3.91开始统一为 Element 对象,不再推荐使用 INode。


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

1. 跟手动效

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

D
DCloud_LXH 已提交
23
	uvue的data更新,有一套diff机制,每次触发data更新,会多几毫秒的耗时。
D
DCloud_LXH 已提交
24

D
DCloud_LXH 已提交
25 26 27 28 29 30 31 32 33
	此时推荐通过 DOM API 跳过 vue 框架直接操作组件的样式。

2. Draw API

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

[性能](../performance.md)章节,对这2个场景有详细的阐述。


D
DCloud_LXH 已提交
34
## DOM元素对象@getDomNode
D
DCloud_LXH 已提交
35 36 37

在操作DOM元素对象前,需要先获取 `Element` 对象,可通过 `uni.getElementById``this.$refs` 获取。

D
DCloud_LXH 已提交
38
### 通过uni.getElementById获取DOM元素
D
DCloud_LXH 已提交
39

D
DCloud_LXH 已提交
40
app-uvue 页面中可以为页面元素节点设置 id 属性,然后通过 [uni.getElementById](../api/get-element.md#getelementbyid) 获取 DOM 元素对象。
D
DCloud_LXH 已提交
41

W
补提  
wanganxp 已提交
42 43
但注意这个方法只能获取栈顶页面的element。如需绑定调用的页面,需使用下方的[this.$refs](#refs)

D
DCloud_LXH 已提交
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
首先需要为组件设置 id 属性值:
```vue
<!-- id 属性值为 myView,后续可以通过此值查找 -->
<view id="myView" class="container">
    <text>Hello World</text>
</view>
```

在页面`onReady` 后(太早组件可能没有创建),通过 `uni.getElementById` 获取。如果长期使用,可以保存在vue的 data 中。
```ts
  export default {
    data() {
      return {
        color: 'red',
        myView: null as Element | null
      }
    },
    onReady() {
D
DCloud_LXH 已提交
62
        // 获取组件对象并保存在 this.myView 中
D
DCloud_LXH 已提交
63 64 65 66 67
        this.myView = uni.getElementById('myView');
    },
}
```

W
补提  
wanganxp 已提交
68
### 通过this.$refs获取DOM元素@refs
D
DCloud_LXH 已提交
69
app-uvue页面中可以通过 vue 框架中的组件实例对象 [this.$refs](https://uniapp.dcloud.net.cn/tutorial/vue3-api.html#%E5%AE%9E%E4%BE%8B-property) 获取 DOM 元素对象。
D
DCloud_LXH 已提交
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88

首先需要为组件设置 ref 属性值,它类似于id:
```vue
<!-- ref 属性值为 myView,后续可以通过此值查找 -->
<view ref="myView" class="container">
    <text>Hello World</text>
</view>
```

在页面`onReady` 后(太早组件可能没有创建),通过 `this.$refs` 获取。如果长期使用,可以保存在vue的 data 中。
```ts
  export default {
    data() {
      return {
        color: 'red',
        myView: null as Element | null
      }
    },
    onReady() {
D
DCloud_LXH 已提交
89
        // 获取组件对象并保存在 this.myView 中
D
DCloud_LXH 已提交
90 91 92 93 94 95 96 97 98 99 100 101 102
        this.myView = this.$refs['myView'] as Element;  //需要使用 as 转换
    },
}
```

### 操作DOM元素对象
获取DOM元素对象Elment后,可通过其属性或方法操作组件,完整API参考[Element对象文档](element.md)

如通过Element对象的 style 属性更新组件的样式:
```ts
this.myView?.style?.setProperty('background-color', 'red');
```

D
DCloud_LXH 已提交
103 104 105
### 示例
以下是完整的操作示例:
```vue
D
DCloud_LXH 已提交
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
<template>
  <!-- #ifdef APP -->
  <scroll-view style="flex:1;align-items: center;">
  <!-- #endif -->
    <view id="myView" ref="myView" class="container">
      <text>Hello World</text>
    </view>
    <button @tap="updateElement">操作Element</button>
  <!-- #ifdef APP -->
  </scroll-view>
  <!-- #endif -->
</template>

<script>
  export default {
    data() {
      return {
        color: 'red',
        myView: null as Element | null
      }
    },
    onLoad() {
    },
    onReady() {
      this.myView = uni.getElementById('myView');       //通过uni.getElementById获取
      //this.myView = this.$refs['myView'] as Element;  //通过this.$refs获取,需要使用 as 转换
    },
    methods: {
      updateElement() {
        this.color = 'red' == this.color ? 'blue' : 'red';
        this.myView?.style?.setProperty('background-color', this.color);
      }
    },
  }
</script>

<style>
  .container {
    align-items: center;
    justify-content: center;
    background-color: aquamarine;
    width: 100%;
    height: 200px;
  }
</style>

```

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


D
DCloud_LXH 已提交
157
## 通过DrawableContext绘制View
D
DCloud_LXH 已提交
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251

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

### 获取 DrawableContext 对象

DrawableContext 可通过节点对象(Element)的`getDrawableContext()`方法获取

```vue
<template>
	<view ref="drawable" style="width: 750rpx;height: 750rpx;">
	</view>
</template>
<script>
	export default {
		onReady() {
			var ctx = (this.$refs['drawable'] as Element).getDrawableContext()
		}
	}
</script>
```

### 绘制内容

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

```ts
<script>
	export default {
		onReady() {
			var ctx = (this.$refs['drawable'] as Element).getDrawableContext()
			ctx.moveTo(50, 40);
			ctx.lineTo(200, 40);
			ctx.stroke();
		}
	}
</script>
```

### 更新到画布

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

```ts
<script>
	export default {
		onReady() {
			var ctx = (this.$refs['drawable'] as Element).getDrawableContext()
			ctx.moveTo(50, 40);
			ctx.lineTo(200, 40);
			ctx.stroke();
			ctx.update()
		}
	}
</script>
```

### 清除画布内容

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

```vue
<template>
	<view ref="drawable" style="width: 750rpx;height: 750rpx;" @click="drawable">
	</view>
</template>
<script>
	export default {
		data(){
			return {
				change:false
			}
		},
		onReady() {
			this.drawable()
		},
		methods:{
			drawable(){
				var ctx = (this.$refs['drawable'] as Element).getDrawableContext()
				ctx.reset();
				if(this.change) {
					ctx.strokeStyle = "#33ff0000"
					ctx.lineWidth = 10
				}
				this.change = !this.change
				ctx.moveTo(50, 40);
				ctx.lineTo(200, 40);
				ctx.stroke();
				ctx.update()
			}
		}
	}
</script>
```

Q
qiang 已提交
252 253 254
## 注意事项

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