uts-vue-component.md 14.3 KB
Newer Older
shutao-dc's avatar
shutao-dc 已提交
1
## UTS插件-标准模式组件
2

shutao-dc's avatar
shutao-dc 已提交
3
使用vue组件开发规范,通过插件封装原生平台view提供给native-view组件,实现组件特定功能及UI展示。
4 5 6 7 8

## 前置条件

继续阅读文档前,开发者需要了解以下前置条件:

9
+ 了解 [uts语法](https://doc.dcloud.net.cn/uni-app-x/uts/)[uts原生插件](https://doc.dcloud.net.cn/uni-app-x/plugin/uts-plugin.html)
shutao-dc's avatar
shutao-dc 已提交
10 11
+ 了解 [vue组件](https://uniapp.dcloud.net.cn/tutorial/vue3-components.html)
+ 了解 [native-view组件](https://doc.dcloud.net.cn/uni-app-x/component/native-view.html)
12

shutao-dc's avatar
shutao-dc 已提交
13
## UTS插件-标准模式组件目录结构
14

15 16
<pre v-pre="" data-lang="">
<code class="lang-" style="padding:0">
shutao-dc's avatar
shutao-dc 已提交
17 18 19
┌─components                      // vue组件代码
|   ├─xxx                      // vue组件名称文件夹  xxx代替组件名称
|       ├─xxx.uvue             // vue组件uts代码 xxx代替组件名称
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
├─static                          // 静态资源
├─utssdk
│	├─app-android                 //Android平台目录
│	│	├─assets                  //Android原生assets资源目录,可选
│	│	├─libs                    //Android原生库目录,可选
│	│	├─res                     //Android原生res资源目录,可选
│	│	├─AndroidManifest.xml     //Android原生应用清单文件,可选
│	│	├─config.json             //Android原生配置文件
│	│	├─index.uts               //Android原生插件能力实现,可选
│	├─app-ios                     //iOS平台目录
│	│	├─Frameworks              //iOS原生依赖的第三方 framework 依赖库存放目录,可选
│	│	├─Resources               //iOS原生所依赖的资源文件存放目录,可选
│	│	├─info.plist              //iOS原生所需要添加到主 info.plist 文件中的配置文件,可选
│	│	├─UTS.entitlements        //iOS原生所需要添加到主工程 .entitlements 文件中的配置文件,可选
│	│	├─config.json             //iOS原生配置文件
│	│	├─index.uts               //iOS原生插件能力实现,可选
│	├─web                         //web平台目录
│	│	└─index.uts
│	└─index.uts                   // 跨平台插件能力实现,可选
└─package.json                    // 插件清单文件
</code>
41 42
</pre>

shutao-dc's avatar
shutao-dc 已提交
43
如上所示,UTS插件-标准模式组件目录结构与UTS插件基本相同,差别在于components目录,vue组件代码存放components目录下。
44 45 46

其他目录文件详情可参考[UTS插件文档](https://doc.dcloud.net.cn/uni-app-x/plugin/uts-plugin.html#%E6%8F%92%E4%BB%B6%E7%9A%84%E7%9B%AE%E5%BD%95%E7%BB%93%E6%9E%84)

shutao-dc's avatar
shutao-dc 已提交
47
## 开发UTS插件-标准模式组件
48

shutao-dc's avatar
shutao-dc 已提交
49
### 构建标准模式组件
50

shutao-dc's avatar
shutao-dc 已提交
51
HBuilder X 选中你的项目,项目根目录选中uni_modules目录,右键选择新建uni_modules插件,弹窗后分类选择 “UTS插件-标准模式组件”,填写组件名称,以下均已 native-button 为例
52

shutao-dc's avatar
shutao-dc 已提交
53
![](https://web-ext-storage.dcloud.net.cn/doc/uts/uts_plugin/create-uts-vue-component.png)
54

shutao-dc's avatar
shutao-dc 已提交
55
创建完毕 HBuilder X 会自动构建模版文件,参考:`UTS插件-标准模式组件目录结构`
56

shutao-dc's avatar
shutao-dc 已提交
57
### UTS插件-标准模式组件代码编写
58

shutao-dc's avatar
shutao-dc 已提交
59
#### 添加 native-view
60

shutao-dc's avatar
shutao-dc 已提交
61
构建标准模式组件后,HBuilder X 会自动创建components/native-button/native-button.uvue文件,在该文件编写代码添加 native-view 标签
62

shutao-dc's avatar
shutao-dc 已提交
63
```html
64
<template>
65
	<native-view></native-view>
66 67 68
</template>
```

shutao-dc's avatar
shutao-dc 已提交
69 70 71
#### native-view 与 原生对象关联

native-view 初始化会触发 @init 事件,此时创建NativeButton对象,native-button.uvue代码中用NativeButton对象调用插件相关的API。将 UniNativeViewElement 传递给NativeButton对象对象,进行关联绑定
72

shutao-dc's avatar
shutao-dc 已提交
73
[NativeButton](#实现nativebutton对象)是在utssdk目录构建的原生对象。NativeButton对象内部处理原生view与native-view绑定关联业务
74

shutao-dc's avatar
shutao-dc 已提交
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
::: preview

> 组合式 API

```html
<template>
	<native-view @init="onviewinit"></native-view>
</template>
<script setup lang="uts">
	//引入 NativeButton 原生对象
	import { NativeButton } from "@/uni_modules/native-button";
	let button : NativeButton | null = null
	//native-view初始化时触发此方法
	function onviewinit(e : UniNativeViewInitEvent) {
		//获取UniNativeViewElement 传递给NativeButton对象
		button = new NativeButton(e.detail.element);
	}
</script>
```

> 选项式 API

```html
98 99 100
<template>
	<native-view @init="onviewinit"></native-view>
</template>
shutao-dc's avatar
shutao-dc 已提交
101
<script lang="uts">
shutao-dc's avatar
shutao-dc 已提交
102
	//引入 NativeButton 原生对象
103 104 105 106 107 108 109 110 111 112
	import { NativeButton } from "@/uni_modules/native-button";
	export default {
		data() {
			return {
				button: null as NativeButton | null
			}
		},
		methods: {
			//native-view初始化时触发此方法
			onviewinit(e : UniNativeViewInitEvent) {
shutao-dc's avatar
shutao-dc 已提交
113
				//获取UniNativeViewElement 实例化NativeButton将element以构造参数传递给NativeButton对象
114 115 116 117
				this.button = new NativeButton(e.detail.element);
			}
		}
	}
shutao-dc's avatar
shutao-dc 已提交
118
</script>
119 120
```

shutao-dc's avatar
shutao-dc 已提交
121 122
:::

shutao-dc's avatar
shutao-dc 已提交
123
#### 组件声明方法
124 125 126

在 methods 节点中添加updateText方法,native-button组件使用者可调用该方法更新native-button文案。 [页面调用组件方法](https://doc.dcloud.net.cn/uni-app-x/vue/component.html#page-call-component-method)

shutao-dc's avatar
shutao-dc 已提交
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
::: preview

> 组合式 API

```ts
<script setup lang="uts">
	//引入 NativeButton 原生对象
	import { NativeButton } from "@/uni_modules/native-button";
	let button : NativeButton | null = null
	//声明方法
	function updateText(value : string) {
		button?.updateText(value)
	}
</script>
```

> 选项式 API

145
```ts
146 147 148 149 150 151 152 153
methods: {
	//对外函数
	updateText(value: string) {
		this.button?.updateText(value)
	}
}
```

shutao-dc's avatar
shutao-dc 已提交
154 155
:::

shutao-dc's avatar
shutao-dc 已提交
156
#### 组件声明props
157

shutao-dc's avatar
shutao-dc 已提交
158
native-button 声明props,例如native-button的文案信息text属性,按vue组件规范监听到text属性更新,通过NativeButton对象驱动更新原生view属性,在components/native-button/native-button.uvue编写如下代码,具体参考[vue组件Props规范](https://cn.vuejs.org/guide/typescript/composition-api#typing-component-props)
159

shutao-dc's avatar
shutao-dc 已提交
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
::: preview

> 组合式 API

```html
<script setup lang="uts">
	//声明属性
	const props = defineProps<{ text : string }>()
	//声明方法
	function updateText(value : string) {
		button?.updateText(value)
	}
	//监听属性变化
	watchEffect(() => {
		// console.log("watchEffect   "+props.text)
		const text = props.text
		updateText(text)
	})
</script>
```

> 选项式 API

183 184 185 186 187 188 189 190
```html
<script lang="uts">
	export default {
		props: {
			"text": {
				type: String,
				default: ''
			}
191
		},
192 193 194 195 196 197 198 199 200 201 202
		watch: {
			"text": {
				handler(newValue : string, oldValue : string) {
					this.value = newValue
					this.button?.updateText(this.value)
				},
				immediate: true
			},
		},
	}
</script>
203 204
```

shutao-dc's avatar
shutao-dc 已提交
205 206
:::

shutao-dc's avatar
shutao-dc 已提交
207
#### 组件声明事件
208

shutao-dc's avatar
shutao-dc 已提交
209
native-button 声明事件,例如原生组件触发点击事件@buttonTap, NativeButton对象通过 UniNativeViewElement 的 dispatchEvent 函数触发native-view的 @customClick 自定义事件。native-button.uvue监听native-view的 @customClick 自定义事件实现emit触发声明事件,具体参考[vue组件事件规范](https://cn.vuejs.org/guide/typescript/composition-api#typing-component-emits)
210

shutao-dc's avatar
shutao-dc 已提交
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
::: preview

> 组合式 API

```html
<template>
	<native-view @customClick="ontap"></native-view>
</template>
<script setup lang="uts">
	//声明事件
	const emit = defineEmits<{
		(e : "buttonTap", event : UniNativeViewEvent) : void
	}>()
	
	function ontap(e : UniNativeViewEvent) {
		emit("buttonTap", e)
	}
</script>
```

> 选项式 API

233
```html
234 235 236
<template>
	<native-view @customClick="ontap"></native-view>
</template>
237 238 239 240 241 242
<script lang="uts">
	export default {
		methods: {
			ontap(e: UniNativeViewEvent) {
				this.$emit("buttonTap", e)
			}
243 244
		}
	}
245
</script>
246 247
```

shutao-dc's avatar
shutao-dc 已提交
248 249
:::

shutao-dc's avatar
shutao-dc 已提交
250 251
**注意:**

shutao-dc's avatar
shutao-dc 已提交
252
目前自定义事件参数仅支持[UniNativeViewEvent](../component/native-view#uninativeviewevent)
shutao-dc's avatar
shutao-dc 已提交
253

254
native-button/components/native-button/native-button.uvue 最终代码如下:
255

shutao-dc's avatar
shutao-dc 已提交
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
::: preview

> 组合式 API

``` html
<template>
	<native-view @init="onviewinit" @customClick="ontap"></native-view>
</template>
<script setup lang="uts">
	import { NativeButton } from "@/uni_modules/native-button";
	let button : NativeButton | null = null
	
	//声明属性
	const props = defineProps<{ text : string }>()
	
	//声明事件
	const emit = defineEmits<{
		(e : "buttonTap", event : UniNativeViewEvent) : void
	}>()
	
	//声明方法
	function updateText(value : string) {
		button?.updateText(value)
	}
	
	//监听属性变化
	watchEffect(() => {
		const text = props.text
		updateText(text)
	})

	//native-view初始化时触发此方法
	function onviewinit(e : UniNativeViewInitEvent) {
		//获取UniNativeViewElement 传递给NativeButton对象
		button = new NativeButton(e.detail.element);
		updateText(props.text)
	}

	function ontap(e : UniNativeViewEvent) {
		emit("buttonTap", e)
	}

	function onUnmounted() {
		// iOS平台需要主动释放 uts 实例
		button?.destroy()
	}
</script>
```

> 选项式 API

307
```html
308
<template>
309 310
	<native-view @init="onviewinit" @customClick="ontap"></native-view>
</template>
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
<script lang="uts">
	import { NativeButton } from "@/uni_modules/native-button";
	export default {
		data() {
			return {
				button: null as NativeButton | null,
				value: ""
			}
		},
		props: {
			"text": {
				type: String,
				default: ''
			}
		},
		watch: {
			"text": {
				handler(newValue : string, oldValue : string) {
					this.value = newValue
					this.updateText(newValue)
				},
				immediate: true
			},
		},
		methods: {
			//native-view初始化时触发此方法
			onviewinit(e : UniNativeViewInitEvent) {
				//获取UniNativeViewElement 传递给NativeButton插件
				this.button = new NativeButton(e.detail.element);
				this.button?.updateText(this.value)
			},
342 343
			ontap(e: UniNativeViewEvent) {
				this.$emit("buttonTap", e)
344 345 346 347 348 349 350 351 352
			},
			updateText(value: string) {
				this.button?.updateText(value)
			}
		},
		unmounted() {
			// iOS平台需要主动释放 uts 实例
			this.button?.destroy()
		}
353
	}
354 355 356
</script>
```

shutao-dc's avatar
shutao-dc 已提交
357 358
:::

shutao-dc's avatar
shutao-dc 已提交
359
#### 实现NativeButton对象
360

shutao-dc's avatar
shutao-dc 已提交
361
utssdk目录实现不同平台的原生NativeButton对象,构造参数获取UniNativeViewElement对象与原生view绑定,封装原生view功能关联的API。
362

363
::: preview
364

365
> Android
366

shutao-dc's avatar
shutao-dc 已提交
367
```uts
368 369 370 371 372 373
import { Button } from "android.widget"

export class NativeButton {
	$element : UniNativeViewElement;

	constructor(element : UniNativeViewElement) {
shutao-dc's avatar
shutao-dc 已提交
374
		//接收传递过来的UniNativeViewElement
375 376 377 378 379
		this.$element = element;
		this.bindView();
	}

	button : Button | null = null;
380
	bindView() {
381
		//通过UniElement.getAndroidActivity()获取android平台activity 用于创建view的上下文
382 383 384 385
		this.button = new Button(this.$element.getAndroidActivity()!);  //构建原生view
		//限制原生Button 文案描述不自动大写
		this.button?.setAllCaps(false)
		//监听原生Button点击事件
386
		this.button?.setOnClickListener(_ => {
387 388 389 390 391 392
			const detail = {}
			//构建自定义UniNativeViewEvent返回对象
			const event = new UniNativeViewEvent("customClick", detail)
			//触发原生Button的点击事件
			this.$element.dispatchEvent(event)
		})
393 394 395 396
		//UniNativeViewEvent 绑定 安卓原生view
		this.$element.bindAndroidView(this.button!);
	}

397
	updateText(text: string) {
398 399 400 401 402 403 404 405 406 407
		//更新原生Button 文案描述
		this.button?.setText(text)
	}

	destroy(){
		//数据回收
	}
}
```

408 409
> iOS

shutao-dc's avatar
shutao-dc 已提交
410
```uts
411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461
import { UIButton, UIControl } from "UIKit"

export class NativeButton {

	element : UniNativeViewElement;
	button : UIButton | null;

	constructor(element : UniNativeViewElement) {
    // 接收组件传递过来的UniNativeViewElement
		this.element = element;
		super.init()
		this.bindView();
	}

	// element 绑定原生view
	bindView() {
    // 初始化原生 UIButton
    this.button = new UIButton(type=UIButton.ButtonType.system)
    // 构建方法选择器
    const method = Selector("buttonClickAction")
    // button 绑定点击回调方法
    button?.addTarget(this, action = method, for = UIControl.Event.touchUpInside)
    // UniNativeViewElement 绑定原生 view
		this.element.bindIOSView(this.button!);
	}

	updateText(text : string) {
    // 更新 button 显示文字
		this.button?.setTitle(text, for = UIControl.State.normal)
	}

	/**
	 * 按钮点击回调方法
	 * 在 swift 中,所有target-action (例如按钮的点击事件,NotificationCenter 的通知事件等)对应的 action 函数前面都要使用 @objc 进行标记。
	 */
	@objc buttonClickAction() {
    //构建自定义 UniNativeViewEvent 对象
		let event = new UniNativeViewEvent("customClick")
    //触发自定义事件
		this.element.dispatchEvent(event)
	}

	destroy() {
    // 释放 UTS 实例对象,避免内存泄露
		UTSiOS.destroyInstance(this)
	}
}
```

:::

shutao-dc's avatar
shutao-dc 已提交
462
更多实现可参考 标准模式组件 [native-button](https://gitcode.net/dcloud/hello-uni-app-x/-/tree/dev/uni_modules/native-button)
463

shutao-dc's avatar
shutao-dc 已提交
464
此时一个简单的UTS插件-标准模式组件就完成了,
465 466

**注意:**
shutao-dc's avatar
shutao-dc 已提交
467
+ UTS插件-标准模式组件的 components 目录下的代码中不能含有原生平台任何引用对象,这会导致vue原生组件无法跨平台,与原生平台关联的代码都应放在UTS插件中
468
+ ios平台需要vue组件主动释放 uts 实例,所以页面触发 unmounted 生命周期时需要调用 this.button?.destroy() 避免内存泄露
shutao-dc's avatar
shutao-dc 已提交
469
+ android平台 native-view 组件不支持border、background、box-shadow属性,可以使用view标签包裹native-view,在view标签设置以上属性
470

shutao-dc's avatar
shutao-dc 已提交
471
### 页面引用UTS插件-标准模式组件
472

shutao-dc's avatar
shutao-dc 已提交
473
以 native-button 为例, 创建标准模式组件的项目页面可以直接使用 native-button 标签,也可将native-button插件包放置其他项目的uni-modules文件夹中。项目页面即可使用 native-button 标签
474

475 476
```html
<template>
477
	<view style="flex:1">
478 479 480 481 482 483
		<native-button class="native-button" text="buttonText" @buttonTap="ontap"></native-button>
	</view>
</template>

<script>
	export default {
484 485 486
		methods: {
			ontap(e : UniNativeViewEvent) {
			  console.log("ontap----------"+e.type)
487 488 489 490 491 492
			}
		}
	}
</script>

<style>
493 494 495 496
	.native-button {
		height: 100px;
		width: 200px;
		margin: 25px auto 25px auto;
497
	}
498 499
</style>
```