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

Posted

技术标签:

【中文标题】如何正确使用 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 【发布时间】:2021-12-24 05:19:12 【问题描述】:

我有一个有用的小 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>

【问题讨论】:

【参考方案1】:

完整的警告是:

[Vue 警告]:无关的非道具属性(modelModifiers)已传递给组件,但无法自动继承,因为组件呈现片段或文本根节点。

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

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

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

demo

【讨论】:

我不知道为什么我没有收到这方面的通知,但是很好的解释! modelModifiers 和 vue 做它自己的事件触发版本有点让我失望,但这是一个很好的解释!【参考方案2】:

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

<script setup>

import  onMounted  from 'vue'

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

onMounted(() => 

  console.dir(props.readonly)

)

</script>

在 Vite 设置中,defineProps 是全局注册的,似乎是唯一不需要 import 的方法,我不知道这是否适用于任何其他编译器方法.

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

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

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

【讨论】:

感谢您的评论!我尝试了这种语法,不幸的是没有任何改变。我想我现在会使用 v-model 并希望一切顺利。我几乎可以肯定我需要以某种身份使用:value="modelValue"

以上是关于如何正确使用 v-model 和 Composition API :value="modelValue" 语法?将 Vue2 自定义输入转换为 Vue3的主要内容,如果未能解决你的问题,请参考以下文章

Composit PK hashcode与equals

Vue.js 如何使用 v-model 和计算属性以及复选框来镜像输入字段

v-model 更改时如何触发事件?

v-model拿不到@input的返回值

Fluent NHibernate Composite Id 长度设置不正确

比较v-bind和v-model