虽然Vue Composition API RFC Reference站点有许多watch
模块的高级使用场景,但没有关于**如何观看组件 prop **的示例?
Vue Composition API RFC的主页或Github中的vuejs/composition-api中也没有提到它。
我创建了一个Codesandbox来详细说明这个问题。
<template>
<div id="app">
<img width="25%" src="./assets/logo.png">
<br>
<p>Prop watch demo with select input using v-model:</p>
<PropWatchDemo :selected="testValue"/>
</div>
</template>
<script>
import { createComponent, onMounted, ref } from "@vue/composition-api";
import PropWatchDemo from "./components/PropWatchDemo.vue";
export default createComponent({
name: "App",
components: {
PropWatchDemo
},
setup: (props, context) => {
const testValue = ref("initial");
onMounted(() => {
setTimeout(() => {
console.log("Changing input prop value after 3s delay");
testValue.value = "changed";
// This value change does not trigger watchers?
}, 3000);
});
return {
testValue
};
}
});
</script>
<template>
<select v-model="selected">
<option value="null">null value</option>
<option value>Empty value</option>
</select>
</template>
<script>
import { createComponent, watch } from "@vue/composition-api";
export default createComponent({
name: "MyInput",
props: {
selected: {
type: [String, Number],
required: true
}
},
setup(props) {
console.log("Setup props:", props);
watch((first, second) => {
console.log("Watch function called with args:", first, second);
// First arg function registerCleanup, second is undefined
});
// watch(props, (first, second) => {
// console.log("Watch props function called with args:", first, second);
// // Logs error:
// // Failed watching path: "[object Object]" Watcher only accepts simple
// // dot-delimited paths. For full control, use a function instead.
// })
watch(props.selected, (first, second) => {
console.log(
"Watch props.selected function called with args:",
first,
second
);
// Both props are undefined so its just a bare callback func to be run
});
return {};
}
});
</script>
EDIT:虽然我的问题和代码示例最初是使用JavaScript的,但我实际上使用的是TypeScript。Tony Tom的第一个答案虽然有效,但导致了一个类型错误。Michal Levý的答案解决了这个问题。所以我在后面用typescript
标记了这个问题。
EDIT 2:这是我为这个自定义选择组件设计的React式连接的最佳但最基本的版本,在bootstrap-vue
* 的<b-form-select>
之上(否则实现是不可知的,但这个底层组件确实会发出@input和@change事件,这取决于更改是通过编程还是用户交互进行的)*。
<template>
<b-form-select
v-model="selected"
:options="{}"
@input="handleSelection('input', $event)"
@change="handleSelection('change', $event)"
/>
</template>
<script lang="ts">
import {
createComponent, SetupContext, Ref, ref, watch, computed,
} from '@vue/composition-api';
interface Props {
value?: string | number | boolean;
}
export default createComponent({
name: 'CustomSelect',
props: {
value: {
type: [String, Number, Boolean],
required: false, // Accepts null and undefined as well
},
},
setup(props: Props, context: SetupContext) {
// Create a Ref from prop, as two-way binding is allowed only with sync -modifier,
// with passing prop in parent and explicitly emitting update event on child:
// Ref: https://v2.vuejs.org/v2/guide/components-custom-events.html#sync-Modifier
// Ref: https://medium.com/@jithilmt/vue-js-2-two-way-data-binding-in-parent-and-child-components-1cd271c501ba
const selected: Ref<Props['value']> = ref(props.value);
const handleSelection = function emitUpdate(type: 'input' | 'change', value: Props['value']) {
// For sync -modifier where 'value' is the prop name
context.emit('update:value', value);
// For @input and/or @change event propagation
// @input emitted by the select component when value changed <programmatically>
// @change AND @input both emitted on <user interaction>
context.emit(type, value);
};
// Watch prop value change and assign to value 'selected' Ref
watch(() => props.value, (newValue: Props['value']) => {
selected.value = newValue;
});
return {
selected,
handleSelection,
};
},
});
</script>
6条答案
按热度按时间ruoxqz4g1#
如果您看一下这里的
watch
类型,就会清楚地看到watch
的第一个参数可以是array
、function
或Ref<T>
传递给
setup
函数的props
是React对象(很可能是readonly(reactive())
,它的属性是getter。因此,您要做的是将getter的值作为watch
的第一个参数传递,在本例中为字符串“initial”。因为Vue 2$watch
API是在幕后使用的(Vue 3中存在相同的函数),您实际上是在尝试监视组件示例上名为“initial”的不存在的属性。您的回调仅被调用一次。至少调用一次的原因是新的
watch
API的行为类似于当前$watch
(带有immediate
选项)(更新日期:03/03/2021-这一点后来发生了变化,在Vue 3的发布版本中,watch
与Vue 2中的延迟方式相同)因此,您无意中执行了Tony Tom建议的相同操作,但使用了错误的值。在这两种情况下,当您使用TypeScript时,它都不是有效代码。
您可以改为执行以下操作:
这里,Vue立即执行第一个函数以收集依赖项(以了解什么应该触发回调),第二个函数是回调本身。
另一种方法是使用
toRefs
转换props对象,这样它的属性将是Ref<T>
类型,您可以将它们作为watch
的第一个参数传递。不过大多数时候看 prop 是不需要的,只需直接在你的模板中使用
props.xxx
(或者setup
),剩下的就让Vue来做了。snz8szmq2#
我只是想在上面的答案中添加一些细节。正如Michal所提到的,
props
是一个对象,作为一个整体是React的。但是,props对象中的每个键本身并不是React的。我们需要调整
reactive
对象中某个值的watch
签名,并与ref
值进行比较**注意:**在使用此可能错误的代码之前,请参阅下面Michal Levý的注解:
只是一些更多的信息,即使它不是问题中提到的情况:如果我们想监视多个属性,可以传递一个数组而不是一个引用
oxcyiej73#
这并没有解决如何“监视”属性的问题。但是如果你想知道如何让 prop 对Vue的Composition API做出响应,那就继续读下去。在大多数情况下,你不应该写一堆代码来“监视”东西(除非你在修改后产生了副作用)。
秘诀是:组件
props
是被动的。一旦你访问了一个特定的 prop ,它就不是被动的了。这个划分或访问对象的一部分的过程被称为“解构”。在新的组合API中,你需要习惯于一直考虑这个问题--这是决定使用reactive()
还是ref()
的关键部分。所以我的建议(下面的代码)是,如果你想保持React性,你可以把你需要的属性设置为
ref
:我当然希望Vue向导能找出如何使这变得更容易...但据我所知,这是我们必须用组合API编写的代码类型。
这里有一个官方文档的链接,他们直接警告你不要破坏 prop 。
dz6r00yl4#
在我的例子中,我使用
key
求解也许你的情况会像这样
但是我不知道它与
watch
相比有什么优点和缺点m0rkklqb5#
更改您的手表方法如下。
vshtjzan6#
上面的选项对我都不起作用,但是我想我找到了一个简单的方法,似乎可以很好地在composition API中保持vue2的编码风格
只需为 prop 创建一个
ref
别名,如下所示:myPropAlias = ref(props.myProp)
你从化名开始
对我来说就像一个魅力和最小