部分数据来源:ChatGPT
在 Vue3 中, customRef
是一个新的 API,它让我们可以更加灵活地实现自定义的响应式数据追踪逻辑。而在不同的场景下,我们可能需要使用不同的数据追踪逻辑来满足业务需求。下面我们通过一个案例来讲解在 Data、Watch 和 Computed 等场景下如何正确实现 ref 计算器。
案例介绍
我们将实现一个简单的 ref 计算器,在该计算器中,用户可以输入两个数字并选择相应的运算符来进行计算。与一般的计算器相比,我们的计算器会增加一个功能:在结果超过 1000 时,会将结果自动缩小为原来的一半。为了实现这样的功能,我们需要对计算结果进行监听,并在结果超过 1000 时进行转化处理。
Data 场景下的实现
首先我们来看 Data 场景下的实现。在这种情况下,我们可以使用 reactive
方法将 Data 对象包装成可响应式数据,并在其中声明计算结果等属性。同时,我们可以通过 watch
方法对计算结果进行监听,并实现自定义的数据追踪逻辑。以下是示例代码:
<template>
<div>
<input v-model.number="num1" />
<select v-model="operator">
<option value="+">+</option>
<option value="-">-</option>
<option value="*">*</option>
<option value="/">/</option>
</select>
<input v-model.number="num2" />
<button @click="calculate">Calculate</button>
<p>Result: {{ result }}</p>
</div>
</template>
<script>
import { reactive, watch } from 'vue'
export default {
setup() {
const data = reactive({
num1: 0,
num2: 0,
operator: '+',
result: 0
})
const calculate = () => {
switch (data.operator) {
case '+':
data.result = data.num1 + data.num2
break
case '-':
data.result = data.num1 - data.num2
break
case '*':
data.result = data.num1 * data.num2
break
case '/':
data.result = data.num1 / data.num2
break
}
}
watch(() => data.result, (newResult, oldResult) => {
if (newResult > 1000 && oldResult <= 1000) {
data.result = newResult / 2
}
}, { immediate: true })
return {
...data,
calculate
}
}
}
在上述代码中,我们使用了 reactive
方法将 Data 对象包装成可响应式数据,并声明了计算结果等属性。同时,在 watch
方法中,我们对计算结果进行了监听,并实现了自定义的数据追踪逻辑。具体来说,当计算结果超过 1000 时,我们会将结果自动缩小为原来的一半。
Watch 场景下的实现
接下来我们来看 Watch 场景下的实现。在这种情况下,由于 Watch 的监听器可以返回一个函数,在该函数中我们可以自定义数据追踪逻辑。因此,我们可以直接使用 Watch 方法对计算结果进行监听,并在回调函数中实现自定义的数据追踪逻辑。
以下是示例代码:
<template>
<div>
<input v-model.number="num1" />
<select v-model="operator">
<option value="+">+</option>
<option value="-">-</option>
<option value="*">*</option>
<option value="/">/</option>
</select>
<input v-model.number="num2" />
<button @click="calculate">Calculate</button>
<p>Result: {{ result }}</p>
</div>
</template>
<script>
import { ref, watch } from 'vue'
export default {
setup() {
const num1 = ref(0)
const num2 = ref(0)
const operator = ref('+')
const result = ref(0)
const calculate = () => {
switch (operator.value) {
case '+':
result.value = num1.value + num2.value
break
case '-':
result.value = num1.value - num2.value
break
case '*':
result.value = num1.value * num2.value
break
case '/':
result.value = num1.value / num2.value
break
}
}
watch(result, (newResult, oldResult) => {
if (newResult > 1000 && oldResult <= 1000) {
result.value = newResult / 2
}
})
return {
num1,
num2,
operator,
result,
calculate
}
}
}
在上述代码中,我们使用了 ref
方法将计算器的各个数据项转化为可响应式数据。同时,我们使用了 Watch 方法对计算结果进行监听,并实现了自定义的数据追踪逻辑。
Computed 场景下的实现
最后,我们来看 Computed 场景下的实现。在这种情况下,我们可以使用 customRef
方法创建一个自定义的 ref,并在其中实现自定义的数据追踪逻辑。
以下是示例代码:
<template>
<div>
<input v-model.number="num1" />
<select v-model="operator">
<option value="+">+</option>
<option value="-">-</option>
<option value="*">*</option>
<option value="/">/</option>
</select>
<input v-model.number="num2" />
<button @click="calculate">Calculate</button>
<p>Result: {{ result }}</p>
</div>
</template>
<script>
import { customRef } from 'vue'
export default {
setup() {
const num1 = customRef((track, trigger) => {
let value = 0
return {
get() {
track()
return value
},
set(newValue) {
value = newValue
trigger()
}
}
})
const num2 = customRef((track, trigger) => {
let value = 0
return {
get() {
track()
return value
},
set(newValue) {
value = newValue
trigger()
}
}
})
const operator = customRef((track, trigger) => {
let value = '+'
return {
get() {
track()
return value
},
set(newValue) {
value = newValue
trigger()
}
}
})
const result = customRef((track, trigger) => {
const calculate = () => {
switch(operator.value) {
case '+':
return num1.value + num2.value
case '-':
return num1.value - num2.value
case '*':
return num1.value * num2.value
case '/':
return num1.value / num2.value
default:
return 0
}
}
return {
get() {
track()
return calculate()
},
set() {
// 禁止直接设置 result 的值
}
}
})
const calculate = () => {
result.value = result.value // 触发 result 的 get 操作,从而触发其他 value 的 track 操作
}
return {
num1,
num2,
operator,
result,
calculate
}
}
}
</script>