简体中文
选项式 API,要求在script里编写export default {}
,在其中定义data、methods、生命周期等选项。
Web | Android | iOS | |
---|---|---|---|
data | 4.0 | 3.9 | 4.11 |
props | 4.0 | 3.9 | 4.11 |
computed | 4.0 | 3.9 | 4.11 |
methods | 4.0 | 3.9 | 4.11 |
watch | 4.0 | 3.9 | 4.11 |
emits | 4.0 | 3.9 | 4.11 |
expose | 4.0 | x | x |
用于声明组件初始响应式状态的函数。
示例 详情
<template>
<view class="page">
<view class="flex justify-between flex-row mb-10">
<text>str: </text>
<text id="str">{{ str }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>num: </text>
<text id="num">{{ num }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>arr: </text>
<text id="arr">{{ arr.join(',') }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>obj.str: </text>
<text id="obj-str">{{ obj.str }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>obj.num: </text>
<text id="obj-num">{{ obj.num }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>obj.arr: </text>
<text id="obj-arr">{{ obj.arr.join(',') }}</text>
</view>
<view ref='htmlRef' id="idRef" class="flex justify-between flex-row mb-10">
<text>data 存储 element不需要被包装</text>
<text id="isSameRefText">{{ refElementIsSame }}</text>
</view>
<button @click="updateData">update data</button>
</view>
</template>
<script lang="uts">
type Obj = {
str : string,
num : number,
arr : number[]
}
export default {
data() {
return {
str: 'default str',
num: 0,
arr: [1, 2, 3],
// 特殊类型需要通过 as 指定类型
obj: {
str: 'default obj.str',
num: 10,
arr: [4, 5, 6]
} as Obj,
refElement: null as UniElement | null,
refElementIsSame: false
}
},
methods: {
refTest() {
const queryElementById1 = uni.getElementById('idRef')
const queryElementById2 = uni.getElementById('idRef')
const htmlRefElement = this.$refs['htmlRef'] as UniElement;
this.refElement = htmlRefElement
if (queryElementById1 === queryElementById2
&& queryElementById1 === htmlRefElement
&& queryElementById1 === this.refElement) {
this.refElementIsSame = true
}
},
updateData() {
this.str = 'new str'
this.num = 1
this.arr = [4, 5, 6]
this.obj.str = 'new obj.str'
this.obj.num = 100
this.obj.arr = [7, 8, 9]
this.refTest()
},
},
}
</script>
用于声明一个组件的 props。
示例 详情
<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>
用于声明要在组件实例上暴露的计算属性。
示例 详情
<template>
<view class="page">
<view class="flex justify-between flex-row mb-10">
<text>count:</text>
<text id="count">{{ count }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>computed double count:</text>
<text id="double-count">{{ doubleCount }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>obj.arr:</text>
<text id="obj-arr">{{ JSON.stringify(obj.arr) }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>computed obj.arr.length:</text>
<text id="obj-arr-len">{{ objArrLen }}</text>
</view>
<button id="update-btn" @click="update">update</button>
</view>
</template>
<script lang="uts">
type Obj = {
arr : number[]
}
export default {
data(){
return {
count: 0,
obj:{
arr: [1,2,3]
} as Obj
}
},
computed: {
doubleCount(): number {
return this.count * 2
},
objArrLen(): number {
return this.obj.arr.length
}
},
methods: {
update(){
this.count++
this.obj.arr.push(this.obj.arr.length + 1)
}
}
}
</script>
用于声明要混入到组件实例中的方法。
声明的方法可以直接通过组件实例访问,或者在模板语法表达式中使用。所有的方法都会将它们的 this
上下文自动绑定为组件实例,即使在传递时也如此。
在声明方法时避免使用箭头函数,因为它们不能通过 this
访问组件实例。
用于声明在数据更改时调用的侦听回调。
注意
watch immediate
第一次调用时,App-Android 平台旧值为初始值,web 平台为 null。示例 详情
<template>
<!-- #ifdef APP -->
<scroll-view style="flex: 1; padding-bottom: 20px">
<!-- #endif -->
<view class="page">
<view class="flex justify-between flex-row mb-10">
<text>count:</text>
<text id="count" ref="countRef">{{ count }}</text>
</view>
<view class="flex justify-between mb-10">
<text>watch count result:</text>
<text id="watch-count-res">{{ watchCountRes }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>watch count track number:</text>
<text id="watch-count-track-num">{{ watchCountTrackNum }}</text>
</view>
<view class="flex justify-between mb-10">
<text>watch count cleanup number:</text>
<text id="watch-count-cleanup-res">{{ watchCountCleanupRes }}</text>
</view>
<button class="increment-btn mb-10" @click="increment">increment</button>
<button class="stop-watch-count-btn mb-10" @click="triggerStopWatchCount">
stop watch count
</button>
<view class="flex justify-between flex-row mb-10">
<text>obj.str:</text>
<text id="obj-str" ref="objStrRef">{{ obj.str }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>watch obj.str trigger number:</text>
<text id="watch-obj-str-trigger-num">{{ watchObjStrTriggerNum }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>obj.num:</text>
<text id="obj-num">{{ obj.num }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>obj.bool:</text>
<text id="obj-bool" ref="objBoolRef">{{ obj.bool }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>obj.arr:</text>
<text id="obj-arr" ref="objArrRef">{{ JSON.stringify(obj.arr) }}</text>
</view>
<view class="flex justify-between mb-10">
<text>watch obj result:</text>
<text id="watch-obj-res">{{ watchObjRes }}</text>
</view>
<view class="flex justify-between mb-10">
<text>watch obj.str result:</text>
<text id="watch-obj-str-res">{{ watchObjStrRes }}</text>
</view>
<view class="flex justify-between mb-10">
<text>watch obj.bool result:</text>
<text id="watch-obj-bool-res">{{ watchObjBoolRes }}</text>
</view>
<view class="flex justify-between mb-10">
<text>watch obj.arr result:</text>
<text id="watch-obj-arr-res">{{ watchObjArrRes }}</text>
</view>
<button class="update-obj-btn mb-10" @click="updateObj">
update obj
</button>
</view>
<!-- #ifdef APP -->
</scroll-view>
<!-- #endif -->
</template>
<script lang="uts">
type Obj = {
num : number,
str : string,
bool : boolean,
arr : number[]
}
export default {
data() {
return {
countRef: null as UniTextElement | null,
count: 0,
watchCountRes: '',
watchCountCleanupRes: '',
watchCountTrackNum: 0,
stopWatchCount: () => { },
obj: {
num: 0,
str: 'num: 0',
bool: false,
arr: [0]
} as Obj,
watchObjRes: '',
objStrRef: null as UniTextElement | null,
watchObjStrRes: '',
watchObjStrTriggerNum: 0,
objBoolRef: null as UniTextElement | null,
watchObjBoolRes: '',
watchObjArrRes: '',
}
},
onReady() {
// TODO: app-android this.$watch 返回类型不对
// watchCountTrackNum 各端表现也不一致
const self = this
// #ifdef APP
this.$watch('count',
(count : number, prevCount : number, onCleanup : OnCleanup) => {
this.watchCountRes = `count: ${count}, prevCount: ${prevCount}, count ref text (flush sync): ${(this.$refs['countRef'] as UniTextElement).value}`
const cancel = () => {
this.watchCountCleanupRes = `watch count cleanup: ${count}`
}
onCleanup(cancel)
},
{
// 侦听器在响应式依赖改变时立即触发
flush: 'sync',
// 响应属性或引用作为依赖项被跟踪时调用
onTrack(event : DebuggerEvent) {
if (event.type === 'get') {
self.watchCountTrackNum++
}
}
// TODO: vue>3.4.15 开始 监听函数、onTrack、onTrigger 同时存在修改响应式数据时,会报错 Maximum call stack size exceeded
// 所以将 onTrack 与 onTrigger 调整到两个 watch 里
})
// #endif
// #ifdef WEB
this.stopWatchCount = this.$watch(
'count',
(count : number, prevCount : number, onCleanup : OnCleanup) => {
this.watchCountRes = `count: ${count}, prevCount: ${prevCount}, count ref text (flush sync): ${(this.$refs['countRef'] as UniTextElement).childNodes[0].getAttribute('value')}`
const cancel = () => {
this.watchCountCleanupRes = `watch count cleanup: ${count}`
}
onCleanup(cancel)
},
{
// 侦听器在响应式依赖改变时立即触发
flush: 'sync',
// 响应属性或引用作为依赖项被跟踪时调用
onTrack(event : DebuggerEvent) {
if (event.type === 'get') {
self.watchCountTrackNum++
}
}
// TODO: vue>3.4.15 开始 监听函数、onTrack、onTrigger 同时存在修改响应式数据时,会报错 Maximum call stack size exceeded
// 所以将 onTrack 与 onTrigger 调整到两个 watch 里
})
// #endif
},
watch: {
obj: {
handler(obj : Obj, prevObj ?: Obj) {
if (prevObj == null) {
this.watchObjRes = `obj: {"num":${obj.num},"str":"${obj.str}","bool":${obj.bool},"arr":${JSON.stringify(obj.arr)}}, prevObj: ${JSON.stringify(prevObj)}`
} else {
// #ifdef WEB
this.watchObjRes = `obj: {"num":${obj.num},"str":"${obj.str}","bool":${obj.bool},"arr":${JSON.stringify(obj.arr)}}, prevObj: {"num":${prevObj?.num},"str":"${prevObj?.str}","bool":${prevObj?.bool},"arr":${JSON.stringify(prevObj?.arr)}}`
// #endif
// #ifndef WEB
this.watchObjRes = `obj: {"num":${obj.num},"str":"${obj.str}","bool":${obj.bool},"arr":${JSON.stringify(obj.arr)}}, prevObj: {"num":${prevObj.num},"str":"${prevObj.str}","bool":${prevObj.bool},"arr":${JSON.stringify(prevObj.arr)}}`
// #endif
}
},
// immediate: true 第一次触发, 旧值应该是 undefined, 现在 app 是初始值
immediate: true,
deep: true
},
'obj.str': function (str : string, prevStr : string) {
// #ifdef APP
this.watchObjStrRes = `str: ${str}, prevStr: ${prevStr}, obj.str ref text (flush pre): ${(this.$refs['objStrRef'] as UniTextElement).value}`
// #endif
// #ifdef WEB
this.watchObjStrRes = `str: ${str}, prevStr: ${prevStr}, obj.str ref text (flush pre): ${(this.$refs.objStrRef as UniTextElement).childNodes[0].getAttribute('value')}`
// #endif
},
'obj.bool': {
handler: function (bool : boolean, prevBool : boolean) {
// #ifdef APP
this.watchObjBoolRes = `bool: ${bool}, prevBool: ${prevBool}, obj.bool ref text (flush post): ${(this.$refs['objBoolRef'] as UniTextElement).value}`
// #endif
// #ifdef WEB
this.watchObjBoolRes = `bool: ${bool}, prevBool: ${prevBool}, obj.bool ref text (flush post): ${(this.$refs.objBoolRef as UniTextElement).childNodes[0].getAttribute('value')}`
// #endif
},
// 侦听器延迟到组件渲染之后触发
flush: 'post',
deep: true
},
'obj.arr': {
handler: function (arr : number[], prevArr : number[]) {
this.watchObjArrRes = `arr: ${JSON.stringify(arr)}, prevArr: ${JSON.stringify(prevArr)}`
},
deep: true
}
},
methods: {
triggerStopWatchCount() {
// #ifdef WEB
this.stopWatchCount()
// #endif
},
increment() {
this.count++
},
updateObj() {
this.obj.num++
this.obj.str = `num: ${this.obj.num}`
this.obj.bool = !this.obj.bool
this.obj.arr.push(this.obj.num)
}
}
}
</script>
用于声明由组件触发的自定义事件。
示例 详情
<template>
<view>
<button @click="click" class="call-parent-btn">调用父组件事件</button>
</view>
</template>
<script>
export default {
emits: ['callback'],
methods: {
click () {
this.$emit('callback', `${Date.now()}`)
}
}
}
</script>
<style scoped>
</style>
Web | Android | iOS | |
---|---|---|---|
template | x | x | x |
render | 4.0 | 3.9 | 4.11 |
compilerOptions | x | x | x |
slots | 4.0 | 3.9 | 4.11 |
用于声明组件的字符串模板。
示例 详情
<template>
<view class="page">
<template v-if="dataInfo.isShow">
<text id="title">{{ title }}</text>
</template>
<template v-else>
<text>隐藏标题时显示</text>
</template>
<button id="show-botton" @click="handleShow">{{ dataInfo.isShow ? '点击隐藏' : '点击显示' }}</button>
<template v-for="(item, index) in list" :key="index">
<text :class="'item'">{{ index + 1 }}.{{ item.name }}</text>
</template>
<button @click="goMapStyle">跳转绑定 Map 类型 style 页面</button>
</view>
</template>
<script lang='uts'>
type DataInfo = {
isShow: boolean
}
type objType = {
name : string
}
export default {
data() {
return {
title: "hello",
dataInfo: {
isShow: false,
} as DataInfo,
list: [{
name: 'foo1'
},
{
name: 'foo2'
}
] as objType[]
}
},
methods: {
handleShow() {
this.dataInfo.isShow = !this.dataInfo.isShow
},
goMapStyle() {
uni.navigateTo({ url: '/pages/built-in/special-elements/template/template-map-style-options' })
}
}
}
</script>
<style>
.item {
margin: 15px;
padding: 4px 8px;
border: #eee solid 1px;
}
</style>
用于编程式地创建组件虚拟 DOM 树的函数。
render
是字符串模板的一种替代,可以使你利用 JavaScript 的丰富表达力来完全编程式地声明组件最终的渲染输出。
预编译的模板,例如单文件组件中的模板,会在构建时被编译为 render
选项。如果一个组件中同时存在 render
和 template,则
render
将具有更高的优先级。
示例 详情
<script lang="uts">
import CompForHFunction from '@/components/CompForHFunction.uvue'
import CompForHFunctionWithSlot from '@/components/CompForHFunctionWithSlot.uvue'
import Foo from './Foo.uvue'
// 故意外部声明为UTSJSONObject
const msgProps = { class: 'uni-common-mt msg', style: { color: 'blue' } }
export default {
data() {
return {
msg: 'default msg'
}
},
render() : VNode {
return h('view', { class: 'page' }, [
h(CompForHFunctionWithSlot, {}, () : VNode[] => [h('text', { class: 'comp-slot' }, 'component slot')]),
h(CompForHFunction, { msg: this.msg }),
h('text', msgProps, this.msg),
h(Foo, null, {
header: (): VNode[] => [h('text', { id: "header" }, 'header')],
footer: (): VNode[] => [h('text', { id: "footer" }, 'footer')]
}),
h(
'button',
{
class: 'uni-common-mt btn',
type: 'primary',
onClick: () => {
this.msg = 'new msg'
}
},
'click'
)
])
}
}
</script>
<style>
.btn {
color: red;
}
</style>
一个在渲染函数中以编程方式使用插槽时辅助类型推断的选项。
示例 详情
作用域插槽需在组件中指定插槽数据类型
<template>
<view class="page">
<Foo>
<template #header="{ msg }">
<view class="mb-10 flex justify-between flex-row">
<text>header slot msg:</text>
<text id="slot-header">{{ msg }}</text>
</view>
</template>
<template #default="{ num }">
<view class="mb-10 flex justify-between flex-row">
<text>default slot num:</text>
<text id="slot-default">{{ num }}</text>
</view>
</template>
<template v-for="item in 2" #[`num${item}`]="{ num }">
<view class="mb-10 flex justify-between flex-row">
<text>num{{ item }} slot:</text>
<text :id="`slot-num${item}`">{{ num }}</text>
</view>
</template>
<template v-if="msgTrue['isShow']" #[msgTrue['name']]="{ msg }">
<view class="mb-10 flex justify-between flex-row">
<text>{{ msgTrue['name'] }} slot msg:</text>
<text id="slot-msg-true">{{ msg }}</text>
</view>
</template>
<template v-if="msgFalse['isShow']" #[msgFalse['name']]="{ msg }">
<view class="mb-10 flex justify-between flex-row">
<text>{{ msgFalse['name'] }} slot msg:</text>
<text id="slot-msg-false">{{ msg }}</text>
</view>
</template>
<template #footer="{ arr }">
<view class="mb-10 flex justify-between flex-row">
<text>footer slot arr:</text>
<text id="slot-footer">{{ JSON.stringify(arr) }}</text>
</view>
</template>
</Foo>
</view>
</template>
<script lang="uts">
import Foo from './Foo-options.uvue'
export default {
components: {Foo},
data(){
return {
msgTrue: {
isShow: true,
name: 'msgTrue'
},
msgFalse: {
isShow: false,
name: 'msgFalse'
}
}
}
}
</script>
示例 详情
<template>
<!-- #ifdef APP -->
<scroll-view style="flex: 1" :bounces="false">
<!-- #endif -->
<view class="page container">
<text>page lifecycle 选项式 API</text>
<view class="flex flex-row justify-between mt-10">
<text>onLoad 触发:</text>
<text>{{ isOnloadTriggered }}</text>
</view>
<view class="flex flex-row justify-between mt-10">
<text>onShow 触发:</text>
<text>{{ isOnShowTriggered }}</text>
</view>
<view class="flex flex-row justify-between mt-10">
<text>onReady 触发:</text>
<text>{{ isOnReadyTriggered }}</text>
</view>
<view class="flex flex-row justify-between mt-10">
<text>onPullDownRefresh 触发:</text>
<text>{{ isOnPullDownRefreshTriggered }}</text>
</view>
<view class="flex flex-row justify-between mt-10">
<text>onReachBottom 触发:</text>
<text>{{ isOnReachBottomTriggered }}</text>
</view>
<view class="flex flex-row justify-between mt-10">
<text>onBackPress 触发:</text>
<text>{{ isOnBackPressTriggered }}</text>
</view>
<view class="flex flex-row justify-between mt-10">
<text>onHide 触发:</text>
<text>{{ isOnHideTriggered }}</text>
</view>
<view class="flex flex-row justify-between mt-10">
<text>onResize 触发:</text>
<text>{{ isOnResizeTriggered }}</text>
</view>
<MonitorPageLifecycleOptions />
<button class="mt-10" @click="scrollToBottom">scrollToBottom</button>
<button class="mt-10" @click="pullDownRefresh">
trigger pullDownRefresh
</button>
<button class="mt-10" @click="goOnBackPress">
跳转 onBackPress 示例
</button>
</view>
<!-- #ifdef APP -->
</scroll-view>
<!-- #endif -->
</template>
<script lang="uts">
import { state, setLifeCycleNum } from '@/store/index.uts'
import MonitorPageLifecycleOptions from './monitor-page-lifecycle-options.uvue'
type DataInfo = {
isScrolled: boolean
}
export default {
components: { MonitorPageLifecycleOptions },
data() {
return {
isOnloadTriggered: false,
isOnShowTriggered: false,
isOnReadyTriggered: false,
isOnPullDownRefreshTriggered: false,
isOnPageScrollTriggered: false,
isOnReachBottomTriggered: false,
isOnBackPressTriggered: false,
isOnHideTriggered: false,
isOnResizeTriggered: false,
dataInfo: {
isScrolled: false,
} as DataInfo
}
},
onLoad(options : OnLoadOptions) {
console.log('onLoad', options)
this.isOnloadTriggered = true
// 自动化测试
setLifeCycleNum(state.lifeCycleNum + 100)
},
onShow() {
this.isOnShowTriggered = true
// 自动化测试
setLifeCycleNum(state.lifeCycleNum + 10)
},
onReady() {
this.isOnReadyTriggered = true
// 自动化测试
setLifeCycleNum(state.lifeCycleNum + 10)
},
onPullDownRefresh() {
this.isOnPullDownRefreshTriggered = true
// 自动化测试
setLifeCycleNum(state.lifeCycleNum + 10)
},
onPageScroll(e: OnPageScrollOptions) {
console.log('onPageScroll', e)
this.isOnPageScrollTriggered = true
// 自动化测试
this.dataInfo.isScrolled = true
},
onReachBottom() {
this.isOnReachBottomTriggered = true
// 自动化测试
setLifeCycleNum(state.lifeCycleNum + 10)
},
onBackPress(options : OnBackPressOptions) : boolean | null {
console.log('onBackPress', options)
this.isOnBackPressTriggered = true
// 自动化测试
setLifeCycleNum(state.lifeCycleNum - 10)
return null
},
onHide() {
this.isOnHideTriggered = true
// 自动化测试
setLifeCycleNum(state.lifeCycleNum - 10)
},
onUnload() {
// 自动化测试
setLifeCycleNum(state.lifeCycleNum - 100)
},
onResize(options: OnResizeOptions) {
console.log('onBackPress', options)
this.isOnResizeTriggered = true
// 自动化测试
setLifeCycleNum(state.lifeCycleNum + 10)
},
methods: {
// 自动化测试
pageGetLifeCycleNum() : number {
return state.lifeCycleNum
},
// 自动化测试
pageSetLifeCycleNum(num : number) {
setLifeCycleNum(num)
},
// 自动化测试
pullDownRefresh() {
uni.startPullDownRefresh({
success() {
setTimeout(() => {
uni.stopPullDownRefresh()
// 一秒后立即停止下拉刷新不会触发 onPullDownRefresh,因为下拉动画时间大概需要1.1~1.2秒
}, 1500)
},
})
},
scrollToBottom() {
uni.pageScrollTo({
scrollTop: 2000,
})
},
goOnBackPress() {
uni.navigateTo({url: '/pages/lifecycle/page/onBackPress/on-back-press-options'})
}
},
}
</script>
<style>
.container {
height: 1200px;
}
</style>
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 完成。 |
目前 mounted、unmounted 可以保证当前数据已经同步到 DOM,但是由于排版和渲染是异步的的,所以 mounted、unmounted 不能保证 DOM 排版以及渲染完毕。
如果需要获取排版后的节点信息推荐使用 uni.createSelectorQuery 不推荐直接使用 Element 对象。
在修改 DOM 后,立刻使用 Element 对象的同步接口获取 DOM 状态可能获取到的是排版之前的,而 uni.createSelectorQuery 可以保障获取到的节点信息是排版之后的。
当 A 页面存在 keepAlive
组件,A 页面 navigateTo
B 页面时
keepAlive
的组件会触发 deactivated
生命周期keepAlive
的组件不会触发 deactivated
生命周期当 B 页面 back 返回 A 页面时
keepAlive
的组件会触发 activated
生命周期keepAlive
的组件不会触发 activated
生命周期示例 详情
<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>
Web | Android | iOS | |
---|---|---|---|
provide | 4.0 | 3.99 | 4.11 |
inject | 4.0 | 3.99 | 4.11 |
mixins | 4.0 | 3.99 | 4.11 |
extends | - | - | - |
当使用 inject
声明从上层提供方注入的属性时,支持两种写法:字符串数组和对象。推荐使用对象写法,因为当使用数组方法时,类型会被推导为 any | null
类型。
使用对象写法时,额外增加 type
属性用于标记类型。如果注入的属性类型不是基础数据类型,需要通过 PropType
来标记类型:
示例 详情
inject 1
inject 2
<template>
<view>
<text class="mt-10 bold">component for inject 1</text>
<text class="mt-10 alias-provide-page-title"
>aliasProvidePageTitle: {{ aliasProvidePageTitle }}</text
>
<text class="mt-10 provide-page-str"
>providePageStr: {{ providePageStr }}</text
>
<text class="mt-10 provide-page-num"
>providePageNum: {{ providePageNum }}</text
>
<text class="mt-10 provide-page-bool"
>providePageBool: {{ providePageBool }}</text
>
<text class="mt-10 provide-page-object-title"
>providePageObject.title: {{ providePageObject['title'] }}</text
>
<text class="mt-10 provide-page-object-content"
>providePageObject.content: {{ providePageObject['content'] }}</text
>
<text class="mt-10 provide-page-arr"
>providePageArr: {{ JSON.stringify(providePageArr) }}</text
>
<text class="mt-10 provide-page-map"
>providePageMap: {{ JSON.stringify(providePageMapObj) }}</text
>
<text class="mt-10 provide-page-set"
>providePageSet: {{ JSON.stringify(providePageSetArr) }}</text
>
<text class="mt-10 test-inject-string-default-value"
>testInjectStringDefaultValue: {{ testInjectStringDefaultValue }}</text
>
<text class="mt-10 test-inject-object-default-value-title"
>testInjectObjectDefaultValue.title:
{{ testInjectObjectDefaultValue['title'] }}</text
>
<text class="mt-10 test-inject-object-default-value-content"
>testInjectObjectDefaultValue.content:
{{ testInjectObjectDefaultValue['content'] }}</text
>
</view>
</template>
<script lang="uts">
export default {
inject: {
aliasProvidePageTitle: {
type: String,
from: 'providePageTitle',
default: 'default alias provide page title'
},
providePageStr: {
type: String,
default: 'default provide page str'
},
providePageNum: {
type: Number,
default: 0
},
providePageBool: {
type: Boolean,
default: false
},
providePageObject: {
type: UTSJSONObject,
default: (): UTSJSONObject => {
return {
title: 'default provide page object title',
content: 'default provide page object content'
}
}
},
providePageArr: {
type: Array as PropType<String[]>,
default: (): String[] => {
return ['default provide page arr']
}
},
providePageMap: {
type: Object as PropType<Map<string, string>>,
default: (): Map<string, string> => {
return new Map<string, string>([['key', 'default provide page map']])
}
},
providePageSet: {
type: Object as PropType<Set<string>>,
default: (): Set<string> => {
return new Set<string>(['default provide page set'])
}
},
testInjectStringDefaultValue: {
type: String,
default: 'test inject string default value'
},
testInjectObjectDefaultValue: {
type: UTSJSONObject,
default(): UTSJSONObject {
return {
title: 'test inject object default value title',
content: 'test inject object default value content'
}
}
}
},
computed: {
providePageMapObj(): UTSJSONObject {
const obj: UTSJSONObject = {}
this.providePageMap.forEach((value, key) => {
obj[key] = value
})
return obj
},
providePageSetArr(): string[] {
const arr: string[] = []
this.providePageSet.forEach((value) => {
arr.push(value)
})
return arr
}
}
}
</script>
一个包含组件选项对象的数组,这些选项都将被混入到当前组件的实例中。
mixins
选项接受一个 mixin 对象数组。这些 mixin 对象可以像普通的实例对象一样包含实例选项,它们将使用一定的选项合并逻辑与最终的选项进行合并。举例来说,如果你的 mixin 包含了一个 created
钩子,而组件自身也有一个,那么这两个函数都会被调用。
mixins
仅支持通过字面量对象方式和 defineMixin
函数方式定义。App.uvue
不支持 mixins
, 全局 mixins 也不会对 App.uvue
生效。const mixin1 = defineMixin({
onLoad() {
console.log('mixin1 onLoad')
}
})
export default {
mixins: [
mixin1,
{
data() {
return {
mixin2: 'mixin2'
}
}
}
]
}
app.mixin
内嵌入的 mixin <
在 app.mixin
中声明的 mixin <
在 page.mixin
内嵌入的 mixin <
在 page.mixin
中声明的 mixin <
在 component.mixin
内嵌入的 mixin <
在 component.mixin
中声明的 mixinapp.mixin
内嵌入的 mixinapp.mixin
中声明的 mixinpage.mixin
内嵌入的 mixinpage.mixin
中声明的 mixincomponent.mixin
内嵌入的 mixincomponent.mixin
中声明的 mixinmixins-web
mixins-app-page-namesake
mixins-app
<template>
<!-- #ifdef APP -->
<scroll-view style="flex: 1">
<!-- #endif -->
<view class="page">
<text id="mixin-prop" class="mb-10">mixinProp: {{mixinProp}}</text>
<text id="mixin-data-msg" class="mb-10">mixinDataMsg: {{mixinDataMsg}}</text>
<text id="mixin-onload-msg" class="mb-10">mixinOnloadMsg: {{mixinOnloadMsg}}</text>
<text id="mixin-computed" class="mb-10">mixinComputed: {{mixinComputed}}</text>
<Comp1 title="title" @globalMixinEmit1="(arg: string) => handleMixinEmitter('globalMixinEmit1', arg)"
@globalChildMixinEmit1="(arg: string) => handleMixinEmitter('globalChildMixinEmit1', arg)"
@globalMixinEmit2="(arg: string) => handleMixinEmitter('globalMixinEmit2', arg)"
@globalChildMixinEmit2="(arg: string) => handleMixinEmitter('globalChildMixinEmit2', arg)"
@mixinEmit="(arg: string) => handleMixinEmitter('mixinEmit', arg)"
@childMixinEmit="(arg: string) => handleMixinEmitter('childMixinEmit', arg)" />
<text v-if="handleMixinEmitterMsg" class="mt-10 handle-mixin-emitter-msg">
handleMixinEmitterMsg: {{ handleMixinEmitterMsg }}
</text>
<Comp2 class='comp2' title="title" />
</view>
<!-- #ifdef APP -->
</scroll-view>
<!-- #endif -->
</template>
<script lang="uts">
import Comp1 from './components/Comp1.uvue'
import Comp2 from './components/Comp2.uvue'
export default {
components: {
Comp1,
Comp2,
},
mixins: [defineMixin({
props: {
mixinProp: {
type: String,
default: '通过字面量定义非全局 mixin props'
}
},
data() {
return {
handleMixinEmitterMsg: '',
mixinDataMsg: '通过字面量定义非全局 mixin data',
mixinOnloadMsg: ''
}
},
computed: {
mixinComputed(): string {
const res = `通过字面量定义非全局 mixin computed, 更新后的 mixinOnloadMsg: ${this.mixinOnloadMsg}`
console.log(res)
return res
}
},
onLoad() {
this.mixinOnloadMsg = 'mixin onLoad msg in onLoad'
},
methods: {
mixinMethod(): string {
const res = '通过字面量定义非全局 mixin method'
console.log(res)
return res
},
handleMixinEmitter(emit: string, arg: string) {
this.handleMixinEmitterMsg = `触发 ${emit}, 参数为 ${arg}`
console.log(this.handleMixinEmitterMsg)
}
},
})]
}
</script>
Web | Android | iOS | |
---|---|---|---|
name | 4.0 | 3.9 | 4.11 |
inheritAttrs | 4.0 | 3.9 | 4.11 |
components | 4.0 | 3.9 | 4.11 |
directives | - | - | - |
用于显式声明组件展示时的名称。
组件的名字有以下用途:
示例 详情
<template>
<view class="page">
<!-- #ifdef APP-ANDROID -->
<!-- TODO: ios & web 不支持 a b 互相引用 -->
<child-a :limit="5" />
<!-- #endif -->
<child-c :limit="5" />
</view>
</template>
<script lang='uts'>
// #ifdef APP-ANDROID
import ChildA from './childA-options.uvue'
// #endif
import childC from './childC-options.uvue'
export default {
components: {
// #ifdef APP-ANDROID
ChildA,
// #endif
childC
}
}
</script>
用于控制是否启用默认的组件 attribute 透传行为。
默认情况下,父组件传递的,但没有被子组件解析为 props 的 attributes 绑定会被“透传”。这意味着当我们有一个单根节点的子组件时,这些绑定会被作为一个常规的 attribute 应用在子组件的根节点元素上。当你编写的组件想要在一个目标元素或其他组件外面包一层时,可能并不期望这样的行为。我们可以通过设置 inheritAttrs
为 false
来禁用这个默认行为。这些 attributes 可以通过 $attrs
这个实例属性来访问,并且可以通过 v-bind
来显式绑定在一个非根节点的元素上。
示例 详情
inheritAttrs: true
inheritAttrs: false
<template>
<view class="mt-10" ref="mixin-comp-root">
<text class="bold">Comp1: inheritAttrs: false</text>
<text class="mt-10" style="color:#ccc;"
>rootElementTitle should be null</text
>
<text class="mt-10 root-element-title-1">rootElementTitle: {{ rootElementTitle }}</text>
<!-- #ifdef APP -->
<text class="mt-10 bold">trigger emitter:</text>
<button class="mt-10 global-mixin-emit-1" @click="triggerEmitter('globalMixinEmit1')">
trigger globalMixinEmit1
</button>
<button
class="mt-10 global-child-mixin-emit-1"
@click="triggerEmitter('globalChildMixinEmit1')">
trigger globalChildMixinEmit1
</button>
<button class="mt-10 global-mixin-emit-2" @click="triggerEmitter('globalMixinEmit2')">
trigger globalMixinEmit2
</button>
<button
class="mt-10 global-child-mixin-emit-2"
@click="triggerEmitter('globalChildMixinEmit2')">
trigger globalChildMixinEmit2
</button>
<button class="mt-10 mixin-emit" @click="triggerEmitter('mixinEmit')">
trigger mixinEmit
</button>
<button class="mt-10 child-mixin-emit" @click="triggerEmitter('childMixinEmit')">
trigger childMixinEmit
</button>
<MixinComp />
<!-- #endif -->
</view>
</template>
<script lang="uts">
export default {
mixins:[{
mixins: [{
emits: ['childMixinEmit']
}],
inheritAttrs: false,
emits:['mixinEmit']
}],
data(){
return {
rootElementTitle: '' as string | null
}
},
mounted(){
const rootElement = this.$refs['mixin-comp-root'] as UniElement
this.rootElementTitle = JSON.stringify(rootElement.getAttribute('title'))
},
methods: {
triggerEmitter(emit: string){
this.$emit(emit, emit)
},
}
}
</script>
一个对象,用于注册对当前组件实例可用的组件。
示例 详情
<template>
<view class="page">
<child
class="child-class"
str="str from parent"
@childClick="() => {}" />
</view>
</template>
<script lang="uts">
import child from './child-options.uvue'
export default {
components: {
child,
},
}
</script>
Web | Android | iOS | |
---|---|---|---|
$data | 4.0 | √ | 4.11 |
$props | 4.0 | √ | 4.11 |
$el | 4.0 | √ | 4.11 |
$options | 4.0 | √ | 4.11 |
$parent | 4.0 | √ | 4.11 |
$root | 4.0 | √ | 4.11 |
$slots | 4.0 | √ | 4.11 |
$refs | 4.0 | √ | 4.11 |
$attrs | 4.0 | √ | 4.11 |
$watch() | 4.0 | √ | 4.11 |
$emit | 4.0 | √ | 4.11 |
$forceUpdate | 4.0 | √ | 4.11 |
$nextTick | 4.0 | √ | 4.11 |
$callMethod | 4.0 | √ | 4.11 |
从 data
选项函数中返回的对象,会被组件赋为响应式。组件实例将会代理对其数据对象的属性访问。
data内 $ 开头的属性不可直接使用 this.$xxx
访问,需要使用 this.$data['$xxx']
,这是vue的规范
目前安卓端可以使用 this.$xxx 访问是Bug而非特性,请勿使用此特性。
示例
<template>
<view></view>
</template>
<script>
export default {
data() {
return {
$a: 1
}
},
onReady() {
console.log(this.$data['$a'] as number) // 1
}
}
</script>
示例 详情
<template>
<view class="page">
<view class="flex justify-between flex-row mb-10">
<text>str: </text>
<text id="str">{{ str }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>num: </text>
<text id="num">{{ num }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>arr: </text>
<text id="arr">{{ arr.join(',') }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>obj.str: </text>
<text id="obj-str">{{ obj.str }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>obj.num: </text>
<text id="obj-num">{{ obj.num }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>obj.arr: </text>
<text id="obj-arr">{{ obj.arr.join(',') }}</text>
</view>
<view ref='htmlRef' id="idRef" class="flex justify-between flex-row mb-10">
<text>data 存储 element不需要被包装</text>
<text id="isSameRefText">{{ refElementIsSame }}</text>
</view>
<button @click="updateData">update data</button>
</view>
</template>
<script lang="uts">
type Obj = {
str : string,
num : number,
arr : number[]
}
export default {
data() {
return {
str: 'default str',
num: 0,
arr: [1, 2, 3],
// 特殊类型需要通过 as 指定类型
obj: {
str: 'default obj.str',
num: 10,
arr: [4, 5, 6]
} as Obj,
refElement: null as UniElement | null,
refElementIsSame: false
}
},
methods: {
refTest() {
const queryElementById1 = uni.getElementById('idRef')
const queryElementById2 = uni.getElementById('idRef')
const htmlRefElement = this.$refs['htmlRef'] as UniElement;
this.refElement = htmlRefElement
if (queryElementById1 === queryElementById2
&& queryElementById1 === htmlRefElement
&& queryElementById1 === this.refElement) {
this.refElementIsSame = true
}
},
updateData() {
this.str = 'new str'
this.num = 1
this.arr = [4, 5, 6]
this.obj.str = 'new obj.str'
this.obj.num = 100
this.obj.arr = [7, 8, 9]
this.refTest()
},
},
}
</script>
表示组件当前已解析的 props 对象。
示例 详情
<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>
该组件实例管理的 DOM 根节点。
示例 详情
<template>
<view class="page flex justify-between flex-row">
<text class="child">root node tagName:</text>
<text class="tag-name">{{ el }}</text>
</view>
</template>
<script lang='uts'>
export default {
data() {
return {
el: '',
}
},
mounted() {
this.el = this.$el?.nodeName ?? ''
},
}
</script>
已解析的用于实例化当前组件的组件选项。
示例 详情
<template>
<view class="page">
<view class="mb-10 flex justify-between flex-row">
<text>component name: </text>
<text id="component-name">{{ dataInfo.name }}</text>
</view>
<!-- #ifndef APP-ANDROID -->
<view class="mb-10 flex justify-between flex-row">
<text>custom key: </text>
<text id="custom-key">{{ dataInfo.customKey }}</text>
</view>
<view class="mb-10 flex justify-between flex-row">
<text>mixin data str: </text>
<text id="mixin-data-str">{{ dataInfo.mixinDataStr }}</text>
</view>
<!-- #endif -->
</view>
</template>
<script lang="uts">
import mixins from "./mixins.uts"
type DataInfo = {
name: string
customKey: string
mixinDataStr: string
}
export default {
mixins: [mixins],
name: "$options",
_customKey: "custom key",
data() {
return {
dataInfo: {
name: "",
customKey: "",
mixinDataStr: "",
} as DataInfo
}
},
mounted() {
this.dataInfo.name = this.$options.name!
// #ifndef APP-ANDROID
this.dataInfo.customKey = this.$options._customKey
// @ts-ignore
this.dataInfo.mixinDataStr = this.$options.data({})['str']
// #endif
},
}
</script>
当前组件可能存在的父组件实例,如果当前组件是顶层组件,则为 null
。
示例 详情
<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>
当前组件树的根组件实例。如果当前实例没有父组件,那么这个值就是它自己。
示例 详情
<template>
<view class="page">
<view class="mb-10 flex justify-between flex-row">
<text>root str in parent component: </text>
<text id="root-str-parent">{{ rootStr }}</text>
</view>
<child />
</view>
</template>
<script lang="uts">
import Child from './child-options.uvue'
export default {
components: {Child},
data () {
return {
str: 'root component str',
rootStr: ''
}
},
onReady() {
this.rootStr = this.$root!.$data['str'] as string
}
}
</script>
一个表示父组件所传入插槽的对象。
示例 详情
<template>
<view class="page">
<slot-comp class="slot-comp">
<template v-slot:header>header</template>
<template v-slot:default>default</template>
<template v-slot:footer>footer</template>
</slot-comp>
</view>
</template>
<script lang="uts">
import slot from './slot-options.uvue'
export default {
components: {
slotComp: slot
}
}
</script>
<style>
.row {
display: flex;
flex-direction: row;
justify-content: space-between;
margin-bottom: 10px;
}
</style>
一个包含 DOM 元素和组件实例的对象,通过模板引用注册。
示例 详情
<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>
一个包含了组件所有透传 attributes 的对象。
示例 详情
<template>
<view class="page">
<child
class="child-class"
str="str from parent"
@childClick="() => {}" />
</view>
</template>
<script lang="uts">
import child from './child-options.uvue'
export default {
components: {
child,
},
}
</script>
用于命令式地创建侦听器的 API。
示例 详情
<template>
<!-- #ifdef APP -->
<scroll-view style="flex: 1; padding-bottom: 20px">
<!-- #endif -->
<view class="page">
<view class="flex justify-between flex-row mb-10">
<text>count:</text>
<text id="count" ref="countRef">{{ count }}</text>
</view>
<view class="flex justify-between mb-10">
<text>watch count result:</text>
<text id="watch-count-res">{{ watchCountRes }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>watch count track number:</text>
<text id="watch-count-track-num">{{ watchCountTrackNum }}</text>
</view>
<view class="flex justify-between mb-10">
<text>watch count cleanup number:</text>
<text id="watch-count-cleanup-res">{{ watchCountCleanupRes }}</text>
</view>
<button class="increment-btn mb-10" @click="increment">increment</button>
<button class="stop-watch-count-btn mb-10" @click="triggerStopWatchCount">
stop watch count
</button>
<view class="flex justify-between flex-row mb-10">
<text>obj.str:</text>
<text id="obj-str" ref="objStrRef">{{ obj.str }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>watch obj.str trigger number:</text>
<text id="watch-obj-str-trigger-num">{{ watchObjStrTriggerNum }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>obj.num:</text>
<text id="obj-num">{{ obj.num }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>obj.bool:</text>
<text id="obj-bool" ref="objBoolRef">{{ obj.bool }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>obj.arr:</text>
<text id="obj-arr" ref="objArrRef">{{ JSON.stringify(obj.arr) }}</text>
</view>
<view class="flex justify-between mb-10">
<text>watch obj result:</text>
<text id="watch-obj-res">{{ watchObjRes }}</text>
</view>
<view class="flex justify-between mb-10">
<text>watch obj.str result:</text>
<text id="watch-obj-str-res">{{ watchObjStrRes }}</text>
</view>
<view class="flex justify-between mb-10">
<text>watch obj.bool result:</text>
<text id="watch-obj-bool-res">{{ watchObjBoolRes }}</text>
</view>
<view class="flex justify-between mb-10">
<text>watch obj.arr result:</text>
<text id="watch-obj-arr-res">{{ watchObjArrRes }}</text>
</view>
<button class="update-obj-btn mb-10" @click="updateObj">
update obj
</button>
</view>
<!-- #ifdef APP -->
</scroll-view>
<!-- #endif -->
</template>
<script lang="uts">
type Obj = {
num : number,
str : string,
bool : boolean,
arr : number[]
}
export default {
data() {
return {
countRef: null as UniTextElement | null,
count: 0,
watchCountRes: '',
watchCountCleanupRes: '',
watchCountTrackNum: 0,
stopWatchCount: () => { },
obj: {
num: 0,
str: 'num: 0',
bool: false,
arr: [0]
} as Obj,
watchObjRes: '',
objStrRef: null as UniTextElement | null,
watchObjStrRes: '',
watchObjStrTriggerNum: 0,
objBoolRef: null as UniTextElement | null,
watchObjBoolRes: '',
watchObjArrRes: '',
}
},
onReady() {
// TODO: app-android this.$watch 返回类型不对
// watchCountTrackNum 各端表现也不一致
const self = this
// #ifdef APP
this.$watch('count',
(count : number, prevCount : number, onCleanup : OnCleanup) => {
this.watchCountRes = `count: ${count}, prevCount: ${prevCount}, count ref text (flush sync): ${(this.$refs['countRef'] as UniTextElement).value}`
const cancel = () => {
this.watchCountCleanupRes = `watch count cleanup: ${count}`
}
onCleanup(cancel)
},
{
// 侦听器在响应式依赖改变时立即触发
flush: 'sync',
// 响应属性或引用作为依赖项被跟踪时调用
onTrack(event : DebuggerEvent) {
if (event.type === 'get') {
self.watchCountTrackNum++
}
}
// TODO: vue>3.4.15 开始 监听函数、onTrack、onTrigger 同时存在修改响应式数据时,会报错 Maximum call stack size exceeded
// 所以将 onTrack 与 onTrigger 调整到两个 watch 里
})
// #endif
// #ifdef WEB
this.stopWatchCount = this.$watch(
'count',
(count : number, prevCount : number, onCleanup : OnCleanup) => {
this.watchCountRes = `count: ${count}, prevCount: ${prevCount}, count ref text (flush sync): ${(this.$refs['countRef'] as UniTextElement).childNodes[0].getAttribute('value')}`
const cancel = () => {
this.watchCountCleanupRes = `watch count cleanup: ${count}`
}
onCleanup(cancel)
},
{
// 侦听器在响应式依赖改变时立即触发
flush: 'sync',
// 响应属性或引用作为依赖项被跟踪时调用
onTrack(event : DebuggerEvent) {
if (event.type === 'get') {
self.watchCountTrackNum++
}
}
// TODO: vue>3.4.15 开始 监听函数、onTrack、onTrigger 同时存在修改响应式数据时,会报错 Maximum call stack size exceeded
// 所以将 onTrack 与 onTrigger 调整到两个 watch 里
})
// #endif
},
watch: {
obj: {
handler(obj : Obj, prevObj ?: Obj) {
if (prevObj == null) {
this.watchObjRes = `obj: {"num":${obj.num},"str":"${obj.str}","bool":${obj.bool},"arr":${JSON.stringify(obj.arr)}}, prevObj: ${JSON.stringify(prevObj)}`
} else {
// #ifdef WEB
this.watchObjRes = `obj: {"num":${obj.num},"str":"${obj.str}","bool":${obj.bool},"arr":${JSON.stringify(obj.arr)}}, prevObj: {"num":${prevObj?.num},"str":"${prevObj?.str}","bool":${prevObj?.bool},"arr":${JSON.stringify(prevObj?.arr)}}`
// #endif
// #ifndef WEB
this.watchObjRes = `obj: {"num":${obj.num},"str":"${obj.str}","bool":${obj.bool},"arr":${JSON.stringify(obj.arr)}}, prevObj: {"num":${prevObj.num},"str":"${prevObj.str}","bool":${prevObj.bool},"arr":${JSON.stringify(prevObj.arr)}}`
// #endif
}
},
// immediate: true 第一次触发, 旧值应该是 undefined, 现在 app 是初始值
immediate: true,
deep: true
},
'obj.str': function (str : string, prevStr : string) {
// #ifdef APP
this.watchObjStrRes = `str: ${str}, prevStr: ${prevStr}, obj.str ref text (flush pre): ${(this.$refs['objStrRef'] as UniTextElement).value}`
// #endif
// #ifdef WEB
this.watchObjStrRes = `str: ${str}, prevStr: ${prevStr}, obj.str ref text (flush pre): ${(this.$refs.objStrRef as UniTextElement).childNodes[0].getAttribute('value')}`
// #endif
},
'obj.bool': {
handler: function (bool : boolean, prevBool : boolean) {
// #ifdef APP
this.watchObjBoolRes = `bool: ${bool}, prevBool: ${prevBool}, obj.bool ref text (flush post): ${(this.$refs['objBoolRef'] as UniTextElement).value}`
// #endif
// #ifdef WEB
this.watchObjBoolRes = `bool: ${bool}, prevBool: ${prevBool}, obj.bool ref text (flush post): ${(this.$refs.objBoolRef as UniTextElement).childNodes[0].getAttribute('value')}`
// #endif
},
// 侦听器延迟到组件渲染之后触发
flush: 'post',
deep: true
},
'obj.arr': {
handler: function (arr : number[], prevArr : number[]) {
this.watchObjArrRes = `arr: ${JSON.stringify(arr)}, prevArr: ${JSON.stringify(prevArr)}`
},
deep: true
}
},
methods: {
triggerStopWatchCount() {
// #ifdef WEB
this.stopWatchCount()
// #endif
},
increment() {
this.count++
},
updateObj() {
this.obj.num++
this.obj.str = `num: ${this.obj.num}`
this.obj.bool = !this.obj.bool
this.obj.arr.push(this.obj.num)
}
}
}
</script>
在当前组件触发一个自定义事件。任何额外的参数都会传递给事件监听器的回调函数。
示例 详情
<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>
强制该组件重新渲染。
示例 详情
<template>
<view class="page">
<view class="split-title">$forceUpdate</view>
<text class="uni-common-mt time">Date.now(): {{ Date.now() }}</text>
<button
class="uni-common-mt trigger-force-update-btn"
type="primary"
@click="triggerForceUpdate">
trigger $forceUpdate
</button>
</view>
</template>
<script lang="uts">
export default {
methods: {
triggerForceUpdate(){
this.$forceUpdate()
}
}
}
</script>
绑定在实例上的 nextTick() 函数。
目前 $nextTick 可以保证当前数据已经同步到 DOM,但是由于排版和渲染是异步的,所以 $nextTick 不能保证 DOM 排版以及渲染完毕。
如果需要获取排版后的节点信息推荐使用 uni.createSelectorQuery 不推荐直接使用 Element 对象。
在修改 DOM 后,立刻使用 Element 对象的同步接口获取 DOM 状态可能获取到的是排版之前的,而 uni.createSelectorQuery 可以保障获取到的节点信息是排版之后的。
示例 详情
<template>
<!-- #ifdef APP -->
<scroll-view style="flex: 1">
<!-- #endif -->
<view class="page">
<view class="flex justify-between mb-10">
<text ref="text">title for callback:</text>
<text id="page-text-callback">{{ dataInfo.titleForCallback }}</text>
</view>
<view class="flex justify-between mb-10">
<text ref="text">before $nextTick callback title:</text>
<text>{{ dataInfo.beforeNextTickCallbackTitle }}</text>
</view>
<view class="flex justify-between mb-10">
<text ref="text">after $nextTick callback title:</text>
<text>{{ dataInfo.afterNextTickCallbackTitle }}</text>
</view>
<view class="flex justify-between mb-10">
<text ref="text">title for promise:</text>
<text id="page-text-promise">{{ dataInfo.titleForPromise }}</text>
</view>
<view class="flex justify-between mb-10">
<text ref="text">before $nextTick promise title:</text>
<text>{{ dataInfo.beforeNextTickPromiseTitle }}</text>
</view>
<view class="flex justify-between mb-10">
<text ref="text">after $nextTick promise title:</text>
<text>{{ dataInfo.afterNextTickPromiseTitle }}</text>
</view>
<button id="page-test-next-tick-btn" @click="pageTestNextTick">page test $nextTick</button>
<Child id="child-component" />
</view>
<!-- #ifdef APP -->
</scroll-view>
<!-- #endif -->
</template>
<script lang="uts">
import Child from './child-options.uvue'
type DataInfo = {
titleForCallback : string
beforeNextTickCallbackTitle : string
afterNextTickCallbackTitle : string
titleForPromise : string
beforeNextTickPromiseTitle : string
afterNextTickPromiseTitle : string
}
export default {
components: {
Child
},
data() {
return {
dataInfo: {
titleForCallback: 'default title for callback',
beforeNextTickCallbackTitle: '',
afterNextTickCallbackTitle: '',
titleForPromise: 'default title for promise',
beforeNextTickPromiseTitle: '',
afterNextTickPromiseTitle: '',
} as DataInfo
}
},
methods: {
pageTestNextTick() {
this.nextTickCallback()
this.nextTickPromise()
},
nextTickCallback() {
const pageText = uni.getElementById('page-text-callback')!
this.dataInfo.titleForCallback = 'new title for callback'
// #ifdef APP
this.dataInfo.beforeNextTickCallbackTitle = pageText.getAttribute('value')!
// #endif
// #ifndef APP
// @ts-ignore
this.dataInfo.beforeNextTickCallbackTitle = pageText.textContent
// #endif
this.$nextTick(() => {
// #ifdef APP
this.dataInfo.afterNextTickCallbackTitle = pageText.getAttribute('value')!
// #endif
// #ifndef APP
// @ts-ignore
this.dataInfo.afterNextTickCallbackTitle = pageText.textContent
// #endif
})
},
nextTickPromise() {
const pageText = uni.getElementById('page-text-promise')!
this.dataInfo.titleForPromise = 'new title for promise'
// #ifdef APP
this.dataInfo.beforeNextTickPromiseTitle = pageText.getAttribute('value')!
// #endif
// #ifndef APP
// @ts-ignore
this.dataInfo.beforeNextTickPromiseTitle = pageText.textContent
// #endif
this.$nextTick().then(() => {
// #ifdef APP
this.dataInfo.afterNextTickPromiseTitle = pageText.getAttribute('value')!
// #endif
// #ifndef APP
// @ts-ignore
this.dataInfo.afterNextTickPromiseTitle = pageText.textContent
// #endif
})
}
}
}
</script>