解决中文输入法导致的频繁 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 中,都可以通过封装一个通用的输入组件来解决这一问题,从而避免中文输入过程中出现的"值被提前更新"的情况。这样既能保持输入体验的流畅性,也能确保数据的准确性。

相关推荐
hh随便起个名4 小时前
力扣二叉树的三种遍历
javascript·数据结构·算法·leetcode
我是小路路呀4 小时前
element级联选择器:已选中一个二级节点,随后又点击了一个一级节点(仅浏览,未确认选择),此时下拉框失去焦点并关闭
javascript·vue.js·elementui
程序员爱钓鱼5 小时前
Node.js 编程实战:文件读写操作
前端·后端·node.js
PineappleCoder5 小时前
工程化必备!SVG 雪碧图的最佳实践:ID 引用 + 缓存友好,无需手动算坐标
前端·性能优化
JIngJaneIL5 小时前
基于springboot + vue古城景区管理系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot·后端
敲敲了个代码5 小时前
隐式类型转换:哈基米 == 猫 ? true :false
开发语言·前端·javascript·学习·面试·web
澄江静如练_6 小时前
列表渲染(v-for)
前端·javascript·vue.js
JustHappy6 小时前
「chrome extensions🛠️」我写了一个超级简单的浏览器插件Vue开发模板
前端·javascript·github
Loo国昌6 小时前
Vue 3 前端工程化:架构、核心原理与生产实践
前端·vue.js·架构