web-dev-qa-db-ja.com

Vue Composition API / Vue 3?

Vue Composition API RFCリファレンスサイト には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>

[〜#〜] edit [〜#〜]:私の質問とコード例は当初JavaScriptでしたが、実際にはTypeScriptを使用しています。トニートムの最初の答えは機能していますが、タイプエラーにつながります。これはMichalLevýの回答によって解決されました。そのため、後でこの質問に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://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>
5
ux.engineer

上記の回答にさらに詳細を追加したかっただけです。 Michalが述べたように、propsはオブジェクトであり、全体としてリアクティブです。ただし、propsオブジェクトの各キーはそれ自体では反応しません。

watch値と比較して、reactiveオブジェクトの値のref署名を調整する必要があります

// 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, ...]) => { 
   /* ... */ 
})

0
Syam Pillai

パスを使用して、特定のプロパティに到達できます。

 watch("props.value", (newValue: Props['value']) => {
      selected.value = newValue;
    });
0