方案:
- 在模板使用指令(最简单、最稳妥)
- 在需要的输入上加 v-trim(支持 a-input):
js
<a-input v-trim v-model="form.xxx" />
或
js
<input v-trim v-model="xxx" />
- 为了兼容 a-input 内部异步渲染的 input,把指令改得更稳一些(插入与更新都尝试绑定):
js
Vue.directive('trim', {
inserted(el, binding, vnode) {
const bind = () => {
const input = el.querySelector('input,textarea');
if (!input || input.type === 'password' || input.__vTrimBound) return;
input.__vTrimBound = true;
input.addEventListener('blur', () => {
const oldValue = input.value;
const newValue = oldValue.trim();
if (newValue !== oldValue) {
input.value = newValue;
input.dispatchEvent(new Event('input', { bubbles: true }));
}
});
};
vnode.context.$nextTick(bind);
},
componentUpdated(el, binding, vnode) {
vnode.context && vnode.context.$nextTick(() => {
const input = el.querySelector('input,textarea');
if (input && !input.__vTrimBound) {
el.dispatchEvent(new Event('blur', { bubbles: true })); // 触发一次以绑定
}
});
}
});
- 全局捕获 blur(无需在模板写 v-trim,对异步节点同样有效)
- 在 main.js 添加一次性全局监听(放在创建 Vue 实例之前或之后都可):
js
document.addEventListener('blur', (e) => {
const t = e.target;
if (!t) return;
const isTextInput = (t.tagName === 'INPUT' || t.tagName === 'TEXTAREA');
if (!isTextInput || t.type === 'password') return;
const oldValue = t.value;
const newValue = typeof oldValue === 'string' ? oldValue.trim() : oldValue;
if (newValue !== oldValue) {
t.value = newValue;
t.dispatchEvent(new Event('input', { bubbles: true }));
}
}, true);
-
优点:覆盖所有动态/第三方输入,省去模板标记。
-
注意:如有不希望被裁剪的特例,需要在逻辑中额外过滤(比如通过 data-no-trim 属性跳过)。
建议
-
快速落地:选方案 2 全局监听,一行代码解决全面生效问题。
-
精细控制:选方案 1,在需要的输入上显式使用 v-trim,并保留增强后的指令实现。