如何正确使用 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"
语法一起工作?
干杯!
注意:
我仍然需要添加自动增长并添加 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: () => ({})
}
}
}
我有一个小工具 custom input 我一直在我的所有 Vue2 项目中使用(允许您使用自动对焦、自动增长和去抖动进行自定义),但现在我在 Vue3 中工作,我'一直在尝试创建更新版本。
我还没有完成,但是我遇到了 Vue3 的组合 API :value="modelValue"
语法的一些问题。在我的 CodeSandbox 中,我有两个输入,一个使用新语法,另一个直接使用 v-model
。后者有效,而 :value="valueInner"
抛出 Extraneous non-props attributes
错误。
我在这里做错了什么,我怎样才能让它与 :value="modelValue"
语法一起工作?
干杯!
注意: 我仍然需要添加自动增长并添加 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: () => ({})
}
}
}