Input组件封装的v-model问题

一、关于组件的封装:

v-bind="$attrs"与v-on="$listener"是组件封装或二次封装过程中经常使用的指令,前者v-bind用于监听父组件中不被认为prop的属性传入到子组件中,后者v-on用于指定父组件的事件(非子组件emit的事件)绑定在子组件中。举个最简单的例子,例如我们封装一个swiper组件:

<template>
    <div class="container">
        <swiper
          v-bind="$attrs"
          v-on="$listener"
        >
            <slot></slot>
        <swiper>
    </div>
</template>

那么我们在父组件中使用<Myswiper></Myswiper>组件时就可以在Myswiper组件上直接绑定swiper具有的一切属性和事件

二、关于v-model的本质

以上方式对于大多数组件封装可能都是没有问题的,但对于Input组件可能就略显不同了,因为对vue大多数开发者绑定Input的值可能更倾向于v-model而不是value本身。

v-model本质是 :value 与 @input的缩写(以下两行代码是等价的)

<input v-model="data"/>
<input :value="data" @input="data=e.detail.value"/>

三、Input组件二次封装使用v-model带来的问题

直接上封装的代码:

<!--子组件-->
<template>
	<view class="container">
		<slot name="prefix"></slot>
		<input
		 v-bind="$attrs"
		 v-on="$listeners"
         :value="v"
         @input="this.$emit('input',e.detail.value)"
		 />
		<slot name="suffix"></slot>
	</view>
</template>

<!--父组件-->
<Myinput
 v-model="data" 
 placeholder="请输入搜索内容"
 @focus="handleFocus"
></Myinput>

以上封装是为了对input组件做一个前后的icon图标,乍一看好像没什么问题,但实际运行起来在不同平台会有不同的意想不到的表现(有兴趣的小伙伴可以试试,这里不做统一总结)。总之你会发现,怎么v-model不生效了?于是乎,就会有网上千篇一律复制粘贴的解决方案,Myinput中用:value和@input代替v-model或者Myinput中监听子组件传来的值以修改data的值等等。

那么请问,为什么elementui中封装的el-input组件可以直接使用v-model呢??????

四、解决:

我们来看一下el-input组件源码

      <input
        :tabindex="tabindex"
        v-if="type !== 'textarea'"
        class="el-input__inner"
        v-bind="$attrs"
        :type="showPassword ? (passwordVisible ? 'text': 'password') : type"
        :disabled="inputDisabled"
        :readonly="readonly"
        :autocomplete="autoComplete || autocomplete"
        ref="input"
        @compositionstart="handleCompositionStart"
        @compositionupdate="handleCompositionUpdate"
        @compositionend="handleCompositionEnd"
        @input="handleInput"
        @focus="handleFocus"
        @blur="handleBlur"
        @change="handleChange"
        :aria-label="label"
      >



      handleInput(event) {
        /*...some code in here*/

        this.$emit('input', event.target.value);

        /*...some code in here*/
      },
      handleChange(event) {
        this.$emit('change', event.target.value);
      },

仔细观察的小伙伴会发现,el-input组件的封装并未使用v-on="$listener",反之是它干脆把常用的input事件都给手动监听一下然后暴露给外部组件使用。为什么?

因为如果在子组件使用v-bind和v-on监听,并在外部组件中使用v-model绑定,因为代码执行器在执行v-model时相当于执行了:value和@input,你可以手动把v-model换成:value和@input,如下

<!--子组件-->
<template>
	<view class="container">
		<slot name="prefix"></slot>
		<input
		 v-bind="$attrs"
		 v-on="$listeners"
         :value="v"
         @input="this.$emit('input',e.detail.value)"
		 />
		<slot name="suffix"></slot>
	</view>
</template>

<!--父组件-->
<Myinput
 :value="data" 
 placeholder="请输入搜索内容"
 @input="consInput"
></Myinput>

consInput (e) {
    console.log('run',e);
    this.data = e;
}

你会发现,在输入框输入一次,consInput事件会执行两次。其中原因是会先执行子组件的@input,再执行父组件的@input,子组件的input执行原因是父组件通过子组件的v-on="$listener"绑定上去的,父组件的执行原因是子组件emit过来的input事件执行。

总结:对于input这类需要绑定值的组件封装,如果想在外层组件使用v-model,那就不建议封装时使用v-on="$listener",如果你确实需要在外层组件使用input的一些事件,那就emit手动暴露事件,具体可参见el-input的做法

引用\[1\]:当在自定义组件上使用 v-model 时,编译器会将 v-model 展开为如下的形式: <CustomInput :modelValue="searchText" @update:modelValue="newValue => searchText = newValue" /> 。 引用\[2\]:注:这个 event 事件名只能是 input model:{ prop: 'value', // 此处的value为父组件v-model绑定数据 event: 'input' // 派发事件,事件名只能是input,否则无法生效 }, methods:{ timeChange(e) { if(this.format) { // 更新 model this.$emit('input', uni.$u.timeFormat(e.value,this.format)); } else { // 更新 model this.$emit('input', e.value); } }, } 。 根据您提供的引用内容,uview的input组件使用v-model不生效的问题可能是由于以下原因导致的: 1. 您在自定义组件上使用了v-model,但是没有正确处理modelValue和update:modelValue事件。在自定义组件中,您需要将v-model展开为一个modelValue属性和一个update:modelValue事件,以便正确绑定和更新数据。\[1\] 2. 您可能没有正确设置input组件model属性和event属性。在uview的input组件中,您需要将model属性设置为'value',将event属性设置为'input',以便正确绑定和更新数据。\[2\] 3. 您可能在使用u-input组件时出现了其他问题,比如条件渲染(v-if)导致组件不正确地显示或隐藏。请确保您的条件渲染逻辑正确,并且组件能够正确地显示和绑定数据。\[3\] 综上所述,如果uview的input组件使用v-model不生效,您可以检查以上几个方面的问题,并确保正确设置和处理modelValue属性和update:modelValue事件,以及正确设置model和event属性。 #### 引用[.reference_title] - *1* *2* [组件二次封装之自定义 v-model](https://blog.csdn.net/lwx931449660/article/details/122354581)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [uview,u-input组件在明密文切换时app不生效](https://blog.csdn.net/weixin_44202904/article/details/130620984)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值