简体中文
注意
<script setup>
和 <script>
同时使用,如果需要配置 options
内容,比如 name
,可以使用 defineOptions
。await
。<script setup>
配置 generic
泛型类型参数。App.uvue
暂不支持组合式 API。Web | Android | iOS | |
---|---|---|---|
ref() | 4.0 | √ | 4.11 |
computed() | 4.0 | √ | 4.11 |
reactive() | 4.0 | √ | 4.11 |
readonly() | 4.0 | 4.0 | 4.11 |
watchEffect() | 4.0 | 4.0 | 4.11 |
watchPostEffect() | 4.0 | 4.0 | 4.11 |
watchSyncEffect() | 4.0 | 4.0 | 4.11 |
watch() | 4.0 | 4.0 | 4.11 |
接受一个内部值,返回一个响应式的、可更改的 ref 对象,此对象只有一个指向其内部值的属性 .value
。
示例 详情
<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>str:</text>
<text id="str">{{ str }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>bool:</text>
<text id="bool">{{ bool }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>arr:</text>
<text id="arr">{{ JSON.stringify(arr) }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>counter.count:</text>
<text id="counter-count">{{ counter.count }}</text>
</view>
<button class='mb-10' id="change-count-btn" @click="changeCount">change count</button>
<button class='mb-10' id='change-str-btn' @click='changeStr'>change str</button>
<button class='mb-10' id='change-bool-btn' @click='changeBool'>change bool</button>
<button class='mb-10' id='change-arr-btn' @click='changeArr'>change arr</button>
<button class='mb-10' id='change-counter-btn' @click='changeCounter'>change counter</button>
</view>
</template>
<script setup lang="uts">
// 基础数据类型可自动推导类型
const count = ref(0)
const str = ref('default str')
const bool = ref(false)
// 可通过泛型指定类型
const arr = ref<number[]>([1, 2, 3])
type Counter = {
count : number
}
// 可通过泛型指定类型
const counter = ref<Counter>({
count: 0
})
const changeCount = () => {
count.value++
}
const changeStr = () => {
str.value = 'new str'
}
const changeBool = () => {
bool.value = !bool.value
}
const changeArr = () => {
arr.value.push(arr.value.length + 1)
}
const changeCounter = () => {
counter.value.count++
}
</script>
<template ref>
示例 详情
<template>
<view class="page">
<view class="row">
<text>NodeRef: </text>
<text ref="nodeRef">{{ 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 setup lang="uts">
import Child from './child-composition.uvue'
type DataInfo = {
existRef: boolean
existChildRef: boolean
textItemsExpectNum: number
existTextItems: boolean
}
const dataInfo = reactive<DataInfo>({
existRef: false,
existChildRef: false,
textItemsExpectNum: 3,
existTextItems: false
})
const nodeRef = ref<UniElement | null>(null)
const childRef = ref<UniElement | null>(null)
const textItems = ref<UniElement[] | null>(null)
onReady(() => {
dataInfo.existRef = nodeRef.value !== null
dataInfo.existChildRef = childRef.value !== null
dataInfo.existTextItems = textItems.value?.length === dataInfo.textItemsExpectNum
})
defineExpose({
dataInfo
})
</script>
<style>
.row {
display: flex;
flex-direction: row;
justify-content: space-between;
margin-bottom: 10px;
}
</style>
侦听一个或多个响应式数据源,并在数据源变化时调用所给的回调函数。
示例 详情
<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 class="flex justify-between mb-10">
<text>watch count and obj.num result:</text>
<text id="watch-count-obj-num-res">{{ watchCountAndObjNumRes }}</text>
</view>
</view>
<!-- #ifdef APP -->
</scroll-view>
<!-- #endif -->
</template>
<script setup lang="uts">
type Obj = {
num : number,
str : string,
bool : boolean,
arr : number[]
}
const countRef = ref<UniTextElement | null>(null)
const count = ref(0)
const watchCountRes = ref('')
const watchCountCleanupRes = ref('')
const watchCountTrackNum = ref(0)
const stopWatchCount = watch(count, (count : number, prevCount : number, onCleanup : OnCleanup) => {
// #ifdef APP
watchCountRes.value = `count: ${count}, prevCount: ${prevCount}, count ref text (flush sync): ${countRef.value!.value}`
// #endif
// #ifdef WEB
watchCountRes.value = `count: ${count}, prevCount: ${prevCount}, count ref text (flush sync): ${(countRef.value!.childNodes[0] as HTMLElement).innerText}`
// #endif
const cancel = () => {
watchCountCleanupRes.value = `watch count cleanup: ${count}`
}
onCleanup(cancel)
}, {
// 侦听器在响应式依赖改变时立即触发
flush: 'sync',
// 响应属性或引用作为依赖项被跟踪时调用
onTrack(event : DebuggerEvent) {
if (event.type === 'get') {
watchCountTrackNum.value++
}
}
// TODO: vue>3.4.15 开始 监听函数、onTrack、onTrigger 同时存在修改响应式数据时,会报错 Maximum call stack size exceeded
// 所以将 onTrack 与 onTrigger 调整到两个 watch 里
})
const triggerStopWatchCount = () => stopWatchCount()
const increment = () => {
count.value++
}
const obj = reactive({
num: 0,
str: 'num: 0',
bool: false,
arr: [0]
} as Obj)
// immediate: true 第一次触发, 旧值应该是 undefined, 现在 app 是初始值
const watchObjRes = ref('')
watch(obj, (obj : Obj, prevObj ?: Obj) => {
watchObjRes.value = `obj: ${JSON.stringify(obj)}, prevObj: ${JSON.stringify(prevObj)}`
}, { immediate: true })
const objStrRef = ref<UniTextElement | null>(null)
const watchObjStrRes = ref('')
const watchObjStrTriggerNum = ref(0)
watch(() : string => obj.str, (str : string, prevStr : string) => {
// #ifdef APP
watchObjStrRes.value = `str: ${str}, prevStr: ${prevStr}, obj.str ref text (flush pre): ${objStrRef.value!.value}`
// #endif
// #ifdef WEB
watchObjStrRes.value = `str: ${str}, prevStr: ${prevStr}, obj.str ref text (flush pre): ${(objStrRef.value!.childNodes[0] as HTMLElement).innerText}`
// #endif
}, {
// 侦听器在组件渲染之前触发
flush: 'pre',
// 侦听器回调被依赖项的变更触发时调用
onTrigger(event : DebuggerEvent) {
if (event.type === 'set') {
watchObjStrTriggerNum.value++
}
}
})
const objBoolRef = ref<UniTextElement | null>(null)
const watchObjBoolRes = ref('')
watch(() : boolean => obj.bool, (bool : boolean, prevBool : boolean) => {
// #ifdef APP
watchObjBoolRes.value = `bool: ${bool}, prevBool: ${prevBool}, obj.bool ref text (flush post): ${objBoolRef.value!.value}`
// #endif
// #ifdef WEB
watchObjBoolRes.value = `bool: ${bool}, prevBool: ${prevBool}, obj.bool ref text (flush post): ${(objBoolRef.value!.childNodes[0] as HTMLElement).innerText}`
// #endif
}, {
// 侦听器延迟到组件渲染之后触发
flush: 'post'
})
const watchObjArrRes = ref('')
watch(() : number[] => obj.arr, (arr : number[], prevArr : number[]) => {
watchObjArrRes.value = `arr: ${JSON.stringify(arr)}, prevArr: ${JSON.stringify(prevArr)}`
}, { deep: true })
const watchCountAndObjNumRes = ref('')
watch([count, () : number => obj.num], (state : number[], preState : number[]) => {
watchCountAndObjNumRes.value = `state: ${JSON.stringify(state)}, preState: ${JSON.stringify(preState)}`
})
const updateObj = () => {
obj.num++
obj.str = `num: ${obj.num}`
obj.bool = !obj.bool
obj.arr.push(obj.num)
}
</script>
接受一个 getter 函数,返回一个只读的响应式 ref 对象。该 ref 通过 .value
暴露 getter 函数的返回值。它也可以接受一个带有 get
和 set
函数的对象来创建一个可写的 ref 对象。
注意
computed()
需通过泛型指定返回值类型。const count = ref(0)
const doubleCount = computed<number>(() : number => {
return count.value * 2
})
示例 详情
<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 setup lang="uts">
const count = ref(0)
const doubleCount = computed(() : number => {
return count.value * 2
})
type Obj = {
arr : number[]
}
const obj = reactive({
arr: [1, 2, 3]
} as Obj)
const objArrLen = computed<number>(() : number => {
return obj.arr.length
})
const update = () => {
count.value++
obj.arr.push(obj.arr.length + 1)
}
</script>
返回一个对象的响应式代理。
详细信息
响应式转换是“深层”的:它会影响到所有嵌套的属性。一个响应式对象也将深层地解包任何 ref 属性,同时保持响应性。
若要避免深层响应式转换,只想保留对这个对象顶层次访问的响应性,请使用 shallowReactive() 作替代。
示例 详情
<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>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">{{ JSON.stringify(obj['arr']) }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>count1:</text>
<text id="count1">{{ count1 }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>obj1.a.b.c:</text>
<text id="obj1-a-b-c">{{ obj1.getString('a.b.c') }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>arr1(spread):</text>
<text id="arr1">{{ JSON.stringify(arr1) }}</text>
</view>
<button class='mb-10' id="update-count-btn" @click="updateCount">update count</button>
<button class='mb-10' id="update-obj-str-btn" @click="updateObjStr">update obj.str</button>
<button class='mb-10' id="update-obj-num-btn" @click="updateObjNum">update obj.num</button>
<button class='mb-10' id="update-obj-arr-btn" @click="updateObjArr">update obj.arr</button>
<button class='mb-10' id="update-obj1-a-b-c-btn" @click="updateObj1_A_B_C">update obj1.a.b.c</button>
<button class='mb-10' id="update-arr1-btn" @click="updateArr1(false)">update arr1 without reactive</button>
<button class='mb-10' id="update-arr1-reactive-btn" @click="updateArr1(true)">update arr1 with reactive</button>
</view>
</template>
<script setup lang="uts">
const count = ref(0)
// TODO: 待支持后补充泛型示例
const obj = reactive({
str: 'default str',
num: count,
arr: ['a', 'b', 'c']
})
const updateObjStr = () => {
obj['str'] = 'new str';
}
const updateObjNum = () => {
obj['num'] = (obj['num'] as number) + 1
}
const updateCount = () => {
count.value++
}
const updateObjArr = () => {
(obj['arr'] as string[]).push('d')
}
const obj1 = reactive({
a: { b: { c: 'c' } }
})
const count1 = ref(0)
watchEffect(() => {
count1.value++
// 测试getString等keyPath触发依赖收集
obj1.getString("a.b.c")
})
function updateObj1_A_B_C() {
((obj1["a"] as UTSJSONObject)["b"] as UTSJSONObject)["c"] = "c1-" + Date.now()
}
const arr1 = ref<number[]>([])
function test(...args : number[]) {
arr1.value = args
}
function updateArr1(isReactive : boolean) {
if (isReactive) {
test(...reactive([4, 5, 6]))
} else {
test(...[1, 2, 3])
}
}
</script>
接受一个对象 (不论是响应式还是普通的) 或是一个 ref,返回一个原值的只读代理。
详细信息
只读代理是深层的:对任何嵌套属性的访问都将是只读的。它的 ref 解包行为与 reactive()
相同,但解包得到的值是只读的。
要避免深层级的转换行为,请使用 shallowReadonly() 作替代。
示例 详情
<template>
<view class="page">
<view class="flex justify-between flex-row mb-10">
<text>data.str:</text>
<text id="data-str">{{ data.str }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>data.num:</text>
<text id="data-num">{{ data.num }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>data.arr:</text>
<text id="data-arr">{{ JSON.stringify(data.arr) }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>readonly data.str:</text>
<text id="readonly-data-str">{{ readonlyData.str }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>readonly data.num:</text>
<text id="readonly-data-num">{{ readonlyData.num }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>readonly data.arr:</text>
<text id="readonly-data-arr">{{ JSON.stringify(readonlyData.arr) }}</text>
</view>
<button id="update-data-btn" class="mb-10" @click="updateData">
update data
</button>
<button id="update-readonly-data-btn" @click="updateReadonlyData">
update readonly data
</button>
</view>
</template>
<script setup lang="uts">
type Data = {
str : string,
num : number,
arr : string[]
}
// 可通过泛型指定类型
const data = reactive<Data>({
str: 'default str',
num: 0,
arr: ['a', 'b', 'c']
})
// 可通过泛型指定类型
const readonlyData = readonly<Data>(data)
const updateData = () => {
data.str = 'new str'
data.num++
data.arr.push('d')
}
const updateReadonlyData = () => {
readonlyData.str = 'new readonly str'
readonlyData.num++
readonlyData.arr.push('e')
}
</script>
立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行。
详细信息
第一个参数就是要运行的副作用函数。这个副作用函数的参数也是一个函数,用来注册清理回调。清理回调会在该副作用下一次执行前被调用,可以用来清理无效的副作用,例如等待中的异步请求 (参见下面的示例)。
第二个参数是一个可选的选项,可以用来调整副作用的刷新时机或调试副作用的依赖。
默认情况下,侦听器将在组件渲染之前执行。设置 flush: 'post'
将会使侦听器延迟到组件渲染之后再执行。在某些特殊情况下 (例如要使缓存失效),可能有必要在响应式依赖发生改变时立即触发侦听器。这可以通过设置 flush: 'sync'
来实现。然而,该设置应谨慎使用,因为如果有多个属性同时更新,这将导致一些性能和数据一致性的问题。
返回值是一个用来停止该副作用的函数。
示例 详情
<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 class="flex justify-between mb-10">
<text>watch count and obj.num result:</text>
<text id="watch-count-obj-num-res">{{ watchCountAndObjNumRes }}</text>
</view>
</view>
<!-- #ifdef APP -->
</scroll-view>
<!-- #endif -->
</template>
<script setup lang='uts'>
type Obj = {
num : number,
str : string,
bool : boolean,
arr : number[]
}
const countRef = ref<UniTextElement | null>(null)
const count = ref(0)
const watchCountRes = ref('')
const watchCountCleanupRes = ref('')
const watchCountTrackNum = ref(0)
const stopWatchCount = watchEffect((onCleanup : OnCleanup) => {
if (countRef.value !== null) {
// #ifdef APP
watchCountRes.value = `count: ${count.value}, count ref text (flush sync): ${countRef.value!.value}`
// #endif
// #ifdef WEB
watchCountRes.value = `count: ${count.value}, count ref text (flush sync): ${(countRef.value!.childNodes[0] as HTMLElement).innerText}`
// #endif
} else {
watchCountRes.value = `count: ${count.value}, count ref text (flush sync): `
}
const cancel = () => {
watchCountCleanupRes.value = `watch count cleanup: ${count.value}`
}
onCleanup(cancel)
}, {
// 侦听器在响应式依赖改变时立即触发
flush: 'sync',
// 响应属性或引用作为依赖项被跟踪时调用
onTrack(event : DebuggerEvent) {
if (event.type === 'get') {
watchCountTrackNum.value++
}
},
})
const triggerStopWatchCount = () => stopWatchCount()
const increment = () => {
count.value++
}
const obj = reactive({
num: 0,
str: 'num: 0',
bool: false,
arr: [0]
} as Obj)
const watchObjRes = ref('')
watchEffect(() => {
watchObjRes.value = `obj: ${JSON.stringify(obj)}`
})
const objStrRef = ref<UniTextElement | null>(null)
const watchObjStrRes = ref('')
const watchObjStrTriggerNum = ref(0)
watchEffect(() => {
if (objStrRef.value !== null) {
// #ifdef APP
watchObjStrRes.value = `str: ${obj.str}, obj.str ref text (flush pre): ${objStrRef.value!.value}`
// #endif
// #ifdef WEB
watchObjStrRes.value = `str: ${obj.str}, obj.str ref text (flush pre): ${(objStrRef.value!.childNodes[0] as HTMLElement).innerText}`
// #endif
} else {
watchObjStrRes.value = `str: ${obj.str}, obj.str ref text (flush pre): `
}
}, {
// 侦听器在组件渲染之前触发
flush: 'pre',
// 侦听器回调被依赖项的变更触发时调用
onTrigger(event : DebuggerEvent) {
if (event.type === 'set') {
watchObjStrTriggerNum.value++
}
}
})
const objBoolRef = ref<UniTextElement | null>(null)
const watchObjBoolRes = ref('')
watchEffect(() => {
if (objBoolRef.value !== null) {
// #ifdef APP
watchObjBoolRes.value = `bool: ${obj.bool}, obj.bool ref text (flush post): ${objBoolRef.value!.value}`
// #endif
// #ifdef WEB
watchObjBoolRes.value = `bool: ${obj.bool}, obj.bool ref text (flush post): ${(objBoolRef.value!.childNodes[0] as HTMLElement).innerText}`
// #endif
} else {
watchObjBoolRes.value = `bool: ${obj.bool}, obj.bool ref text (flush post): `
}
}, {
// 侦听器延迟到组件渲染之后触发
flush: 'post'
})
const watchObjArrRes = ref('')
watchEffect(() => {
watchObjArrRes.value = `arr: ${JSON.stringify(obj.arr)}`
})
const watchCountAndObjNumRes = ref('')
watchEffect(() => {
watchCountAndObjNumRes.value = `count: ${count.value}, obj.num: ${obj.num}`
})
const updateObj = () => {
obj.num++
obj.str = `num: ${obj.num}`
obj.bool = !obj.bool
obj.arr.push(obj.num)
}
</script>
watchEffect() 使用 flush: 'post'
选项时的别名。
示例 详情
<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.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 class="flex justify-between mb-10">
<text>watch count and obj.num result:</text>
<text id="watch-count-obj-num-res">{{ watchCountAndObjNumRes }}</text>
</view>
</view>
<!-- #ifdef APP -->
</scroll-view>
<!-- #endif -->
</template>
<script setup lang='uts'>
type Obj = {
num : number,
str : string,
bool : boolean,
arr : number[]
}
const countRef = ref<UniTextElement | null>(null)
const count = ref<number>(0)
const watchCountRes = ref('')
const watchCountCleanupRes = ref('')
const watchCountTrackNum = ref(0)
const stopWatchCount = watchPostEffect((onCleanup : OnCleanup) => {
if (countRef.value !== null) {
// #ifdef APP
watchCountRes.value = `count: ${count.value}, count ref text: ${countRef.value!.value}`
// #endif
// #ifdef WEB
watchCountRes.value = `count: ${count.value}, count ref text: ${(countRef.value!.childNodes[0] as HTMLElement).innerText}`
// #endif
} else {
watchCountRes.value = `count: ${count.value}, count ref text: `
}
const cancel = () => {
watchCountCleanupRes.value = `watch count cleanup: ${count.value}`
}
onCleanup(cancel)
},
{
// 响应属性或引用作为依赖项被跟踪时调用
onTrack(event : DebuggerEvent) {
if (event.type === 'get') {
watchCountTrackNum.value++
}
}
},
)
const triggerStopWatchCount = () => stopWatchCount()
const increment = () => {
count.value++
}
const obj = reactive({
num: 0,
str: 'num: 0',
bool: false,
arr: [0]
} as Obj)
const watchObjRes = ref('')
watchPostEffect(() => {
watchObjRes.value = `obj: {"num":${obj.num},"str":"${obj.str}","bool":${obj.bool},"arr":${JSON.stringify(obj.arr)}}`
})
const objStrRef = ref<UniTextElement | null>(null)
const watchObjStrRes = ref('')
const watchObjStrTriggerNum = ref(0)
watchPostEffect(() => {
if (objStrRef.value !== null) {
// #ifdef APP
watchObjStrRes.value = `str: ${obj.str}, obj.str ref text: ${objStrRef.value!.value}`
// #endif
// #ifdef WEB
watchObjStrRes.value = `str: ${obj.str}, obj.str ref text: ${(objStrRef.value!.childNodes[0] as HTMLElement).innerText}`
// #endif
} else {
watchObjStrRes.value = `str: ${obj.str}, obj.str ref text: `
}
},{
// 侦听器回调被依赖项的变更触发时调用
onTrigger(event : DebuggerEvent) {
if (event.type === 'set') {
watchObjStrTriggerNum.value++
}
}
})
const watchObjArrRes = ref('')
watchPostEffect(() => {
watchObjArrRes.value = `arr: ${JSON.stringify(obj.arr)}`
})
const watchCountAndObjNumRes = ref('')
watchPostEffect(() => {
watchCountAndObjNumRes.value = `count: ${count.value}, obj.num: ${obj.num}`
})
const updateObj = () => {
obj.num++
obj.str = `num: ${obj.num}`
obj.bool = !obj.bool
obj.arr.push(obj.num)
}
</script>
watchEffect() 使用 flush: 'sync'
选项时的别名。
示例 详情
<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.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 class="flex justify-between mb-10">
<text>watch count and obj.num result:</text>
<text id="watch-count-obj-num-res">{{ watchCountAndObjNumRes }}</text>
</view>
</view>
<!-- #ifdef APP -->
</scroll-view>
<!-- #endif -->
</template>
<script setup lang='uts'>
type Obj = {
num : number,
str : string,
bool : boolean,
arr : number[]
}
const countRef = ref<UniTextElement | null>(null)
const count = ref(0)
const watchCountRes = ref('')
const watchCountCleanupRes = ref('')
const watchCountTrackNum = ref(0)
const stopWatchCount = watchSyncEffect((onCleanup : OnCleanup) => {
if (countRef.value !== null) {
// #ifdef APP
watchCountRes.value = `count: ${count.value}, count ref text: ${countRef.value!.value}`
// #endif
// #ifdef WEB
watchCountRes.value = `count: ${count.value}, count ref text: ${(countRef.value!.childNodes[0] as HTMLElement).innerText}`
// #endif
} else {
watchCountRes.value = `count: ${count.value}, count ref text: `
}
const cancel = () => {
watchCountCleanupRes.value = `watch count cleanup: ${count.value}`
}
onCleanup(cancel)
},
{
// 响应属性或引用作为依赖项被跟踪时调用
onTrack(event : DebuggerEvent) {
if (event.type === 'get') {
watchCountTrackNum.value++
}
}
},
)
const triggerStopWatchCount = () => stopWatchCount()
const increment = () => {
count.value++
}
const obj = reactive({
num: 0,
str: 'num: 0',
bool: false,
arr: [0]
} as Obj)
const watchObjRes = ref('')
watchSyncEffect(() => {
watchObjRes.value = `obj: {"num":${obj.num},"str":"${obj.str}","bool":${obj.bool},"arr":${JSON.stringify(obj.arr)}}`
})
const objStrRef = ref<UniTextElement | null>(null)
const watchObjStrRes = ref('')
const watchObjStrTriggerNum = ref(0)
watchSyncEffect(() => {
if (objStrRef.value !== null) {
// #ifdef APP
watchObjStrRes.value = `str: ${obj.str}, obj.str ref text: ${objStrRef.value!.value}`
// #endif
// #ifdef WEB
watchObjStrRes.value = `str: ${obj.str}, obj.str ref text: ${(objStrRef.value!.childNodes[0] as HTMLElement).innerText}`
// #endif
} else {
watchObjStrRes.value = `str: ${obj.str}, obj.str ref text: `
}
}, {
// 侦听器回调被依赖项的变更触发时调用
onTrigger(event : DebuggerEvent) {
if (event.type === 'set') {
watchObjStrTriggerNum.value++
}
}
})
const watchObjArrRes = ref('')
watchSyncEffect(() => {
watchObjArrRes.value = `arr: ${JSON.stringify(obj.arr)}`
})
const watchCountAndObjNumRes = ref('')
watchSyncEffect(() => {
watchCountAndObjNumRes.value = `count: ${count.value}, obj.num: ${obj.num}`
})
const updateObj = () => {
obj.num++
obj.str = `num: ${obj.num}`
obj.bool = !obj.bool
obj.arr.push(obj.num)
}
</script>
Web | Android | iOS | |
---|---|---|---|
isRef() | 4.0 | 4.0 | 4.11 |
unref() | 4.0 | 4.0 | 4.11 |
toRef() | 4.11 | 4.0 | 4.11 |
toValue() | 4.11 | 4.0 | 4.11 |
toRefs() | 4.11 | 4.0 | 4.11 |
isProxy() | 4.0 | 4.0 | 4.11 |
isReactive() | 4.0 | 4.0 | 4.11 |
isReadonly() | 4.0 | 4.0 | 4.11 |
注意
toRefs()
仅支持 Array
和 UTSJSONObject
, 不支持自定义类型。检查某个值是否为 ref。
示例 详情
<template>
<view class="page">
<view class="flex justify-between flex-row mb-10">
<text>ref count:</text>
<text id="ref-count">{{ refCount }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>isRef ref count:</text>
<text id="is-ref-ref-count">{{ isRefRefCount }}</text>
</view>
<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>isRef count:</text>
<text id="is-ref-count">{{ isRefCount }}</text>
</view>
</view>
</template>
<script setup lang="uts">
const refCount = ref(0);
const count = 0;
const isRefRefCount = isRef(refCount);
const isRefCount = isRef(count);
</script>
如果参数是 ref,则返回内部值,否则返回参数本身。这是 val = isRef(val) ? val.value : val
计算的一个语法糖。
示例 详情
<template>
<view class="page">
<view class="flex justify-between flex-row mb-10">
<text>ref count:</text>
<text id="ref-count">{{ refCount }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>ref count type:</text>
<text id="ref-count-type">{{ refCountType }}</text>
</view>
<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>count type:</text>
<text id="count-type">{{ countType }}</text>
</view>
</view>
</template>
<script setup lang="uts">
const refCount = ref<number>(0);
const refCountType = typeof refCount;
const count = unref(refCount);
const countType = typeof count;
</script>
可以将值、refs 或 getters 规范化为 refs。
也可以基于响应式对象上的一个属性,创建一个对应的 ref。这样创建的 ref 与其源属性保持同步:改变源属性的值将更新 ref 的值,反之亦然。
示例 详情
<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>isRef count:</text>
<text id="is-ref-count">{{ isRefCount }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>ref count:</text>
<text id="ref-count">{{ refCount }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>isRef ref count:</text>
<text id="is-ref-ref-count">{{ isRefRefCount }}</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>toRef(obj, "num"):</text>
<text id="to-ref-obj-num">{{ objNum }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>toRef(() => obj.num):</text>
<text id="to-ref-fn-obj-num">{{ readonlyObjNum }}</text>
</view>
<button class="mt-10" id="increment-btn" @click="increment">
increment obj.num
</button>
</view>
</template>
<script setup lang="uts">
const count = 0;
const isRefCount = isRef(count);
const refCount = toRef<number>(count);
const isRefRefCount = isRef(refCount);
type Obj = {
num : number
}
const obj = reactive({
num: 0
} as Obj)
const objNum = toRef<number>(obj, 'num')
const readonlyObjNum = toRef<number>(() : number => obj.num)
const increment = () => {
obj.num++;
objNum.value++;
readonlyObjNum.value++;
}
</script>
将值、refs 或 getters 规范化为值。这与 unref() 类似,不同的是此函数也会规范化 getter 函数。如果参数是一个 getter,它将会被调用并且返回它的返回值。
示例 详情
<template>
<view class="page">
<view class="flex justify-between flex-row mb-10">
<text>ref count:</text>
<text id="ref-count">{{ refCount }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>isRef ref count:</text>
<text id="is-ref-ref-count">{{ isRefRefCount }}</text>
</view>
<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>isRef count:</text>
<text id="is-ref-count">{{ isRefCount }}</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>toValue(() => obj.num):</text>
<text id="to-value-obj-num">{{ toValueObjNum }}</text>
</view>
<button class="mt-10" id="increment-btn" @click="increment">
increment obj.num
</button>
</view>
</template>
<script setup lang="uts">
const refCount = ref<number>(0);;
const isRefRefCount = isRef(refCount);
const count = toValue(refCount);
const isRefCount = isRef(count);
type Obj = {
num : number
}
const obj = reactive({
num: 0
} as Obj)
let toValueObjNum = toValue(() : number => 0)
const increment = () => {
obj.num++;
toValueObjNum++;
}
</script>
将一个响应式对象转换为一个普通对象,这个普通对象的每个属性都是指向源对象相应属性的 ref。每个单独的 ref 都是使用 toRef() 创建的。
示例 详情
<template>
<view class="page">
<view class="flex justify-between flex-row mb-10">
<text>state.num:</text>
<text id="state-num">{{ state['num'] }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>state.str:</text>
<text id="state-str">{{ state['str'] }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>stateAsRefs.num:</text>
<text id="state-as-refs-num">{{ (stateAsRefs['num'] as Ref<number>).value }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>stateAsRefs.str:</text>
<text id="state-as-refs-str">{{ (stateAsRefs['str'] as Ref<string>).value }}</text>
</view>
<button class="mt-10" id="update-state-btn" @click="updateState">update state</button>
</view>
</template>
<script setup lang='uts'>
// toRefs 仅支持 array 和 UTSJSONObject, 不支持自定义类型
const state = reactive({
num: 0,
str: 'str-0'
})
const stateAsRefs = toRefs(state)
const updateState = () => {
state['num'] = (state['num'] as number) + 1;
(stateAsRefs['str'] as Ref<string>).value = `str-${(stateAsRefs['num'] as Ref<number>).value}`
}
</script>
检查一个对象是否是由 reactive()、readonly()、shallowReactive() 或 shallowReadonly() 创建的代理。
示例 详情
<template>
<view class="page">
<view class="flex justify-between flex-row mb-10">
<text>isProxy(count):</text>
<text id="is-proxy-count">{{ isProxyCount }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>isProxy(refCount):</text>
<text id="is-proxy-ref-count">{{ isProxyRefCount }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>isProxy(reactiveCount):</text>
<text id="is-proxy-reactive-count">{{ isProxyReactiveCount }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>isProxy(readonlyCount):</text>
<text id="is-proxy-readonly-count">{{ isProxyReadonlyCount }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>isProxy(shallowReactiveCount):</text>
<text id="is-proxy-shallow-reactive-count">{{
isProxyShallowReactiveCount
}}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>isProxy(shallowReadonlyCount):</text>
<text id="is-proxy-shallow-readonly-count">{{
isProxyShallowReadonlyCount
}}</text>
</view>
</view>
</template>
<script setup lang="uts">
const count = 0;
const isProxyCount = isProxy(count);
const refCount = ref(0);
const isProxyRefCount = isProxy(refCount);
const reactiveCount = reactive({ count: 0 });
const isProxyReactiveCount = isProxy(reactiveCount);
const readonlyCount = readonly({ count: 0 });
const isProxyReadonlyCount = isProxy(readonlyCount);
const shallowReactiveCount = shallowReactive({ count: 0 });
const isProxyShallowReactiveCount = isProxy(shallowReactiveCount);
const shallowReadonlyCount = shallowReadonly({ count: 0 });
const isProxyShallowReadonlyCount = isProxy(shallowReadonlyCount);
</script>
检查一个对象是否是由 reactive() 或 shallowReactive() 创建的代理。
示例 详情
<template>
<view class="page">
<view class="flex justify-between flex-row mb-10">
<text>isReactive(count):</text>
<text id="is-reactive-count">{{ isReactiveCount }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>isReactive(count):</text>
<text id="is-reactive-count">{{ isReactiveCount }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>isReactive(refCount):</text>
<text id="is-reactive-ref-count">{{ isReactiveRefCount }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>isReactive(reactiveCount):</text>
<text id="is-reactive-reactive-count">{{ isReactiveReactiveCount }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>isReactive(readonlyCount):</text>
<text id="is-reactive-readonly-count">{{ isReactiveReadonlyCount }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>isReactive(shallowReactiveCount):</text>
<text id="is-reactive-shallow-reactive-count">{{
isReactiveShallowReactiveCount
}}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>isReactive(shallowReadonlyCount):</text>
<text id="is-reactive-shallow-readonly-count">{{
isReactiveShallowReadonlyCount
}}</text>
</view>
</view>
</template>
<script setup lang="uts">
const count = 0;
const isReactiveCount = isReactive(count);
const refCount = ref(0);
const isReactiveRefCount = isReactive(refCount);
const reactiveCount = reactive({ count: 0 });
const isReactiveReactiveCount = isReactive(reactiveCount);
const readonlyCount = readonly({ count: 0 });
const isReactiveReadonlyCount = isReactive(readonlyCount);
const shallowReactiveCount = shallowReactive({ count: 0 });
const isReactiveShallowReactiveCount = isReactive(shallowReactiveCount);
const shallowReadonlyCount = shallowReadonly({ count: 0 });
const isReactiveShallowReadonlyCount = isReactive(shallowReadonlyCount);
</script>
检查传入的值是否为只读对象。只读对象的属性可以更改,但他们不能通过传入的对象直接赋值。
通过 readonly() 和 shallowReadonly() 创建的代理都是只读的,因为他们是没有 set 函数的 computed() ref。
示例 详情
<template>
<view class="page">
<view class="flex justify-between flex-row mb-10">
<text>isReadonly(count):</text>
<text id="is-readonly-count">{{ isReadonlyCount }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>isReadonly(refCount):</text>
<text id="is-readonly-ref-count">{{ isReadonlyRefCount }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>isReadonly(reactiveCount):</text>
<text id="is-readonly-reactive-count">{{ isReadonlyReactiveCount }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>isReadonly(readonlyCount):</text>
<text id="is-readonly-readonly-count">{{ isReadonlyReadonlyCount }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>isReadonly(shallowReactiveCount):</text>
<text id="is-readonly-shallow-reactive-count">{{
isReadonlyShallowReactiveCount
}}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>isReadonly(shallowReadonlyCount):</text>
<text id="is-readonly-shallow-readonly-count">{{
isReadonlyShallowReadonlyCount
}}</text>
</view>
</view>
</template>
<script setup lang='uts'>
const count = 0;
const isReadonlyCount = isReadonly(count);
const refCount = ref(0);
const isReadonlyRefCount = isReadonly(refCount);
const reactiveCount = reactive({ count: 0 });
const isReadonlyReactiveCount = isReadonly(reactiveCount);
const readonlyCount = readonly({ count: 0 });
const isReadonlyReadonlyCount = isReadonly(readonlyCount);
const shallowReactiveCount = shallowReactive({ count: 0 });
const isReadonlyShallowReactiveCount = isReadonly(shallowReactiveCount);
const shallowReadonlyCount = shallowReadonly({ count: 0 });
const isReadonlyShallowReadonlyCount = isReadonly(shallowReadonlyCount);
</script>
Web | Android | iOS | |
---|---|---|---|
shallowRef() | 4.0 | 4.0 | 4.11 |
triggerRef() | x | 4.0 | 4.11 |
customRef() | 4.0 | 4.0 | 4.11 |
shallowReactive() | 4.0 | 4.0 | 4.11 |
shallowReadonly() | 4.0 | 4.0 | 4.11 |
toRaw() | 4.0 | 4.0 | 4.11 |
markRaw() | - | - | - |
effectScope() | 4.0 | 4.0 | 4.11 |
getCurrentScope() | 4.0 | 4.0 | 4.11 |
onScopeDispose() | 4.0 | 4.0 | 4.11 |
创建一个自定义的 ref,显式声明对其依赖追踪和更新触发的控制方式。
详细信息
customRef()
预期接收一个工厂函数作为参数,这个工厂函数接受 track
和 trigger
两个函数作为参数,并返回一个带有 get 和 set 方法的对象。
一般来说,track()
应该在 get()
方法中调用,而 trigger()
应该在 set()
中调用。然而事实上,你对何时调用、是否应该调用他们有完全的控制权。
示例 详情
<template>
<view class="page">
<view class="flex justify-between flex-row mb-10">
<text>state.count:</text>
<text id="state-count">{{ state['count'] }}</text>
</view>
<button class="mb-10 increment-btn" @click="increment">
increment state.count
</button>
<button class="mb-10 trigger-ref-btn" @click="triggerRefState">
triggerRef state
</button>
</view>
</template>
<script setup lang="uts">
const useCustomRef = (value : UTSJSONObject) : Ref<UTSJSONObject> => {
// @ts-ignore
return customRef<UTSJSONObject>((track, trigger) => {
return {
get() : UTSJSONObject {
track()
return value
},
set(newValue : UTSJSONObject) {
value = newValue
trigger()
}
}
})
}
const state = useCustomRef({ count: 0 })
const increment = () => {
state.value['count'] = (state.value['count'] as number) + 1
}
const triggerRefState = () => {
triggerRef(state)
}
</script>
创建一个 effect 作用域,可以捕获其中所创建的响应式副作用 (即计算属性和侦听器),这样捕获到的副作用可以一起处理。对于该 API 的使用细节
示例 详情
<template>
<view class="page">
<view class="flex justify-between flex-row mb-10">
<text>counter:</text>
<text id="counter">{{ counter }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>watch counter result:</text>
<text id="watch-counter-res">{{ watchCounterRes }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>watchEffect counter result:</text>
<text id="watch-effect-counter-res">{{ watchEffectCounterRes }}</text>
</view>
<button
id="increment-counter-btn"
class="mt-10"
@click="
() => {
counter++;
}
">
increment counter
</button>
<button id="stop-effect-scope-btn" class="mt-10" @click="stopEffectScope">
stop effect scope
</button>
</view>
</template>
<script setup lang="uts">
const scope = effectScope()
const counter = ref(0)
const watchCounterRes = ref('')
const watchEffectCounterRes = ref('')
scope.run(() => {
watch(counter, (newVal : number, oldVal : number) => {
watchCounterRes.value = `newVal: ${newVal}, oldVal: ${oldVal}`
})
watchEffect(() => {
watchEffectCounterRes.value = `counter: ${counter.value}`
})
})
const stopEffectScope = () => scope.stop()
</script>
如果有的话,返回当前活跃的 effect 作用域。
示例 详情
<template>
<view class="page">
<view class="flex justify-between flex-row mb-10">
<text>hasCurrentScope:</text>
<text id="has-current-scope">{{ hasCurrentScope }}</text>
</view>
<button id="create-scope-btn" class="mt-10" @click="createScope">
create scope
</button>
</view>
</template>
<script setup lang="uts">
const hasCurrentScope = ref(false);
const createScope = () => {
const scope = effectScope();
scope.run(() => {
hasCurrentScope.value = getCurrentScope() !== null;
});
};
</script>
在当前活跃的 effect 作用域上注册一个处理回调函数。当相关的 effect 作用域停止时会调用这个回调函数。
这个方法可以作为可复用的组合式函数中 onUnmounted
的替代品,它并不与组件耦合,因为每一个 Vue 组件的 setup()
函数也是在一个 effect 作用域中调用的。
示例 详情
<template>
<view class="page">
<view class="flex justify-between flex-row mb-10">
<text>hasCurrentScope:</text>
<text id="has-current-scope">{{ hasCurrentScope }}</text>
</view>
<button id="create-scope-btn" class="mt-10" @click="createScope">
create scope
</button>
<button id="stop-scope-btn" class="mt-10" @click="stopScope">
stop scope
</button>
</view>
</template>
<script setup lang="uts">
const hasCurrentScope = ref(false)
let scope = null as EffectScope | null
const createScope = () => {
scope = effectScope();
(scope as EffectScope).run(() => {
hasCurrentScope.value = getCurrentScope() != null
onScopeDispose(() => {
hasCurrentScope.value = getCurrentScope() != null
})
})
}
const stopScope = () => {
if (scope !== null) {
(scope as EffectScope).stop()
}
}
</script>
reactive() 的浅层作用形式。
示例 详情
<template>
<view class="page">
<view class="flex justify-between flex-row mb-10">
<text>state.count:</text>
<text id="state-count" :data-count="state.count">{{ state.count }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>state.nested.count:</text>
<text id="state-nested-count">{{ state.nested.count }}</text>
</view>
<button
id="increment-state-count-btn"
class="mb-10"
@click="incrementStateCount">
increment state.count
</button>
<button
id="increment-state-nested-count-btn"
@click="incrementStateNestedCount">
increment state.nested.count
</button>
</view>
</template>
<script setup lang="uts">
type StateNested = {
count : number
}
type State = {
count : number,
nested : StateNested
}
// 可通过泛型指定类型
const state = shallowReactive<State>({
count: 0,
nested: {
count: 0
}
})
const incrementStateCount = () => {
state.count++
}
const incrementStateNestedCount = () => {
state.nested.count++
}
defineExpose({
state
})
</script>
readonly() 的浅层作用形式
示例 详情
<template>
<view :key="pageKey" class="page">
<view class="flex justify-between flex-row mb-10">
<text>state.count:</text>
<text id="state-count">{{ state.count }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>state.nested.count:</text>
<text id="state-nested-count">{{ state.nested.count }}</text>
</view>
<button
id="increment-state-count-btn"
class="mb-10"
@click="incrementStateCount">
increment state.count
</button>
<button
id="increment-state-nested-count-btn"
class="mb-10"
@click="incrementStateNestedCount">
increment state.nested.count
</button>
<button id="update-page-render-btn" @click="updatePageRender">
update page render
</button>
</view>
</template>
<script setup lang="uts">
let pageKey = ref<number>(0)
type StateNested = {
count : number
}
type State = {
count : number,
nested : StateNested
}
// 可通过泛型指定类型
const state = shallowReadonly<State>({
count: 0,
nested: {
count: 0
}
})
// #ifdef APP
const incrementStateCount = () => {
state.count++
}
const incrementStateNestedCount = () => {
state.nested.count++
}
// #endif
const updatePageRender = () => {
pageKey.value = Date.now()
}
</script>
ref() 的浅层作用形式。
示例 详情
<template>
<view class="page">
<view class="flex justify-between flex-row mb-10">
<text>state.count:</text>
<text id="state-count">{{ state.count }}</text>
</view>
<button
id="increment-state-count-btn"
class="mb-10"
@click="incrementStateCount">
increment state.count
</button>
<button id="update-state-btn" @click="updateState">update state</button>
</view>
</template>
<script setup lang="uts">
type State = {
count: number
}
// 可通过泛型指定类型
const state = shallowRef<State>({
count: 0
})
const incrementStateCount = () => {
state.value.count++
}
const updateState = () => {
state.value = { count: state.value.count } as State
}
</script>
根据一个 Vue 创建的代理返回其原始对象。
示例 详情
<template>
<view class="page">
<view class="flex justify-between flex-row mb-10">
<text>check toRaw ref:</text>
<text id="check-to-raw-ref">{{ checkToRawRef }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>check toRaw reactive:</text>
<text id="check-to-raw-reactive">{{ checkToRawReactive }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>check toRaw readonly:</text>
<text id="check-to-raw-readonly">{{ checkToRawReadonly }}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>check toRaw shallowReactive:</text>
<text id="check-to-raw-shallow-reactive">{{
checkToRawShallowReactive
}}</text>
</view>
<view class="flex justify-between flex-row mb-10">
<text>check toRaw shallowReadonly:</text>
<text id="check-to-raw-shallow-readonly">{{
checkToRawShallowReadonly
}}</text>
</view>
</view>
</template>
<script setup lang="uts">
const obj = {}
const refObj = ref(obj);
const checkToRawRef = toRaw<UTSJSONObject>(refObj) === obj;
const reactiveObj = reactive(obj);
const checkToRawReactive = toRaw<UTSJSONObject>(reactiveObj) === obj;
const readonlyObj = readonly(obj);
const checkToRawReadonly = toRaw<UTSJSONObject>(readonlyObj) === obj;
const shallowReactiveObj = shallowReactive(obj);
const checkToRawShallowReactive = toRaw<UTSJSONObject>(shallowReactiveObj) === obj;
const shallowReadonlyObj = shallowReadonly(obj);
const checkToRawShallowReadonly = toRaw<UTSJSONObject>(shallowReadonlyObj) === obj;
</script>
强制触发依赖于一个浅层 ref 的副作用,这通常在对浅引用的内部值进行深度变更后使用。
示例 详情
<template>
<view class="page">
<view class="flex justify-between flex-row mb-10">
<text>state.count:</text>
<text id="state-count">{{ state.count }}</text>
</view>
<button
id="increment-state-count-btn"
class="mb-10"
@click="incrementStateCount">
increment state.count
</button>
<button id="trigger-ref-state-btn" @click="triggerRefState">
trigger state
</button>
</view>
</template>
<script setup lang="uts">
type State = {
count: number
}
const state = shallowRef({
count: 0
} as State)
const incrementStateCount = () => {
state.value.count++
}
const triggerRefState = () => {
triggerRef(state)
}
</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>onPageShow 触发:</text>
<text>{{ isOnPageShowTriggered }}</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>onPageHide 触发:</text>
<text>{{ isOnPageHideTriggered }}</text>
</view>
<view class="flex flex-row justify-between mt-10">
<text>onResize 触发:</text>
<text>{{ isOnResizeTriggered }}</text>
</view>
<view class="flex flex-row justify-between mt-10">
<MonitorPageLifecycleComposition />
</view>
<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 setup lang="uts">
import { state, setLifeCycleNum } from '@/store/index.uts'
import MonitorPageLifecycleComposition from './monitor-page-lifecycle-composition.uvue'
const isOnloadTriggered = ref(false)
const isOnPageShowTriggered = ref(false)
const isOnReadyTriggered = ref(false)
const isOnPullDownRefreshTriggered = ref(false)
const isOnPageScrollTriggered = ref(false)
const isOnReachBottomTriggered = ref(false)
const isOnBackPressTriggered = ref(false)
const isOnPageHideTriggered = ref(false)
const isOnResizeTriggered = ref(false)
type DataInfo = {
isScrolled : boolean
}
const dataInfo = reactive({
isScrolled: false,
} as DataInfo)
onLoad((options : OnLoadOptions) => {
console.log('onLoad', options)
isOnloadTriggered.value = true
// 自动化测试
setLifeCycleNum(state.lifeCycleNum + 100)
})
onPageShow(() => {
isOnPageShowTriggered.value = true
// 自动化测试
setLifeCycleNum(state.lifeCycleNum + 10)
})
onReady(() => {
isOnReadyTriggered.value = true
// 自动化测试
setLifeCycleNum(state.lifeCycleNum + 10)
})
onPullDownRefresh(() => {
isOnPullDownRefreshTriggered.value = true
// 自动化测试
setLifeCycleNum(state.lifeCycleNum + 10)
})
onPageScroll((e: OnPageScrollOptions) => {
console.log('onPageScroll', e)
isOnPageScrollTriggered.value = true
// 自动化测试
dataInfo.isScrolled = true
})
onReachBottom(() => {
isOnReachBottomTriggered.value = true
// 自动化测试
setLifeCycleNum(state.lifeCycleNum + 10)
})
onBackPress((options : OnBackPressOptions) : boolean | null => {
console.log('onBackPress', options)
isOnBackPressTriggered.value = true
// 自动化测试
setLifeCycleNum(state.lifeCycleNum - 10)
return null
})
onPageHide(() => {
isOnPageHideTriggered.value = true
// 自动化测试
setLifeCycleNum(state.lifeCycleNum - 10)
})
onUnload(() => {
// 自动化测试
setLifeCycleNum(state.lifeCycleNum - 100)
})
onResize((options: OnResizeOptions) => {
console.log('onBackPress', options)
isOnResizeTriggered.value = true
// 自动化测试
setLifeCycleNum(state.lifeCycleNum + 10)
})
// 自动化测试
const pageGetLifeCycleNum = () : number => {
return state.lifeCycleNum
}
// 自动化测试
const pageSetLifeCycleNum = (num : number) => {
setLifeCycleNum(num)
}
// 自动化测试
const pullDownRefresh = () => {
uni.startPullDownRefresh({
success() {
setTimeout(() => {
uni.stopPullDownRefresh()
}, 1500)
},
})
}
const scrollToBottom = () => {
uni.pageScrollTo({
scrollTop: 2000,
})
}
const goOnBackPress = () => {
uni.navigateTo({url: '/pages/lifecycle/page/onBackPress/on-back-press-composition'})
}
defineExpose({
dataInfo,
pageGetLifeCycleNum,
pageSetLifeCycleNum,
pullDownRefresh,
scrollToBottom,
})
</script>
<style>
.container {
height: 1200px;
}
</style>
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 完成。 这个钩子仅会在服务端渲染中执行,可以用于执行一些仅存在于服务端的数据抓取过程。 |
目前 App平台 onMounted、onUnmounted 可以保证当前数据已经同步到 DOM,但是由于排版和渲染是异步的的,所以 onMounted、onUnmounted 不能保证 DOM 排版以及渲染完毕。
如果需要获取排版后的节点信息推荐使用 uni.createSelectorQuery 不推荐直接使用 Element 对象。
在修改 DOM 后,立刻使用 Element 对象的同步接口获取 DOM 状态可能获取到的是排版之前的,而 uni.createSelectorQuery 可以保障获取到的节点信息是排版之后的。
注:页面的 onReady 生命周期可以获取到排版后的节点信息
当 A 页面存在 keepAlive
组件,A 页面 navigateTo
B 页面时
keepAlive
的组件会触发 onDeactivated
生命周期keepAlive
的组件不会触发 onDeactivated
生命周期当 B 页面 back 返回 A 页面时
keepAlive
的组件会触发 onActivated
生命周期keepAlive
的组件不会触发 onActivated
生命周期示例 详情
<template>
title: {{ title }}
<button class="component-lifecycle-btn mt-10" @click="updateTitle">
updateTitle
</button>
</template>
<script setup lang='uts'>
import { state, setLifeCycleNum } from '@/store/index.uts'
const title = ref('component for composition API lifecycle test')
const emit = defineEmits<{
(e : 'updateIsScroll', val : boolean) : void
}>()
onLoad((_ : OnLoadOptions) => {
// 自动化测试
setLifeCycleNum(state.lifeCycleNum + 100)
})
onPageShow(() => {
// 自动化测试
setLifeCycleNum(state.lifeCycleNum + 10)
})
onReady(() => {
// 自动化测试
// TODO: onReady 未触发
setLifeCycleNum(state.lifeCycleNum + 10)
})
onPullDownRefresh(() => {
// 自动化测试
setLifeCycleNum(state.lifeCycleNum + 10)
})
onPageScroll((_) => {
// 自动化测试
emit('updateIsScroll', true)
})
onReachBottom(() => {
// 自动化测试
setLifeCycleNum(state.lifeCycleNum + 10)
})
onBackPress((_ : OnBackPressOptions) : boolean | null => {
// 自动化测试
setLifeCycleNum(state.lifeCycleNum - 10)
return null
})
onPageHide(() => {
// 自动化测试
setLifeCycleNum(state.lifeCycleNum - 10)
})
onUnload(() => {
// 自动化测试
setLifeCycleNum(state.lifeCycleNum - 100)
})
onBeforeMount(() => {
// 自动化测试
setLifeCycleNum(state.lifeCycleNum + 1)
console.log('component for lifecycle test onBeforeMount')
})
onMounted(() => {
// 自动化测试
setLifeCycleNum(state.lifeCycleNum + 1)
console.log('component for lifecycle test mounted')
})
onBeforeUpdate(() => {
// 自动化测试
setLifeCycleNum(state.lifeCycleNum + 1)
console.log('component for lifecycle test beforeUpdate')
})
onUpdated(() => {
// 自动化测试
setLifeCycleNum(state.lifeCycleNum + 1)
console.log('component for lifecycle test updated')
})
onBeforeUnmount(() => {
// 自动化测试
setLifeCycleNum(state.lifeCycleNum - 1)
console.log('component for lifecycle test beforeUnmount')
})
onUnmounted(() => {
// 自动化测试
setLifeCycleNum(state.lifeCycleNum - 1)
console.log('component for lifecycle test unmounted')
})
onActivated(() => {
// 自动化测试
setLifeCycleNum(state.lifeCycleNum + 1)
console.log('component for lifecycle test onActivated')
})
onDeactivated(() => {
// 自动化测试
setLifeCycleNum(state.lifeCycleNum - 1)
console.log('component for lifecycle test onDeactivated')
})
const updateTitle = () => {
title.value = 'component for lifecycle test updated'
}
</script>
export default {}
方式定义组件。data
仅支持函数返回对象字面量方式。<script lang="uts">
export default {
data() {
return {
// 必须写这里
}
}
}
</script>
名称 | 类型 | 默认值 | 兼容性 | 描述 | |||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
setup | Any | - | - | ||||||||||
lang | Any | - | |||||||||||
|
Web | Android | iOS |
---|---|---|
4.0 | 3.9 | 4.11 |
示例 详情
<template>
<!-- #ifdef APP -->
<scroll-view style="flex: 1">
<!-- #endif -->
<view class="page">
<view class="flex justify-between flex-row mt-10">
<text>str:</text>
<text id="str">{{ str }}</text>
</view>
<view class="flex justify-between flex-row mt-10">
<text>num:</text>
<text id="num">{{ num }}</text>
</view>
<view class="flex justify-between flex-row mt-10">
<text>bool:</text>
<text id="bool">{{ bool }}</text>
</view>
<view class="flex justify-between flex-row mt-10">
<text>count:</text>
<text id="count">{{ count }}</text>
</view>
<button class="mt-10" id="increment-btn" @click="increment">
increment count
</button>
<view class="flex justify-between flex-row mt-10">
<text>obj.str:</text>
<text id="obj-str">{{ obj['str'] }}</text>
</view>
<view class="flex justify-between flex-row mt-10">
<text>obj.num:</text>
<text id="obj-num">{{ obj['num'] }}</text>
</view>
<view class="flex justify-between flex-row mt-10">
<text>obj.bool:</text>
<text id="obj-bool">{{ obj['bool'] }}</text>
</view>
<button class="mt-10" id="update-obj-btn" @click="updateObj">
update obj
</button>
<!-- #ifdef APP -->
<RenderFunction
:str="str"
:count="count"
:obj="obj"
@compUpdateObj="compUpdateObj"
:isShow="true" />
<!-- #endif -->
<Foo>
<text class="mt-10" id="default-slot-in-foo">default slot in Foo</text>
</Foo>
</view>
<!-- #ifdef APP -->
</scroll-view>
<!-- #endif -->
</template>
<script lang="uts">
// #ifdef APP
import RenderFunction from './RenderFunction.uvue'
// #endif
import Foo from './Foo.uvue'
export default {
components: {
// #ifdef APP
RenderFunction,
// #endif
Foo
},
setup() {
const count = ref(0)
// 函数只能通过声明变量,赋值函数的方式,不支持 function xxx(){}
const increment = () => { count.value++ }
const obj = reactive({
str: 'obj default str',
num: 0,
bool: false,
})
const updateObj = () => {
obj['str'] = 'obj new str'
obj['num'] = 100
obj['bool'] = true
}
const compUpdateObj = () => {
obj['str'] = 'obj new str by comp update'
obj['num'] = 200
obj['bool'] = true
}
return {
str: 'default str',
num: 0,
bool: false,
count,
increment,
obj,
updateObj,
compUpdateObj
}
}
}
</script>
Web | Android | iOS | |
---|---|---|---|
defineProps() | 4.0 | 4.0 | 4.11 |
defineEmits() | 4.0 | 4.0 | 4.11 |
defineModel() | 4.11 | 4.0 | 4.11 |
defineExpose() | 4.0 | 4.0 | 4.11 |
defineOptions() | 4.11 | 4.0 | 4.11 |
defineSlots() | 4.0 | 4.0 | 4.11 |
useSlots() | 4.0 | 4.0 | 4.11 |
useAttrs() | 4.0 | 4.0 | 4.11 |
仅支持数组字面量、对象字面量定义(等同于 options
中的 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 setup lang="uts">
import ArrayLiteral from './array-literal-composition.uvue'
import ObjectType from "./object-type-composition.uvue";
import SameNamePropDefaultValue from "./same-name-prop-default-value-composition.uvue";
import PropsWithDefaults from "./props-with-defaults.uvue";
import ReferenceTypes from './reference-types-composition.uvue'
const str = 'str'
const num = 10
const bool = true
const obj = { age: 18 }
const arr = ['a', 'b', 'c']
</script>
仅支持数组字面量和纯类型参数的方式来声明。
// 数组字面量
const emit = defineEmits(['change'])
// 纯类型参数
const emit = defineEmits<{
(e : 'change', id : number) : void
}>()
const emit = defineEmits<{
// 具名元组语法
change : [id: number]
}>()
<template>
<view>
<button @click="click" class="call-parent-btn">调用父组件事件</button>
</view>
</template>
<script setup lang="uts">
const emit = defineEmits(['callback'])
const click = () => {
emit('callback', `${Date.now()}`)
}
defineExpose({
click
})
</script>
仅支持对象字面量方式定义。
defineOptions({
data() {
return {
count: 0,
price: 10,
total: 0
}
},
computed: {
doubleCount() : number {
return this.count * 2
},
},
watch: {
count() {
this.total = this.price * this.count
},
},
methods: {
increment() {
this.count++
}
}
})
<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 setup lang="uts">
import mixins from "./mixins.uts"
defineOptions({
mixins: [mixins],
name: "$options",
_customKey: "custom key"
})
type DataInfo = {
name: string
customKey: string
mixinDataStr: string
}
const dataInfo = reactive({
name: "",
customKey: "",
mixinDataStr: ""
} as DataInfo)
onMounted(() => {
const instance = getCurrentInstance()!.proxy!
dataInfo.name = instance.$options.name!
// #ifndef APP-ANDROID
dataInfo.customKey = instance.$options._customKey
dataInfo.mixinDataStr = instance.$options.data!({})!['str']
// #endif
})
defineExpose({
dataInfo
})
</script>
使用 <script setup>
的组件是默认关闭的——即通过模板引用或者 $parent 链获取到的组件的公开实例,不会暴露任何在 <script setup>
中声明的绑定。
可以通过 defineExpose
编译器宏来显式指定在 <script setup>
组件中要暴露出去的属性,注意:
defineExpose
导出的属性, 例如:defineExpose({
count
})
setup
中定义的,暂不支持外部定义示例 详情
<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 setup lang="uts">
type Obj = {
str : string,
num : number,
arr : number[]
}
const instance = getCurrentInstance()!.proxy!
const str = ref('default str')
const num = ref(0)
// 可通过泛型指定类型
const arr = ref<number[]>([1, 2, 3])
const obj = ref<Obj>({
str: 'default obj.str',
num: 10,
arr: [4, 5, 6]
})
const refElement = ref<UniElement | null>(null)
const refElementIsSame = ref(false)
const refTest = () => {
const queryElementById1 = uni.getElementById('idRef')
const queryElementById2 = uni.getElementById('idRef')
const htmlRefElement = instance.$refs['htmlRef'] as UniElement | null;
refElement.value = htmlRefElement
if (queryElementById1 === queryElementById2
&& queryElementById1 === htmlRefElement
&& queryElementById1 === refElement.value
) {
refElementIsSame.value = true
}
}
const updateData = () => {
str.value = 'new str'
num.value = 1
arr.value = [4, 5, 6]
obj.value.str = 'new obj.str'
obj.value.num = 100
obj.value.arr = [7, 8, 9]
refTest()
}
defineExpose({
updateData
})
</script>
这个宏可以用来声明一个双向绑定 prop,通过父组件的 v-model
来使用。组件 v-model 指南中也讨论了示例用法。
在底层,这个宏声明了一个 model prop 和一个相应的值更新事件。如果第一个参数是一个字符串字面量,它将被用作 prop 名称;否则,prop 名称将默认为 "modelValue"
。在这两种情况下,你都可以再传递一个额外的对象,它可以包含 prop 的选项和 model ref 的值转换选项。
注意: android 端 defineModel
暂不支持创建 Array
类型 prop
。
<template>
<view>
<view class="mb-10 flex justify-between flex-row">
<text>v-model in Foo:</text>
<text id="model-value-text">{{ modelValue }}</text>
</view>
<view class="mb-10 flex justify-between flex-row">
<text>v-model:msg in Foo:</text>
<text id="model-msg-text">{{ msg }}</text>
</view>
<view class="mb-10 flex justify-between flex-row">
<text>defineModel num:</text>
<text id="model-num-text">{{ num }}</text>
</view>
<view class="mb-10 flex justify-between flex-row">
<text>defineModel strArr:</text>
<text id="model-str-arr-text">{{ JSON.stringify(strArr) }}</text>
</view>
<view class="mb-10 flex justify-between flex-row">
<text>defineModel numArr:</text>
<text id="model-num-arr-text">{{ JSON.stringify(numArr) }}</text>
</view>
<button class="mb-10" id="update-value-btn" @click="updateValue">
update value
</button>
</view>
</template>
<script setup lang="uts">
// 在被修改时,触发 "update:modelValue" 事件
const modelValue = defineModel({ type: String })
// 在被修改时,触发 "update:msg" 事件
const msg = defineModel('msg', { type: String, default: 'default msg' })
const num = defineModel('num', { type: Number, default: 1 })
const strArr = defineModel<string[]>('strArr', { default: () => [] as string[] })
const numArr = defineModel('numArr', {type: Array as PropType<number[]>, required: true })
const updateValue = () => {
modelValue.value += '1'
msg.value += '2'
num.value++
strArr.value.push(`${strArr.value.length}`)
numArr.value.push(numArr.value.length)
}
</script>
这个宏可以用于为 IDE 提供插槽名称和 props 类型检查的类型提示。
defineSlots()
只接受类型参数,没有运行时参数。类型参数应该是一个类型字面量,其中属性键是插槽名称,值类型是插槽函数。函数的第一个参数是插槽期望接收的 props,其类型将用于模板中的插槽 props。返回类型目前被忽略,可以是 any
,但我们将来可能会利用它来检查插槽内容。
它还返回 slots
对象,该对象等同于在 setup 上下文中暴露或由 useSlots()
返回的 slots
对象。
<template>
<view>
<slot name="header" :msg="msg"></slot>
<slot :num="num"></slot>
<slot name="num1" :num="num"></slot>
<slot name="num2" :num="num"></slot>
<slot name="msgTrue" :msg="msg"></slot>
<slot name="msgFalse" :msg="msg"></slot>
<slot name="footer" :arr="arr"></slot>
</view>
</template>
<script setup lang='uts'>
const msg = ref('foo msg')
const num = ref<number>(0)
const arr = ref<string[]>(['a', 'b', 'c'])
defineSlots<{
header(props : { msg : string }) : any,
default(props : { num : number }) : any,
num1(props : { num : number }) : any,
num2(props : { num : number }) : any,
msgTrue(props : { msg : string }) : any,
msgFalse(props : { msg : string }) : any,
footer(props : { arr : string[] }) : any
}>()
</script>
在 <script setup>
使用 slots
和 attrs
的情况应该是相对来说较为罕见的,因为可以在模板中直接通过 $slots
和 $attrs
来访问它们。在你的确需要使用它们的罕见场景中,可以分别用 useSlots
和 useAttrs
两个辅助函数:
<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 setup lang="uts">
import Foo from './Foo-composition.uvue'
const msgTrue = ref({
isShow: true,
name: 'msgTrue'
})
const msgFalse = ref({
isShow: false,
name: 'msgFalse'
})
</script>
<template>
<view>
<view class="mb-10 flex flex-row justify-between">
<text>hasPropsAttr</text>
<text id="has-props-attr">{{ hasPropsAttr }}</text>
</view>
<view class="mb-10 flex flex-row justify-between">
<text>hasEmitsAttr</text>
<text id="has-emits-attr">{{ hasEmitsAttr }}</text>
</view>
<view class="mb-10 flex flex-row justify-between">
<text>hasClassAttr</text>
<text id="has-class-attr">{{ hasClassAttr }}</text>
</view>
</view>
</template>
<script setup lang="uts">
defineEmits(['childClick'])
defineProps({
str: {
type: String,
required: true
}
})
const attrs = useAttrs()
const hasPropsAttr = computed(():boolean => {
return attrs['val'] != null
})
const hasEmitsAttr = computed(():boolean => {
return attrs['childClick'] != null
})
const hasClassAttr = computed(():boolean => {
return attrs['class'] != null
})
</script>
示例 详情
<script setup lang="uts">
import CompForHFunction from '@/components/CompForHFunction.uvue'
import CompForHFunctionWithSlot from '@/components/CompForHFunctionWithSlot.uvue'
import Foo from './Foo.uvue'
const msg = ref('default msg')
// 故意外部声明为UTSJSONObject
const msgProps = { class: 'uni-common-mt msg', style: { color: 'blue' } }
const render = ():VNode => h('view', { class: 'page' }, [
h(CompForHFunctionWithSlot, {}, () : VNode[] => [h('text', { class: 'comp-slot' }, 'component slot')]),
h(CompForHFunction, { msg: msg.value }),
h('text', msgProps, msg.value),
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: () => {
msg.value = 'new msg'
}
},
'click'
)
])
</script>
<template><render /></template>
<style>
.btn {
color: red;
}
</style>