虽然 Vue Composition API RFC Reference 站点 有很多高级使用场景,包括 watch
模块,但没有关于 如何观看组件道具 的示例?
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>
编辑:虽然我的问题和代码示例最初是使用 JavaScript,但我实际上使用的是 TypeScript。托尼汤姆的第一个答案虽然有效,但会导致类型错误。 Michal Levý的回答解决了这个问题。所以我之后用 typescript
标记了这个问题。
EDIT2 :这是我为这个自定义选择组件的反应性接线的抛光但准系统版本,在 <b-form-select>
来自 bootstrap-vue
(否则不可知论实现,但这个底层组件确实发出 @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>
原文由 ux.engineer 发布,翻译遵循 CC BY-SA 4.0 许可协议
如果您查看
watch
在此处 输入,很明显watch
的第一个参数可以是数组、函数或Ref<T>
props
传递给setup
函数是反应性对象(可能由readonly(reactive())
制作,它的属性是吸气剂。所以你所做的是将值传递为watch
的第一个参数 - 在这种情况下是字符串“initial”。因为 Vue 2$watch
API 在后台使用(并且在 Vue 3 中 存在 相同的功能),您实际上是在尝试在您的组件实例上查看名称为“initial”的不存在的属性。您的回调只被调用一次,再也不会被调用。至少调用一次的原因是因为新的
watch
API 的行为类似于当前的$watch
带有immediate
选项( 更新/后来更改了 03/03在 Vue 3 的发布版本中,watch
与 Vue 2 中的懒惰方式相同)因此,您意外地做了托尼汤姆建议的相同事情,但价值错误。在这两种情况下,如果您使用的是 TypeScript,它都是无效的代码
你可以这样做:
这里第一个函数由 Vue 立即执行以收集依赖项(以了解应该触发回调的内容),第二个函数是回调本身。
其他方法是使用
toRefs
转换 props 对象,因此它的属性类型为Ref<T>
您可以将它们作为watch
的第一个参数传递无论如何,大部分时间都不需要观看道具 - 只需在模板中直接使用
props.xxx
(或setup
),然后让 Vue 完成剩下的工作