当前位置: 首页 > 知识库问答 >
问题:

如何使用Vue Comtion API/Vue 3观看道具变化?

蒙华翰
2023-03-14

虽然Vue Composition API RFC Reference site有许多高级使用场景,其中包括观看模块,但没有关于如何观看组件道具的示例?

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。Tony Tom的第一个答案虽然有效,但会导致一个类型错误。米哈尔·列夫的回答解决了这个问题。所以我在后面用typescript标记了这个问题。

EDIT2:这是我为这个定制的select组件设计的精巧而简洁的反应式布线版本,位于

<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://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>

共有3个答案

曾高杰
2023-03-14

这并没有解决如何“监视”属性的问题。但是,如果你想知道如何使用Vue的合成API使道具具有响应性,那么请继续阅读。在大多数情况下,你不应该写一堆代码来“观察”事情(除非你在更改后产生了副作用)。

秘密在于:组件道具是被动的。一旦你接触到一个特定的道具,它就不是被动的。这种分割或访问对象一部分的过程称为“分解”。在新的Composition API中,您需要习惯于一直思考这个问题——这是决定使用reactive()vsref()的关键部分。

所以我的建议(下面的代码)是,如果您想保留反应性,请获取您需要的属性并将其设为ref

export default defineComponent({
  name: 'MyAwesomestComponent',
  props: {
    title: {
      type: String,
      required: true,
    },
    todos: {
      type: Array as PropType<Todo[]>,
      default: () => [],
    },
    ...
  },
  setup(props){ // this is important--pass the root props object in!!!
    ...
    // Now I need a reactive reference to my "todos" array...
    var todoRef = toRefs(props).todos
    ...
    // I can pass todoRef anywhere, with reactivity intact--changes from parents will flow automatically.
    // To access the "raw" value again:
    todoRef.value
    // Soon we'll have "unref" or "toRaw" or some official way to unwrap a ref object
    // But for now you can just access the magical ".value" attribute
  }
}

我真的希望Vue奇才们能想出办法让这更简单。。。但据我所知,这是我们必须用合成API编写的代码类型。

这里是官方文档的链接,它们会直接提醒你不要破坏道具。

阎鸿煊
2023-03-14

我只是想在上面的答案中添加更多细节。正如米哈尔所提到的,propscoming是一个对象,作为一个整体是被动的。但是,道具对象中的每个关键点本身都不是被动的。

我们需要调整watch签名,使其与ref值相比,在reactive对象中获得一个值

// watching value of a reactive object (watching a getter)

watch(() => props.selected, (selection, prevSelection) => { 
   /* ... */ 
})
// directly watching a ref

const selected = ref(props.selected)

watch(selected, (selection, prevSelection) => { 
   /* ... */ 
})

尽管问题中没有提到这种情况,但还有一些信息:如果我们想查看多个属性,可以传递一个数组,而不是一个引用

// Watching Multiple Sources

watch([ref1, ref2, ...], ([refVal1, refVal2, ...],[prevRef1, prevRef2, ...]) => { 
   /* ... */ 
})

袁增
2023-03-14

如果你看一下这里输入的watch,很明显watch的第一个参数可以是数组、函数或Ref

props传递给setup函数是被动对象(可能由reactive()生成),它的属性是getter。因此,在本例中,您要做的是将getter的值作为watch-string“initial”的第一个参数传递。因为Vue 2$watchAPI是在后台使用的(Vue 3中也存在相同的函数),所以实际上您是在尝试监视组件实例上名为“initial”的不存在属性。

您的回调只会被调用一次,不会再被调用。之所以至少调用一次,是因为新的watchAPI的行为与当前的$watch一样,带有立即选项(更新03/03/2021-这一点后来被更改,在Vue 3的发行版中,watch与Vue 2中的方式相同)

所以你无意中做了Tony Tom建议的同样的事情,但价值观是错误的。在这两种情况下,如果您使用的是TypeScript,则它都不是有效代码

你可以这样做:

watch(() => props.selected, (first, second) => {
      console.log(
        "Watch props.selected function called with args:",
        first,
        second
      );
    });

在这里,Vue立即执行第一个函数来收集依赖项(以了解应该触发回调的内容),第二个函数是回调本身。

另一种方法是使用toRefs转换道具对象,使其属性为Ref类型

 类似资料:
  • 我有一个和道具同名的守望者。道具是在某个事件的父组件中动态设置的。我需要一些东西,每次在父组件上触发事件时,都会用属性值触发子组件中的某个函数,即使它设置的属性相同。是否有任何观察者选项或其他处理此类案件的方法允许我这样做? 我尝试了什么(这是在组件中): 仅在newVal与oldVal不同时打印,我需要一些函数来触发,即使它们相同。父组件如下所示: 这些是父数据和方法:

  • 请求header GET /v1/activities/{频道id}/getAuth Authorization:Bearer {ACCESS TOKEN} Content-Type:application/json 注: 请将上方的{ACCESS TOKEN}替换为您的ACCESS TOKEN 请将"{频道id}"替换您需要获取的频道id 返回 { "status": "y", "ms

  • 在角度分量的顶部有以下初始化。 在我的组件中的某个地方,我使用选择器对Ngrx存储进行以下调用,以获取可观测数据。所有这些都很好,我得到了我想要的数据。 我需要知道这个可观察的什么时候完成。我需要设置一个布尔值,当所有可观察到的数据都试图完成时,它将关闭加载指示器。这是通过Web服务完成的。 因为可观测的源来自其他地方,所以我无法挂起“完整”回调

  • 使用RxJS如何在观察者上传递新属性?所以基本上我希望道具“customProp”可以被观察到 添加更多的信息-所以基本上,我试图创建一个冷可观察的,其中制片人需要一个道具,应该来自订户 添加用法信息--coldObservable是DB连接函数,customProp是需要在DB上执行的查询

  • 问题内容: 我想知道是否有什么办法可以在程序运行时观察变量值的变化。当然不使用调试器,我想以 编程方式进行 。例如: 因此,在运行时,如果在我的项目中任何类的任何方法中修改了此value 事件,则应调用该事件。 问题答案: 您需要用一个类替换该类型,该类将在值更改时调用您的侦听器。您可能想忽略未实际更改的值的设置。 例如 您可以使用字节码注入执行此替换,但是更改原始代码非常简单。 一种替代方法是监

  • 类型 备注 internal_mobile 国内手机 internal_pc 国内PC intranet_mobile 内网手机 intranet_pc 内网PC oversea_mobile 国外手机 oversea_pc 国外PC 获取定制列表 请求header GET /v1/activities/{频道id}/watchUrl Authorization:Bearer {ACCESS TO