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 123 124

在 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 已提交
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
::: 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

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

shutao-dc's avatar
shutao-dc 已提交
152
#### 组件声明props
153

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

shutao-dc's avatar
shutao-dc 已提交
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
::: 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

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

shutao-dc's avatar
shutao-dc 已提交
201
#### 组件声明事件
202

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

shutao-dc's avatar
shutao-dc 已提交
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
::: 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

227
```html
228 229 230
<template>
	<native-view @customClick="ontap"></native-view>
</template>
231 232 233 234 235 236
<script lang="uts">
	export default {
		methods: {
			ontap(e: UniNativeViewEvent) {
				this.$emit("buttonTap", e)
			}
237 238
		}
	}
239
</script>
240 241
```

shutao-dc's avatar
shutao-dc 已提交
242 243
**注意:**

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

246
native-button/components/native-button/native-button.uvue 最终代码如下:
247

shutao-dc's avatar
shutao-dc 已提交
248 249 250 251 252 253 254 255 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
::: 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 : "load") : void
		(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)
		emit("load")
	}

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

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

> 选项式 API

301
```html
302
<template>
303 304
	<native-view @init="onviewinit" @customClick="ontap"></native-view>
</template>
305 306 307 308 309 310 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
<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)
			},
336 337
			ontap(e: UniNativeViewEvent) {
				this.$emit("buttonTap", e)
338 339 340 341 342 343 344 345 346
			},
			updateText(value: string) {
				this.button?.updateText(value)
			}
		},
		unmounted() {
			// iOS平台需要主动释放 uts 实例
			this.button?.destroy()
		}
347
	}
348 349 350
</script>
```

shutao-dc's avatar
shutao-dc 已提交
351
#### 实现NativeButton对象
352

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

355
::: preview
356

357
> Android
358

shutao-dc's avatar
shutao-dc 已提交
359
```uts
360 361 362 363 364 365
import { Button } from "android.widget"

export class NativeButton {
	$element : UniNativeViewElement;

	constructor(element : UniNativeViewElement) {
shutao-dc's avatar
shutao-dc 已提交
366
		//接收传递过来的UniNativeViewElement
367 368 369 370 371
		this.$element = element;
		this.bindView();
	}

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

389
	updateText(text: string) {
390 391 392 393 394 395 396 397 398 399
		//更新原生Button 文案描述
		this.button?.setText(text)
	}

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

400 401
> iOS

shutao-dc's avatar
shutao-dc 已提交
402
```uts
403 404 405 406 407 408 409 410 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
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 已提交
454
更多实现可参考 标准模式组件 [native-button](https://gitcode.net/dcloud/hello-uni-app-x/-/tree/dev/uni_modules/native-button)
455

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

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

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

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

467 468
```html
<template>
469
	<view style="flex:1">
470 471 472 473 474 475
		<native-button class="native-button" text="buttonText" @buttonTap="ontap"></native-button>
	</view>
</template>

<script>
	export default {
476 477 478
		methods: {
			ontap(e : UniNativeViewEvent) {
			  console.log("ontap----------"+e.type)
479 480 481 482 483 484
			}
		}
	}
</script>

<style>
485 486 487 488
	.native-button {
		height: 100px;
		width: 200px;
		margin: 25px auto 25px auto;
489
	}
490 491
</style>
```