解决中文输入法导致的频繁 Input 事件!

大家好,我是 前端架构师 - 大卫

更多优质内容请关注微信公众号 @程序员大卫

初心为助前端人🚀,进阶路上共星辰✨,

您的点赞与关注❤️,是我笔耕不辍的灯💡。

背景

在使用 input 输入框时,如果正在通过中文输入法打字,会出现一个问题:输入还未完成(比如拼音还没选字、还没按下回车)时,input 事件就已经触发了。 这会导致我们在还没真正确认输入内容时,数据就被提前更新了。

示例代码如下:

html 复制代码
<script setup lang="ts">
const onInput = (e: Event) => {
	const value = (e.target as HTMLInputElement).value;
	console.log(value);
};
</script>

<template>
	<input @input="onInput" />
</template>

在上图的场景中,我们输入中文时,明明还没确认文字,但控制台已经打印了输入内容。

解决方案

要解决这个问题,可以利用 compositionstartcompositionend 事件:

  • compositionstart:中文输入法开始输入时触发
  • compositionend:中文输入法输入结束(确认文字)时触发

因此,我们可以在输入法开始时设置一个标记(flag),在输入完成前不触发 input 逻辑;等输入结束时,再统一更新一次值。

Vue3 封装组件示例

下面演示一个在 Vue3 中封装的输入组件。

说明:这里的 update:modelValue 是为了保证组件能正常配合 v-model 使用。modelValue 的类型为 String | Number,参考了 Element-Plus 的实现。

html 复制代码
<script setup lang="ts">
import { ref, useAttrs } from "vue";

const props = defineProps({
	modelValue: {
		type: [String, Number],
	},
});

defineOptions({
	inheritAttrs: false,
});

const emit = defineEmits<{
	compositionstart: [e: CompositionEvent];
	compositionend: [e: CompositionEvent];
	input: [value: string | number];
	"update:modelValue": [value: string | number];
}>();

const attrs = useAttrs();
const isComposing = ref(false);

const updateValue = (e: CompositionEvent | Event) => {
	const value = (e.target as HTMLInputElement).value;
	emit("update:modelValue", value); // 保证 v-model 正常工作
	emit("input", value);
};

// 输入法开始
const onCompositionstart = (e: CompositionEvent) => {
	emit("compositionstart", e);
	isComposing.value = true;
};

// 输入法结束
const onCompositionend = (e: CompositionEvent) => {
	emit("compositionend", e);
	isComposing.value = false;
	updateValue(e);
};

// 非中文输入时触发
const onInput = (e: Event) => {
	if (isComposing.value) return;
	updateValue(e);
};
</script>

<template>
	<input
		v-bind="attrs"
		:value="props.modelValue"
		@compositionstart="onCompositionstart"
		@compositionend="onCompositionend"
		@input="onInput"
	/>
</template>

父组件调用方式(Parent.vue):

html 复制代码
<script lang="ts" setup>
import InputIME from "./components/InputIME.vue";

const onInput = (value: string | number) => {
	console.log(value);
};
</script>

<template>
	<InputIME @input="onInput" />
</template>

Vue2 封装组件示例

在 Vue2 中,也可以采用类似的思路:

说明:通过 inheritAttrs: false 阻止默认事件透传,再利用 computed 中的 inputListeners 自定义事件绑定,手动管理输入法的 compositionstartcompositionendinput 事件。

html 复制代码
<script>
export default {
	inheritAttrs: false,
	props: {
		value: [Number, String],
	},
	data() {
		return {
			isComposing: false,
		};
	},
	computed: {
		inputListeners() {
			return {
				...this.$listeners,
				input: (e) => {
					if (this.isComposing) return;
					this.updateValue(e);
				},
				compositionstart: (e) => {
					this.$emit("compositionstart", e);
					this.isComposing = true;
				},
				compositionend: (e) => {
					this.$emit("compositionend", e);
					this.isComposing = false;
					this.updateValue(e);
				},
			};
		},
	},
	methods: {
		updateValue(e) {
			const value = e.target.value;
			this.$emit("input", value);
		},
	},
};
</script>

<template>
	<input :value="value" v-bind="$attrs" v-on="inputListeners" />
</template>

父组件调用方式(Parent.vue):

html 复制代码
<script>
import InputIME from "./components/InputIME.vue";

export default {
	components: {
		InputIME,
	},
	methods: {
		onInput(value) {
			console.log(value);
		},
	},
};
</script>

<template>
	<InputIME @input="onInput" />
</template>

总结

无论是在 Vue3 还是 Vue2 中,都可以通过封装一个通用的输入组件来解决这一问题,从而避免中文输入过程中出现的"值被提前更新"的情况。这样既能保持输入体验的流畅性,也能确保数据的准确性。

相关推荐
程序员黑豆3 小时前
Java包装类:基本类型与对象的桥梁
java·前端·ai编程
bugcome_com5 小时前
JWT 知识小课堂:从入门到精通,一文吃透 JSON Web Token
前端·json
To_OC6 小时前
写了三天 Next.js,我的 React 世界观被拆了又装回去
前端·全栈·next.js
独泪了无痕6 小时前
viewer.js 安装与配置指南:实现图片预览功能
前端·vue.js
起床学FPGA8 小时前
AI回答问题之后,会自动跳到最下面的问题
开发语言·javascript·ecmascript
Fluxart.ai8 小时前
电商商品图审核怎么自动化?规则引擎、人工复核与发布门禁
java·前端·自动化
why技术8 小时前
AI 写的文章,可能都带着手敲一遍都去不掉的“隐形水印”。
前端·人工智能·后端
愚公搬代码9 小时前
【愚公系列】《Android应用案例开发大全》016-LBS类应用掌上杭州(辅助工具类的开发)
android·前端
CodeSheep9 小时前
又一个华为天才少年,离职了!
前端·后端·程序员
kyriewen9 小时前
面试官说"打开你的AI工具"——我才发现,他考的根本不是写代码
前端·人工智能·面试