javascript - 如何使用 Vue Composition API/Vue 3 观察 Prop 变化?

标签 javascript typescript vue.js vuejs3 vue-composition-api

Vue Composition API RFC Reference site watch 有许多高级使用场景模块, 上没有示例如何观看组件 Prop ?
Vue Composition API RFC's main page 中也没有提到它或 vuejs/composition-api in Github .
我创建了一个 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 标记了这个问题然后。
编辑2 : 这是我在 <b-form-select> 之上的这个自定义选择组件的 react 布线的抛光但准系统版本来自 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>

最佳答案

如果你看一下 watch 输入 here 很明显 watch 的第一个参数可以是数组、函数或 Ref<T>props 传递给 setup 函数是 react 对象(可能由 readonly(reactive()) 制成,它的属性是 setter/getter 。所以你要做的是将 setter/getter 的值作为 watch 的第一个参数传递 - 在这种情况下是字符串“初始”。因为 Vue 2 $watch API在引擎盖下使用(与 Vue 3 中的相同函数 exists),您实际上是在尝试在组件实例上查看名称为“initial”的不存在的属性。
您的回调只被调用一次,再也不会被调用。至少调用一次的原因是因为新的 watch API 的行为类似于当前的 $watchimmediate 选项( 更新 03/03/2021 - 后来更改了,在 Vue 3 的发布版本中,watch 和以前一样懒惰在 Vue 2)
因此,您意外地做了托尼汤姆建议的相同事情,但值(value)错误。在这两种情况下,如果您使用的是 TypeScript,它都是无效的代码
你可以这样做:

watch(() => props.selected, (first, second) => {
      console.log(
        "Watch props.selected function called with args:",
        first,
        second
      );
    });
这里第一个函数由 Vue 立即执行以收集依赖项(以了解应该触发回调的内容),第二个函数是回调本身。
其他方法是使用 toRefs 转换 props 对象,因此它的属性将是 Ref<T> 类型,您可以将它们作为 watch 的第一个参数传递
无论如何,大部分时间都不需要观看 Prop - 只需在模板中直接使用 props.xxx (或 setup ),然后让 Vue 完成剩下的工作

关于javascript - 如何使用 Vue Composition API/Vue 3 观察 Prop 变化?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59125857/

相关文章:

javascript - 如何避免在 Angular 2 中多次执行不纯管道?

node.js - 带有 TypeScript : Declaration expected compiler error after @component 的 Angular2

asynchronous - 相当于Dart的TypeScript `Completer`

vue.js - Vuejs 不同种类的入口点令人困惑

javascript - 为什么此事件监听器会拾取在添加之前发送的事件

javascript - 添加 CSS 的缩放转换问题的 Jquery UI 可拖动

java - deployjava.getJREs 不适用于 Windows 7 64 位 jre

javascript - vue.js v-link 不起作用

javascript - 是否可以使用 FlatBuffers 将序列化数据流式传输到文件中?

javascript - 如何让 div 仅包含上次(javascript)函数调用(如果多次调用)中的元素?