如何正确使用 v-model 和 Composition API :value="modelValue" 语法?将 Vue2 自定义输入转换为 Vue3

How to properly work with v-model and the Composition API :value="modelValue" syntax? Converting Vue2 custom input into Vue3

我有一个小工具 custom input 我一直在我的所有 Vue2 项目中使用(允许您使用自动对焦、自动​​增长和去抖动进行自定义),但现在我在 Vue3 中工作,我'一直在尝试创建更新版本。

我还没有完成,但是我遇到了 Vue3 的组合 API :value="modelValue" 语法的一些问题。在我的 CodeSandbox 中,我有两个输入,一个使用新语法,另一个直接使用 v-model。后者有效,而 :value="valueInner" 抛出 Extraneous non-props attributes 错误。

我在这里做错了什么,我怎样才能让它与 :value="modelValue" 语法一起工作?

干杯!

Link to CodeSandbox

注意: 我仍然需要添加自动增长并添加 App.vue.

中的所有用例

CInput

<template>
  <input
    ref="inputRef"
    data-cy="input-field"
    v-if="type !== 'textarea'"
    :disabled="disabled"
    :type="type"
    :placeholder="placeholder"
    :readonly="readonly"
    :required="required"
    :autofocus="autofocus"
    :debounce="debounce"
    :value="valueInner"
  />
  <!-- <input
    ref="inputRef"
    data-cy="input-field"
    v-if="type !== 'textarea'"
    :disabled="disabled"
    :type="type"
    :placeholder="placeholder"
    :readonly="readonly"
    :required="required"
    :autofocus="autofocus"
    :debounce="debounce"
    v-model="valueInner"
  /> -->
</template>

<script>
import { defineComponent, ref, onMounted, nextTick, watch } from "vue";

export default defineComponent({
  props: {
    /** HTML5 attribute */
    disabled: { type: String },
    /** HTML5 attribute (can also be 'textarea' in which case a `<textarea />` is rendered) */
    type: { type: String, default: "text" },
    /** HTML5 attribute */
    placeholder: { type: String },
    /** HTML5 attribute */
    readonly: { type: Boolean },
    /** HTML5 attribute */
    required: { type: Boolean },
    /** v-model */
    modelValue: { type: [String, Number, Date], default: "" },
    autofocus: { type: Boolean, default: false },
    debounce: { type: Number, default: 1000 },
  },
  emits: ["update:modelValue"],
  setup(props, { emit }) {
    const inputRef = ref(null);
    const timeout = ref(null);
    const valueInner = ref(props.modelValue);
    if (props.autofocus === true) {
      onMounted(() => {
        // I don't know we need nexttick
        nextTick(() => {
          inputRef.value.focus();
          // setTimeout(() => inputRef.value.focus(), 500);
        });
      });
    }
    watch(valueInner, (newVal, oldVal) => {
      const debounceMs = props.debounce;
      if (debounceMs > 0) {
        clearTimeout(timeout.value);
        timeout.value = setTimeout(() => emitInput(newVal), debounceMs);
        console.log(newVal);
      } else {
        console.log(newVal);
        emitInput(newVal);
      }
    });
    function emitInput(newVal) {
      let payload = newVal;
      emit("update:modelValue", payload);
    }
    // const onInput = (event) => {
    //   emit("update:modelValue", event.target.value);
    // };
    return { inputRef, valueInner };
  },
});
</script>

App.vue

<template>
  <CInput :autofocus="true" v-model.trim="inputValue1" />
  <CInput :autofocus="false" v-model.trim="inputValue2" />
  <pre>Input Value 1: {{ inputValue1 }}</pre>
  <pre>Input Value 2: {{ inputValue2 }}</pre>
</template>

<script>
import { ref } from "vue";
import CInput from "./components/CInput.vue";
export default {
  name: "App",
  components: { CInput },
  setup() {
    const inputValue1 = ref("");
    const inputValue2 = ref("");
    return { inputValue1, inputValue2 };
  },
};
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

到目前为止,我只使用 Vue 3 + Vite 构建了一个应用程序。 我只使用 <script setup> 方法并坚持使用它,这就是定义道具时的样子:

<script setup>

import { onMounted } from 'vue'

const props = defineProps({
  readonly: { type: Boolean },
  required: { type: Boolean },
})

onMounted(() => {

  console.dir(props.readonly)

})

</script>

在Vite设置中,defineProps是全局注册的,好像是唯一不需要import的方法,我不知道如果任何其他编译器方法都是如此。

v-model 在 Vue3 中弹出为 modelValue 而不是 value,也许你可以 defineProp modelValue 除非它默认总是存在?

这里有一些解释:https://v3.vuejs.org/guide/migration/v-model.html#_2-x-syntax

希望这适用于您并有所帮助。

完整的警告是:

[Vue warn]: Extraneous non-props attributes (modelModifiers) were passed to component but could not be automatically inherited because component renders fragment or text root nodes.

CInput 有多个根节点(即 <input> 和评论节点),因此该组件被渲染为一个片段。 CInput 上的 .trim 修饰符通常传递到根节点的 v-model,如 demo 所示。由于实际代码是一个片段,Vue 无法为您决定在何处传递 modelModifiers 属性,从而导致您观察到警告。

不过,declaring a modelModifiers prop to receive the modifiers足以解决问题:

// CInput.vue
export default {
  props: {
    modelModifiers: {
      default: () => ({})
    }
  }
}

demo