在react当中,表单元素 input
中设置了 value
,则为受控组件,通过 onChange
事件中 setState()
改变 value
值来更新 state
值和DOM中渲染的值。但在vue中,表单元素设置 value
值,即使 value
值改变了,dom中 value
的表现也和data中的 value
不一致
vue和react中受控组件的不同
在 HTML 中,表单元素(如 <input>
、 <textarea>
和 <select>
)通常自己维护 state
,并根据用户输入进行更新。而在 React 中,可变状态(mutable state)通常保存在组件的 state
属性中,并且只能通过使用 setState()
来更新。
我们可以把两者结合起来,使 React 的 state
成为“唯一数据源”。渲染表单的 React 组件还控制着用户输入过程中表单发生
import React, { useState } from "react";
export default function App() {
const [vale, setValue] = useState(0);
const changeEvent = function(e) {
console.log(e);
setValue(123);//value和渲染的值都为123
}
return (
<div className="App">
<input type="text" value={value} onChange={changeEvent} />
</div>
);
}
复制代码
而在vue中,改变 value
的值,只有 data
中的状态改变了,而原生DOM中的 value
值并没有被改变,最终渲染出来的仍然为用户输入的值
<template>
<input :value="value" @input="setValue" />
</template>
<script>
export default {
name: "App",
components: {},
data: function () {
return {
value: '',
};
},
methods: {
setValue(e) {
this.value = 123//仅仅data中value的值改变了,DOM中渲染的value值仍为输入的值
}
}
};
</script>
复制代码
用vue写一个input受控组件
在日常业务中,受控组件的需求经常被用到,用来给input框输入的限制,例如一个仅可以输入数字的 input
框。在使用elementUI的时候,发现其 <el-input>
为受控组件,于是去 elementUI-github 上看了这种操作是如何实现的。
核心原理就是在更新自己的 data
值的同时,一起更新原生input DOM上的 value
。
代码如下:
首先写一个完全像一个普通的 <input>
元素一样使用的 <CtrlInput>
组件
<template>
<label>
<input v-bind="$attrs" :value="value" v-on="inputListeners" />
</label>
</template>
<script>
export default {
name: "CtrlInput",
props: {
value: {
type: String
}
},
computed: {
inputListeners() {
return {
//从父级添加所有的监听器
...this.$listeners,
// 然后我们添加自定义监听器
// 这里确保组件配合 `v-model` 的工作
input: e => {
this.$emit("input", e.target.value);
}
};
}
}
};
</script>
<style scoped></style>
复制代码
所有跟原生 input
相同的 attribute
和监听器都可以正常工作,并且确保组件配合 v-model
也可以工作
然后在 input
监听器中,设置 nativeInputValue
(原生DOM的 value
值)和 data
中的 value
一样即可。
<template>
<label>
<input ref="input" v-bind="$attrs" :value="value" v-on="inputListeners" />
</label>
</template>
<script>
export default {
name: "CtrlInput",
props: {
value: {
type: String
}
},
computed: {
inputListeners() {
return {
//从父级添加所有的监听器
...this.$listeners,
// 然后我们添加自定义监听器
// 这里确保组件配合 `v-model` 的工作
input: e => {
this.$emit("input", e.target.value);
// 保证原生的input value 是可控的
// ensure native input value is controlled
this.$nextTick(this.setNativeInputValue);
}
};
},
nativeInputValue() {
//将传入的值转为String,防止出错
return this.value === null || this.value === undefined
? ""
: String(this.value);
}
},
methods: {
setNativeInputValue() {
// 将展示的原生的input value 和this中的input value保持一致
const input = this.$refs.input;
if (!input) return;
if (input.value === this.nativeInputValue) return;
input.value = this.nativeInputValue;
}
},
watch: {
//将展示的原生的input value 和this中的input value保持一致
nativeInputValue() {
this.setNativeInputValue();
}
}
};
</script>
<style scoped></style>
复制代码
this.nextTick(this.setNativeInputValue); 这行代码意思就是在 data 中的 value 改变完,并且渲染完成后(使用 nextTick )再改变 nativeInputValue 的值,即可让原生DOM和自身的state保持一致
使用
需求:仅可输入数字的input框,输入其他字符就不显示
代码语言:javascript复制<template>
<div>
仅可以输入数字的受控组件
<CtrlInput
type="text"
placeholder="请输入数字"
:value="value"
@input="handleInput"
></CtrlInput>
</div>
</template>
<script>
import CtrlInput from "./CtrlInput";
export default {
name: "Demo",
components: {
CtrlInput
},
data() {
return {
value: ""
};
},
methods: {
handleInput(v) {
if (/^[0-9]*$/.test(v)) {
this.value = v;
}
}
}
};
</script>
复制代码