简体中文
我们可以对一段要复用的js/uts逻辑代码进行封装,抽出function、module等形式。
那么涉及UI的复用时,该如何抽象?
这就是vue的组件机制,把视图template、script、style都封装到独立的uvue组件文件中,在其他需要的地方使用组件的名称进行引用。
每个组件,包括如下几个部分:以组件名称为标记的开始标签和结束标签、组件text内容、组件属性、组件属性值。
组件还可以封装方法、事件、插槽,提供了组件的生命周期,提供了组件和页面的互通信机制,满足了各种高级需求。
如果您还不了解这些概念,请务必先阅读 组件概述文档
uni-app x 组件基于 vue 单文件组件规范,一个组件内,有 3 个根节点标签:
<template>
:组件的模板内容<script>
:组件的脚本代码<style>
:组件的样式组件的内容构成和页面大体上一致,都符合vue的sfc规范。
事实上,一个在pages.json注册的页面uvue文件,也可以被当做一个组件引入到其他页面。
组件和页面的差别有:
onLoad
、onShow
等页面生命周期,比如$setPageStyle等API。mounted
、unmounted
等组件生命周期,比如页面和组件通信的API。项目根目录/components
目录上右键(如果没有,在根目录新建一个 components
目录即可),选择 新建组件
,输入组件名称,选择一个模板;可勾选创建同名目录,将组件放在同名目录下。项目根目录/uni_modules
目录上右键(如果没有,在根目录新建一个 uni_modules
目录即可),选择 新建uni_modules插件
,输入插件ID
,分类选择前端组件-通用组件
;将组件放在和插件ID同名的目录下。pages 目录
下的任意地方创建 .uvue/.vue
文件并编写组件代码注意事项
uni-app x 项目支持使用 .vue
、.uvue
文件作为组件使用,但同文件名的两个文件同时存在,.uvue
文件会优先编译。
传统vue组件,需要安装、引用、注册,三个步骤后才能使用组件。easycom
将其精简为一步。
只要组件安装在项目的 components
目录下或 uni_modules/插件 id/components/插件 id/插件 id.uvue
目录下,并符合 组件名称/组件名称.(vue|uvue)
目录结构。就可以不用引用、注册,直接在页面中使用。
比如 uni-loading,它导入到项目后,存放在了目录 /uni_modules/uni-loading/components/uni-loading/uni-loading.uvue
同时它的组件名称也叫 uni-loading,所以这样的组件,不用在 script 里注册和引用。如下:
<template>
<view>
<uni-loading></uni-loading><!-- 这里会显示一个loading -->
</view>
</template>
<script>
// 这里不用import引入,也不需要在components内注册组件。template里就可以直接用
// ...
</script>
这里出现了uni_module
的概念,简单说下,它是uni-app的一种包管理方案。
uni_module
其实不止服务于组件,它可以容纳组件、script库、页面、项目等所有DCloud插件市场所支持的种类。
在HBuilderX中点右键可方便的更新插件,插件作者也可以方便的上传插件。
uni_module有详细的专项文档,请另行查阅uni_module规范。
如果你的组件不满足easycom标准的目录规范,还有一种办法是在pages.json里声明自己的目录规则,以便编译器查找到你的组件。自定义easycom路径规则的详细教程详见
组件标签名首字母大写,驼峰+ComponentPublicInstance
,如:
<test/>
类型为:TestComponentPublicInstance
<uni-data-checkbox/>
类型为:UniDataCheckboxComponentPublicInstance
不符合 easycom 规范的组件,则需要手动引入:
<!-- 组件 child.vue -->
<template>
<view>Child Component</view>
</template>
<!-- 页面(与 child.vue 组件在同级目录 -->
<template>
<view>
<child ref="component1"></child>
</view>
</template>
<script>
// 引入 child 组件
import child from './child.vue'
export default {
components: {
child
},
data() {
return {
component1: null as ComponentPublicInstance | null // 手动引入组件时的类型
}
}
}
</script>
类型为:ComponentPublicInstance
url
地址中携带参数props
示例 详情
注意
this.$props
是 Map
类型,需要使用 this.$props["propName"]
来访问.
点操作符来访问选项式 API
组合式 API
<template>
<view class="page">
<array-literal :str="str" :num="num" :bool="bool" :obj="obj" :arr="arr" />
<object-type str="str" :num="num" :bool="bool" :obj="obj" :arr="arr" />
<same-name-prop-default-value />
<props-with-defaults />
<!-- #ifdef APP-ANDROID -->
<reference-types :list="[1,2,3]" />
<!-- #endif -->
<!-- #ifndef APP-ANDROID -->
<reference-types :list="['a','b','c']" />
<!-- #endif -->
</view>
</template>
<script lang="uts">
import ArrayLiteral from './array-literal-options.uvue'
import ObjectType from "./object-type-options.uvue";
import SameNamePropDefaultValue from "./same-name-prop-default-value-options.uvue";
import PropsWithDefaults from "./props-with-defaults.uvue";
import ReferenceTypes from './reference-types-options.uvue'
export default {
components: {
ArrayLiteral,
ObjectType,
SameNamePropDefaultValue,
PropsWithDefaults,
ReferenceTypes
},
data() {
return {
str: 'str',
num: 10,
bool: true,
obj: { age: 18 },
arr: ['a', 'b', 'c']
}
},
}
</script>
示例 详情
选项式 API
组合式 API
<template>
<view class="page">
<view class="row">
<text>子组件传的参数</text>
<text id="value">
{{ value }}
</text>
</view>
<child @callback="callback"></child>
</view>
</template>
<script>
import child from './child-options.uvue'
export default {
components: {
child
},
data () {
return {
value: ""
}
},
methods: {
callback (str: string) {
this.value = str
}
}
}
</script>
<style>
.row {
display: flex;
flex-direction: row;
justify-content: space-between;
margin-bottom: 10px;
}
</style>
provide/inject
来向下传递参数 示例 详情
选项式 API
组合式 API
<template>
<!-- #ifdef APP -->
<scroll-view style="flex: 1">
<!-- #endif -->
<view class="page">
<text>provide page</text>
<button class="mt-10" @click="goProvidePage2">
跳转函数方式定义 provide 示例
</button>
<ComponentForInject />
</view>
<!-- #ifdef APP -->
</scroll-view>
<!-- #endif -->
</template>
<script lang="uts">
import ComponentForInject from '../inject/inject-options-1.uvue'
export default {
components: {
ComponentForInject
},
data(){
return {
title: '字面量方式定义 provide page title',
obj: {
title: 'data obj.title',
content: 'data obj.content'
},
}
},
provide: {
providePageStr: '字面量方式定义 provide page str',
providePageNum: 1,
providePageBool: true,
providePageObject: {
title: '字面量方式定义 provide page object title',
content: '字面量方式定义 provide page object content'
},
providePageArr: ['字面量方式定义 provide page arr'],
providePageMap: new Map<string, string>([['key', '字面量方式定义 provide page map']]),
providePageSet: new Set<string>(['字面量方式定义 provide page set']),
},
methods: {
goProvidePage2(){
uni.navigateTo({
url: '/pages/composition/provide/provide-page2'
})
}
},
}
</script>
store/index.uts 文件详情
示例 详情
选项式 API
组合式 API
<template>
<view class="page">
<view class="flex-row">
父组件:
<text class="parent-msg">{{ msg }}</text>
</view>
<button class="parent-btn" @click="change">父组件改变数据</button>
<child></child>
</view>
</template>
<script>
import child from './components/child.uvue'
import { setComponentMsg, state } from '@/store/index.uts'
export default {
components: {
child
},
computed: {
msg(): number {
return state.componentMsg
}
},
methods: {
change() {
setComponentMsg(state.componentMsg + 1)
}
}
}
</script>
main.uts
中使用 app.config.globalProperties
如在 main.uts
中的 createApp
方法中使用:
app.config.globalProperties.globalPropertiesReactiveObj = reactive({
str: 'default reactive string',
num: 0,
bool: false,
} as UTSJSONObject)
示例 详情
选项式 API
组合式 API
<template>
<!-- #ifdef APP -->
<scroll-view style="flex: 1; padding-bottom: 20px">
<!-- #endif -->
<view class="uni-padding-wrap">
<text class="mt-10"
>globalProperties string: {{ globalPropertiesStr }}</text
>
<text class="mt-10"
>globalProperties number: {{ globalPropertiesNum }}</text
>
<text class="mt-10"
>globalProperties boolean: {{ globalPropertiesBool }}</text
>
<text class="mt-10"
>globalProperties object: {{ globalPropertiesObj }}</text
>
<text class="mt-10"
>globalProperties null: {{ globalPropertiesNull }}</text
>
<text class="mt-10"
>globalProperties array: {{ globalPropertiesArr }}</text
>
<text class="mt-10"
>globalProperties set: {{ globalPropertiesSet }}</text
>
<text class="mt-10"
>globalProperties map: {{ globalPropertiesMap }}</text
>
<text class="mt-10"
>globalProperties reactiveObj.str:
{{ globalPropertiesReactiveObj['str'] }}</text
>
<text class="mt-10"
>globalProperties reactiveObj.num:
{{ globalPropertiesReactiveObj['num'] }}</text
>
<text class="mt-10"
>globalProperties reactiveObj.boolean:
{{ globalPropertiesReactiveObj['bool'] }}</text
>
<text class="mt-10"
>globalProperties fun 返回值: {{ globalPropertiesFn() }}</text
>
<button @click="updateGlobalProperties" class="mt-10">
update globalProperties
</button>
</view>
<!-- #ifdef APP -->
</scroll-view>
<!-- #endif -->
</template>
<script lang="uts">
type MyGlobalProperties = {
str : string;
num : number;
bool : boolean;
obj : UTSJSONObject;
null : string | null;
arr : number[];
set : string[];
map : UTSJSONObject;
reactiveObj : UTSJSONObject;
globalPropertiesFnRes: string
}
export default {
data() {
return {
myGlobalProperties: {
str: '',
num: 0,
bool: false,
obj: {},
null: null,
arr: [],
set: [],
map: {},
reactiveObj: {
str: '',
num: 0,
bool: false,
} as UTSJSONObject,
globalPropertiesFnRes: '',
} as MyGlobalProperties,
}
},
onLoad() {
this.getGlobalProperties()
},
onUnload(){
this.resetGlobalProperties()
},
methods: {
getGlobalProperties() {
this.myGlobalProperties.str = this.globalPropertiesStr
this.myGlobalProperties.num = this.globalPropertiesNum
this.myGlobalProperties.bool = this.globalPropertiesBool
this.myGlobalProperties.obj = this.globalPropertiesObj
this.myGlobalProperties.null = this.globalPropertiesNull
this.myGlobalProperties.arr = this.globalPropertiesArr
this.myGlobalProperties.set = []
this.globalPropertiesSet.forEach(item => {
this.myGlobalProperties.set.push(item)
})
this.myGlobalProperties.map = {}
this.globalPropertiesMap.forEach((value: number, key: string) => {
this.myGlobalProperties.map[key] = value
})
this.myGlobalProperties.reactiveObj = this.globalPropertiesReactiveObj
this.myGlobalProperties.globalPropertiesFnRes = this.globalPropertiesFn()
},
resetGlobalProperties() {
this.globalPropertiesStr = 'default string'
this.globalPropertiesNum = 0
this.globalPropertiesBool = false
this.globalPropertiesObj = {
str: 'default globalProperties obj string',
num: 0,
bool: false,
}
this.globalPropertiesNull = null
this.globalPropertiesArr = []
this.globalPropertiesSet = new Set()
this.globalPropertiesMap = new Map()
this.globalPropertiesReactiveObj['str'] = 'default reactive string'
this.globalPropertiesReactiveObj['num'] = 0
this.globalPropertiesReactiveObj['bool'] = false
},
updateGlobalProperties() {
this.globalPropertiesStr = 'new string'
this.globalPropertiesNum = 100
this.globalPropertiesBool = true
this.globalPropertiesObj = {
str: 'new globalProperties obj string',
num: 100,
bool: true,
}
this.globalPropertiesNull = 'not null'
this.globalPropertiesArr = [1, 2, 3]
this.globalPropertiesSet = new Set(['a', 'b', 'c'])
this.globalPropertiesMap = new Map([['a', 1], ['b', 2], ['c', 3]])
this.globalPropertiesReactiveObj['str'] = 'new reactive string'
this.globalPropertiesReactiveObj['num'] = 200
this.globalPropertiesReactiveObj['bool'] = true
this.getGlobalProperties()
}
},
}
</script>
<style>
.uni-padding-wrap {
padding: 10px;
}
</style>
上述 页面与组件通信 方法同样适用于父组件与子组件通信。
easycom
组件方法 在调用组件方法的时候如报错
error: Reference has a nullable type
则需要使用?.
操作符(如:a?.b?.())。
easycom组件,用法和内置组件一样。也是使用 this.$refs
获取组件并转换为组件的类型,通过 .
操作符 调用组件方法或设置属性。
语法
(this.$refs['组件ref属性值'] as 驼峰ComponentPublicInstance)?.foo?.();
示例 详情
选项式 API
组合式 API
<template>
<view>
<call-easy-method ref="callEasyMethod1"></call-easy-method>
<custom-call-easy-method ref="customCallEasyMethod1"></custom-call-easy-method>
</view>
</template>
<script lang="uts">
const delay = (): Promise<string> =>
new Promise((resolve, _) => {
setTimeout(() => {
resolve('')
}, 1000)
})
export default {
data() {
return {
callEasyMethod1: null as CallEasyMethodComponentPublicInstance | null,
customCallEasyMethod1: null as CustomCallEasyMethodComponentPublicInstance | null,
}
},
onReady() {
// 通过组件 ref 属性获取组件实例, 组件标签名首字母大写,驼峰+ComponentPublicInstance
this.callEasyMethod1 = this.$refs['callEasyMethod1'] as CallEasyMethodComponentPublicInstance
this.customCallEasyMethod1 = this.$refs['customCallEasyMethod1'] as CustomCallEasyMethodComponentPublicInstance
this.call()
},
methods: {
async call(): Promise<void> {
this.callCustomMethod()
this.callMethod1()
await delay()
this.callMethod2()
await delay()
this.callMethod3()
await delay()
this.callMethod4()
await delay()
this.callMethod5()
},
callMethod1() {
// 调用组件的 foo1 方法
this.callEasyMethod1?.foo1?.()
},
callMethod2() {
// 调用组件的 foo2 方法并传递 1个参数
this.callEasyMethod1?.foo2?.(Date.now())
},
callMethod3() {
// 调用组件的 foo3 方法并传递 2个参数
this.callEasyMethod1?.foo3?.(Date.now(), Date.now())
},
callMethod4() {
// 调用组件的 foo4 方法并传递 callback
this.callEasyMethod1?.foo4?.(() => {
console.log('callback')
})
},
callMethod5() {
// 注意: 返回值可能为 null,当前例子一定不为空,所以加了 !
const result = this.callEasyMethod1?.foo5?.('string1') as string
console.log(result) // string1
},
callMethodTest(text: string): string | null {
const result = this.callEasyMethod1?.foo5?.(text) as string
return result
},
callCustomMethod() {
// 调用组件的 foo 方法
this.customCallEasyMethod1?.foo?.()
},
callCustomMethodTest(): string | null {
const result = this.customCallEasyMethod1?.foo?.() as string
return result
},
}
}
</script>
uni_modules easycom
组件方法 HBuilderX 3.97+使用 ref
属性拿到组件实例,调用 easycom
组件方法时不需要使用 $callMethod
方法,直接使用点操作符即可 .
在调用组件方法的时候如报错
error: Reference has a nullable type
则需要使用?.
操作符(如:a?.b?.())。
示例 详情
选项式 API
组合式 API
<template>
<view>
<call-easy-method-uni-modules ref="callEasyMethod1"></call-easy-method-uni-modules>
<!-- #ifdef APP -->
<view>---</view>
<test-props id="btn1" :numList="numList" :objList='objList' @buttonclick='onButtonClick'
@numListChange='numListChange' @objListChange='objListChange'
style="width: 80px;height: 30px;background-color: lightblue"></test-props>
<view style="flex-direction: row ;">
<text>isNumListValid: </text>
<text id='isNumListValid'>{{isNumListValid}}</text>
</view>
<view style="flex-direction: row ;">
<text>isObjListValid: </text>
<text id='isObjListValid'>{{isObjListValid}}</text>
</view>
<!-- #endif -->
</view>
</template>
<script lang="uts">
import { testInOtherFile } from './call-method-easycom-uni-modules'
const delay = () : Promise<string> =>
new Promise((resolve, _) => {
setTimeout(() => {
resolve('')
}, 1000)
})
export default {
data() {
return {
callEasyMethod1: null as CallEasyMethodUniModulesComponentPublicInstance | null,
isWatched: false,
changeTimes: 0,
numList: [1] as number[], // 传递 props
objList: [] as any[],
isNumListValid: false,
isObjListValid: false
}
},
onReady() {
// 通过组件 ref 属性获取组件实例, 组件标签名首字母大写,驼峰+ComponentPublicInstance
this.callEasyMethod1 = this.$refs['callEasyMethod1'] as CallEasyMethodUniModulesComponentPublicInstance
this.call()
},
methods: {
async call() : Promise<void> {
this.callMethod1()
await delay()
this.callMethod2()
await delay()
this.callMethod3()
await delay()
this.callMethod4()
await delay()
this.callMethod5()
},
callMethod1() {
// 调用组件的 foo1 方法
this.callEasyMethod1?.foo1?.()
},
callMethod2() {
// 调用组件的 foo2 方法并传递 1个参数
this.callEasyMethod1?.foo2?.(Date.now())
},
callMethod3() {
// 调用组件的 foo3 方法并传递 2个参数
this.callEasyMethod1?.foo3?.(Date.now(), Date.now())
},
callMethod4() {
// 调用组件的 foo4 方法并传递 callback
this.callEasyMethod1?.foo4?.(() => {
console.log('callback')
})
},
callMethod5() {
// 注意: 返回值可能为 null,当前例子一定不为空,所以加了 !
const result = this.callEasyMethod1?.foo5?.('string5') as string
console.log(result) // string1
},
callMethodTest(text : string) : string | null {
const result = this.callEasyMethod1?.foo5?.(text) as string
return result
},
callMethodInOtherFile(text : string) : string {
return testInOtherFile(this.callEasyMethod1!, text)
},
// #ifdef APP-ANDROID
numListChange(res : Map<string, Map<string, any>>) {
const value = res['detail']!['value'] as number[]
const isArray = Array.isArray(value)
const isLengthGt0 = value.length > 0
this.isNumListValid = isArray && isLengthGt0
},
// #endif
// #ifdef APP-IOS
numListChange(res : any) {
const value = res['detail']!['value'] as number[]
const isArray = Array.isArray(value)
const isLengthGt0 = value.length > 0
this.isNumListValid = isArray && isLengthGt0
},
// #endif
// #ifdef APP-ANDROID
objListChange(res : Map<string, Map<string, any>>) {
const value = res['detail']!['value'] as number[]
const isArray = Array.isArray(value)
const isLengthGt0 = value.length > 0
this.isObjListValid = isArray && isLengthGt0
},
// #endif
// #ifdef APP-IOS
objListChange(res : any) {
const value = res['detail']!['value'] as number[]
const isArray = Array.isArray(value)
const isLengthGt0 = value.length > 0
this.isObjListValid = isArray && isLengthGt0
},
// #endif
onButtonClick() {
// 改变 props: 观察 props 返回值为非响应式值
console.log('button click');
this.numList = [3, 2, 1]
this.objList = [{ id: '3' }, { id: '4' }]
}
}
}
</script>
ref
属性搭配 $callMethod
方法 如果不是内置组件,也不是easycom组件,那么无法使用.
操作符了。
此时需使用 this.$refs
获取组件实例,然后通过 $callMethod
调用组件的方法。也就是把组件的方法名、参数,当做callMethod的参数来传递。此时也就没有.
操作符那样的代码提示和校验了。
callMethod可用于所有自定义组件,包括easycom组件也可以使用,只不过easycom组件有更简单的用法。
语法
(this.$refs['组件ref属性值'] as ComponentPublicInstance)?.$callMethod('方法名', ...args)
组件类型
ComponentPublicInstance
示例 详情
选项式 API
组合式 API
<template>
<view class="page">
<child ref='child' />
</view>
</template>
<script lang='uts'>
import child from './child-options.uvue'
export default {
components: {
child
},
data() {
return {
str: "parent str",
num: 1
}
},
methods: {
getNum() : number {
return this.num
},
callMethodByChild(): number {
const child = this.$refs['child'] as ComponentPublicInstance
return child.$parent!.$callMethod('getNum') as number
}
}
}
</script>
注意:
4.0
版本开始支持 $callMethod
调用 defineExpose
导出的方法4.13
版本开始支持 $callMethod
调用 defineExpose
导出的方法使用 this.$refs
获取组件并as转换为组件对应的element类型,通过 .
操作符 调用组件方法或设置属性。
语法
(this.$refs['组件ref属性值'] as Uni[xxx]Element)?.foo?.();
内置组件的element类型规范
Uni组件名(驼峰)
Element
如:
<button>
: UniButtonElement
<picker-view>
: UniPickerViewElement
示例 详情
选项式 API
组合式 API
<template>
<view>
<slider :show-value="true" ref="slider1"></slider>
</view>
</template>
<script>
export default {
data() {
return {
slider1: null as UniSliderElement | null
}
},
onReady() {
// 通过组件 ref 属性获取组件实例, Uni组件名(驼峰)UniElement
this.slider1 = this.$refs['slider1'] as UniSliderElement;
this.setValue()
},
methods: {
setValue() {
this.slider1!.value = 80
},
callMethodTest(text: string): string | null {
this.slider1?.setAttribute('str', text);
const result = this.slider1?.getAttribute('str') as string;
return result;
},
}
}
</script>
bug&tips
组件名(驼峰)
Element 方式相同。目前没有代码提示。选项式 API 和 组件式 API 在监听页面生命周期时有所不同
比如选项式 API 中的
onShow
、onHide
监听页面生命周期在组合式 API 中分别对应onPageShow
、onPageHide
(在组合式 API 时会和 App 的生命周期冲突)具体请查看 页面生命周期
注意
<script setup>
中才能生效示例 详情
选项式 API
组合式 API
<script lang="uts">
export default {
setup() {
// #ifdef APP-ANDROID
onAppHide(() => {
console.log('组件监听应用生命周期 => onAppHide')
})
onAppShow((onShowOptions: OnShowOptions) => {
console.log('组件监听应用生命周期 => onAppShow => onShowOptions', onShowOptions)
})
// #endif
onPageShow(() => {
console.log('组件监听页面生命周期 => onPageShow')
})
onPageHide(() => {
console.log('组件监听页面生命周期 => onPageHide')
})
}
}
</script>
<template>组件监听页面、应用生命周期(选项式 API)</template>
Web | Android | iOS | 描述 | |
---|---|---|---|---|
beforeCreate | 4.0 | 3.9 | 4.11 | 在组件实例初始化完成之后立即调用。 在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。 |
created | 4.0 | 3.9 | 4.11 | 在组件实例处理完所有与状态相关的选项后调用。 在这一步,实例已完成以下的配置:数据观测 (data observer),属性和方法的运算,watch/event 事件回调。 然而,此时挂载阶段还未开始,因此 $el 属性仍不可用。 |
beforeMount | 4.0 | 3.9 | 4.11 | 在组件被挂载之前调用。 相关的 render 函数首次被调用。 当这个钩子被调用时,组件已经完成了其响应式状态的设置,但还没有创建 DOM 节点。 它即将首次执行 DOM 渲染过程。 |
mounted | 4.0 | 3.9 | 4.11 | 在组件被挂载之后调用。 el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子。 如果 root 实例挂载了一个文档内元素,当 mounted 被调用时 vm.$el 也在文档内。 |
beforeUpdate | 4.0 | 3.9 | 4.11 | 在组件即将因为一个响应式状态变更而更新其 DOM 树之前调用。 数据更新时调用,发生在虚拟 DOM 打补丁之前。 这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。 |
updated | 4.0 | 3.9 | 4.11 | 在组件因为一个响应式状态变更而更新其 DOM 树之后调用。 父组件的更新钩子将在其子组件的更新钩子之后调用。 这个钩子会在组件的任意 DOM 更新后被调用,这些更新可能是由不同的状态变更导致的。 如果你需要在某个特定的状态更改后访问更新后的 DOM,请使用 nextTick() 作为替代。 |
beforeUnmount | 4.0 | 3.9 | 4.11 | 在一个组件实例被卸载之前调用。 当这个钩子被调用时,组件实例依然还保有全部的功能。 |
unmounted | 4.0 | 3.9 | 4.11 | 在一个组件实例被卸载之后调用。 可以在这个钩子中手动清理一些副作用,例如计时器、DOM 事件监听器或者与服务器的连接。 |
errorCaptured | 4.0 | x | x | 在捕获了后代组件传递的错误时调用。 这个钩子带有三个实参:错误对象、触发该错误的组件实例,以及一个说明错误来源类型的信息字符串。 这个钩子可以通过返回 false 来阻止错误继续向上传递。 |
renderTracked | 4.0 | x | x | 在一个响应式依赖被组件的渲染作用追踪后调用。 跟踪虚拟 DOM 重新渲染时调用。钩子接收 debugger event 作为参数。 此事件告诉你哪个操作跟踪了组件以及该操作的目标对象和键。 |
renderTriggered | 4.0 | x | x | 在一个响应式依赖被组件触发了重新渲染之后调用。 当虚拟 DOM 重新渲染为 triggered.Similarly 为renderTracked,接收 debugger event 作为参数。 此事件告诉你是什么操作触发了重新渲染,以及该操作的目标对象和键。 |
activated | 4.0 | 4.0 | 4.11 | 若组件实例是 <KeepAlive> 缓存树的一部分,当组件被插入到 DOM 中时调用。 keep-alive 组件激活时调用。 |
deactivated | 4.0 | 4.0 | 4.11 | 若组件实例是 <KeepAlive> 缓存树的一部分,当组件从 DOM 中被移除时调用。 keep-alive 组件停用时调用。 |
serverPrefetch | x | x | x | 当组件实例在服务器上被渲染之前要完成的异步函数。 如果这个钩子返回了一个 Promise,服务端渲染会在渲染该组件前等待该 Promise 完成。 |
Web | Android | iOS | 描述 | |
---|---|---|---|---|
onMounted() | 4.0 | 4.0 | 4.11 | el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子。 如果 root 实例挂载了一个文档内元素,当 mounted 被调用时 vm.$el 也在文档内。 |
onUpdated() | 4.0 | 4.0 | 4.11 | 由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。 |
onUnmounted() | 4.0 | 4.0 | 4.11 | 在一个组件实例被卸载之后调用。 |
onBeforeMount() | 4.0 | 4.0 | 4.11 | 在挂载开始之前被调用:相关的 render 函数首次被调用。 |
onBeforeUpdate() | 4.0 | 4.0 | 4.11 | 数据更新时调用,发生在虚拟 DOM 打补丁之前。 这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。 |
onBeforeUnmount() | 4.0 | 4.0 | 4.11 | 在一个组件实例被卸载之前调用。 |
onErrorCaptured() | x | x | x | 注册一个钩子,在捕获了后代组件传递的错误时调用。 |
onRenderTracked() | x | x | x | 注册一个调试钩子,当组件渲染过程中追踪到响应式依赖时调用。 |
onRenderTriggered() | x | x | x | 注册一个调试钩子,当响应式依赖的变更触发了组件渲染时调用。 |
onActivated() | 4.0 | x | x | keep-alive 组件激活时调用。 |
onDeactivated() | 4.0 | x | x | keep-alive 组件停用时调用。 |
onServerPrefetch() | x | x | x | 注册一个异步函数,在组件实例在服务器上被渲染之前调用。 如果这个钩子返回了一个 Promise,服务端渲染会在渲染该组件前等待该 Promise 完成。 这个钩子仅会在服务端渲染中执行,可以用于执行一些仅存在于服务端的数据抓取过程。 |
示例 详情
选项式 API
组合式 API
<template>
title: {{ title }}
<button class="component-lifecycle-btn mt-10" @click="updateTitle">
updateTitle
</button>
</template>
<script lang='uts'>
import { state, setLifeCycleNum } from '@/store/index.uts';
export default {
name: 'OptionsAPIComponentLifecycle',
data() {
return {
title: 'component for options API lifecycle test',
};
},
beforeCreate() {
// 自动化测试
setLifeCycleNum(state.lifeCycleNum + 1);
console.log('component for lifecycle test beforeCreate');
},
created() {
// 自动化测试
setLifeCycleNum(state.lifeCycleNum + 1);
console.log('component for lifecycle test created');
},
beforeMount() {
// 自动化测试
setLifeCycleNum(state.lifeCycleNum + 1);
console.log('component for lifecycle test beforeMount');
},
mounted() {
// 自动化测试
setLifeCycleNum(state.lifeCycleNum + 1);
console.log('component for lifecycle test mounted');
},
beforeUpdate() {
// 自动化测试
setLifeCycleNum(state.lifeCycleNum + 1);
console.log('component for lifecycle test beforeUpdate');
},
updated() {
// 自动化测试
setLifeCycleNum(state.lifeCycleNum + 1);
console.log('component for lifecycle test updated');
},
beforeUnmount() {
// 自动化测试
setLifeCycleNum(state.lifeCycleNum - 1);
console.log('component for lifecycle test beforeUnmount');
},
unmounted() {
// 自动化测试
setLifeCycleNum(state.lifeCycleNum - 1);
console.log('component for lifecycle test unmounted');
},
activated() {
// 自动化测试
setLifeCycleNum(state.lifeCycleNum + 1);
console.log('component for lifecycle test activated');
},
deactivated() {
// 自动化测试
setLifeCycleNum(state.lifeCycleNum - 1);
console.log('component for lifecycle test deactivated');
},
methods: {
updateTitle() {
this.title = 'component for lifecycle test updated';
},
},
};
</script>
export default
内部声明,不支持其他位置定义后,在 export default
中引用。PropType
标记类型,详见。type
不支持使用自定义的构造函数。示例 详情
选项式 API
组合式 API
<template>
<view class="page">
<array-literal :str="str" :num="num" :bool="bool" :obj="obj" :arr="arr" />
<object-type str="str" :num="num" :bool="bool" :obj="obj" :arr="arr" />
<same-name-prop-default-value />
<props-with-defaults />
<!-- #ifdef APP-ANDROID -->
<reference-types :list="[1,2,3]" />
<!-- #endif -->
<!-- #ifndef APP-ANDROID -->
<reference-types :list="['a','b','c']" />
<!-- #endif -->
</view>
</template>
<script lang="uts">
import ArrayLiteral from './array-literal-options.uvue'
import ObjectType from "./object-type-options.uvue";
import SameNamePropDefaultValue from "./same-name-prop-default-value-options.uvue";
import PropsWithDefaults from "./props-with-defaults.uvue";
import ReferenceTypes from './reference-types-options.uvue'
export default {
components: {
ArrayLiteral,
ObjectType,
SameNamePropDefaultValue,
PropsWithDefaults,
ReferenceTypes
},
data() {
return {
str: 'str',
num: 10,
bool: true,
obj: { age: 18 },
arr: ['a', 'b', 'c']
}
},
}
</script>
在 uni-app js 引擎版
中,非 Web端
只能用于获取自定义组件,不能用于获取内置组件实例(如:view
、text
)。
在 uni-app x
中,内置组件会返回组件根节点的引用,自定义组件会返回组件实例。
注意事项:
ref
属性,将获取到最后一个节点或组件实例的引用。v-for
循环时,绑定 ref
属性会获取到节点或组件实例的集合。uni-app x
中,要访问 $refs
中的属性,需要使用索引方式。示例 详情
uni-app x(选项式)
uni-app x(组合式)
uni-app js 引擎版
<template>
<view class="page">
<view class="row">
<text>NodeRef: </text>
<text ref="node">{{ dataInfo.existRef }}</text>
</view>
<view class="row">
<text>childRef:</text>
<text>{{ dataInfo.existChildRef }}</text>
</view>
<view class="row">
<text>existTextItems:</text>
<text>{{ dataInfo.existTextItems }}</text>
</view>
<view>
<text v-for="item in dataInfo.textItemsExpectNum" ref="textItems" :key="item">{{
item
}}</text>
</view>
<child class="mt-10" ref="childRef">ComponentRef</child>
</view>
</template>
<script lang="uts">
import child from './child-options.uvue'
type DataInfo = {
existRef: boolean
existChildRef: boolean
textItemsExpectNum: number
existTextItems: boolean
}
export default {
components: {
child
},
data() {
return {
dataInfo: {
existRef: false,
existChildRef: false,
textItemsExpectNum: 3,
existTextItems: false
} as DataInfo
}
},
onReady() {
this.dataInfo.existRef = this.$refs['node'] !== null
this.dataInfo.existChildRef = this.$refs['childRef'] !== null
const textItems = this.$refs['textItems'] as Array<UniElement>
this.dataInfo.existTextItems = textItems.length === this.dataInfo.textItemsExpectNum
}
}
</script>
<style>
.row {
display: flex;
flex-direction: row;
justify-content: space-between;
margin-bottom: 10px;
}
</style>
自定义组件 v-model
绑定复杂表达式时,需要通过 as
指定类型(仅App-Android 平台)。
选项式 API
组合式 API
<template>
<input v-model="obj.str as string" />
</template>
<script lang="uts">
type Obj = {
str : string
}
export default {
data() {
return {
obj: {
str: "str"
} as Obj
}
}
}
</script>
示例 详情
作用域插槽需在组件中指定插槽数据类型
选项式 API
组合式 API
<template>
<view class="container">
<view>
<slot name="header" msg="Here might be a page title"></slot>
</view>
<view>
<slot msg="A paragraph for the main content."></slot>
</view>
<view>
<slot name="footer" msg="Here's some contact info"></slot>
</view>
</view>
</template>
<script lang="uts">
export default {
name: 'child',
slots: Object as SlotsType<{
header: { msg: string }
default: { msg: string }
footer: { msg: string }
}>
}
</script>
实现递归组件时不要使用组件 import 自身的写法,直接在模板内使用组件名即可。
// component-a.uvue
<template>
<view>
<text>component-a::{{text}}</text>
<component-a v-if="!end" :text="text" :limit="limit-1"></component-a>
</view>
</template>
<script>
// import componentA from './component-a' // 错误用法
export default {
name: "component-a",
props: {
text: {
type: String,
default: ''
},
limit: {
type: Number,
default: 2
}
},
computed: {
end() : boolean {
return this.limit <= 0
}
}
}
</script>