uni-app vue3 微信小程序中 v-if 元素上的点击事件触发错误

发布于 1周前 作者 itying888 来自 Uni-App

uni-app vue3 微信小程序中 v-if 元素上的点击事件触发错误

开发环境 版本号 项目创建方式
Mac macOS 15.0 (24A335) HBuilderX

产品分类:uniapp/小程序/微信

PC开发环境操作系统:Mac

PC开发环境操作系统版本号:macOS 15.0 (24A335)

HBuilderX类型:正式

HBuilderX版本号:4.28

第三方开发者工具版本号:RC 1.06.2409131

基础库版本号:3.6.0

项目创建方式:HBuilderX


示例代码:

<template>  
    <view class="main">  
        <view class="mobile layout-flex-acjs">  
            <input class="input" type="number" placeholder="请输入乘车人手机号" placeholder-style="color: #b7becc" maxlength="11"  
                :focus="mobileFocus" v-model="mobile" @focus="mobileFocus = true" @blur="mobileFocus = false"  
                v-if="mobileFocus || mobile" />
            <text v-else class="input" @tap="tapMobile"> 请输入乘车人手机号 </text>  
            <view class="image" @tap="clearMobile" v-if="mobileFocus && mobile">  
                <image src="https://tx-t3propublic.t3go.cn/t3-admin/1446319048521506850" />
            </view>  
        </view>  

        <view class="record">  
            <view class="record-h layout-flex-acjs" @tap="toPassengerHistory">  
                <text>历史乘车人</text>  
                <view class="image">  
                    <image src="https://tx-t3propublic.t3go.cn/t3-admin/1446312561728389153" />
                </view>  
            </view>  
        </view>  
    </view>  
</template>  

<script>  
    export default {  
        data() {  
            return {  
                mobile: '',  

                mobileFocus: false, // 号码输入框聚焦  
            };  
        },  
        methods: {  
            tapMobile() {  
                this.mobileFocus = true;  
            },  
            clearMobile(e) {  
                console.log('xxx-clearMobile', e);  
                this.mobile = '';  
            },  
            toPassengerHistory(e) {  
                console.log('xxx-toPassengerHistory', e);  
            },  
        },  
    };  
</script>  

<style lang="less" scoped>  
    .layout-flex {  
        display: flex;  

        &-ac {  
            display: flex;  
            align-items: center;  
        }  

        &-acjc {  
            display: flex;  
            align-items: center;  
            justify-content: center;  
        }  

        &-acjs {  
            display: flex;  
            align-items: center;  
            justify-content: space-between;  
        }  
    }  

    .main {  
        padding-top: 48rpx;  
        margin: 0 auto;  
        width: 702rpx;  
        min-height: 208rpx;  
        background: #ffffff;  
        box-shadow: 0px 16rpx 24rpx 0px rgba(49, 56, 84, 0.08);  
        border-radius: 24rpx;  
        box-sizing: border-box;  

        .mobile {  
            padding: 26rpx 4rpx 26rpx 24rpx;  
            margin: 0 auto;  
            width: 654rpx;  
            height: 112rpx;  
            background: #f7f8fc;  
            // box-shadow: 0px 2rpx 6rpx 0px rgba(49, 56, 84, 0.03);  
            border-radius: 16rpx;  
            box-sizing: border-box;  

            .input {  
                flex: 1;  
                height: 60rpx;  
                font-size: 44rpx;  
                font-family: PingFangSC-Medium, PingFang SC;  
                font-weight: 500;  
                color: #02102a;  
                line-height: 60rpx;  
                caret-color: #ff8533;  
            }  

            .image {  
                padding: 20rpx;  
                width: 44rpx;  
                height: 44rpx;  

                image {  
                    width: 44rpx;  
                    height: 44rpx;  
                }  
            }  
        }  

        .record {  
            position: relative;  
            padding: 32rpx 0 40rpx;  

            &-h {  
                padding: 0 48rpx;  

                text {  
                    font-size: 24rpx;  
                    font-family: PingFangSC-Regular, PingFang SC;  
                    font-weight: 400;  
                    color: #747881;  
                    line-height: 34rpx;  
                }  

                .image {  
                    image {  
                        width: 24rpx;  
                        height: 24rpx;  
                    }  
                }  
            }  

            &-l {  
                padding: 0 16rpx;  
                box-sizing: border-box;  
                margin-top: 24rpx;  
                height: 56rpx;  
                white-space: nowrap;  
            }  

            &-i {  
                display: inline-block;  
                margin-right: 16rpx;  
                padding: 12rpx 24rpx 10rpx;  
                height: 56rpx;  
                background: rgba(255, 231, 214, 0.3);  
                border-radius: 8rpx;  
                border: 2rpx solid rgba(255, 133, 51, 0.5);  
                box-sizing: border-box;  
                font-size: 24rpx;  
                font-family: PingFangSC-Regular, PingFang SC;  
                font-weight: 400;  
                color: #996220;  
                line-height: 34rpx;  

                &:first-child {  
                    margin-left: 32rpx;  
                }  

                &:last-child {  
                    margin-right: 32rpx;  
                }  
            }  

            .mask {  
                position: absolute;  
                top: 86rpx;  
                width: 72rpx;  
                height: 64rpx;  

                &-1 {  
                    left: 0;  
                    background: linear-gradient(90deg, #ffffff 0%, rgba(255, 255, 255, 0) 100%);  
                }  

                &-2 {  
                    right: 0;  
                    background: linear-gradient(90deg, rgba(255, 255, 255, 0) 0%, #ffffff 100%);  
                }  
            }  
        }  
    }  
</style>

15 回复

收到, 我看下


已加分,感谢反馈,已确认bug

下个版本修复,目前可以用 v-show 替换 v-if 来临时解决这个问题

方便告知影响范围吗?或者触发机制是什么?因为v-if经常使用的,但不是每个场景下都触发这个bug

在你的工程,你这边同时绑定了blur 事件和click事件。点击的时候,先后触发blur 事件 和 click事件,blur先触发。 blur触发时更新了事件绑定逻辑,把click绑定的事件替换了导致的bug(下个版本修复)。

一般来讲小程序端,这个bug触发条件也比较苛刻,需要同时连续触发两个事件且触发的事件影响v-if的标签,且v-if上面也绑定对应的事件。 所以影响范围较小

回复 BFC: 我不太理解为什么click绑定的事件会被替换。 我发现uni-app在vue3版本里好像把事件当成变量去渲染了,导致一个事件在AppData、WXML里会变化更新。我感觉并不是同时绑定多个事件的问题。 私信讲的那个例子是子组件向父组件 emit 事件,在老版本正常,但是新版本异常。

还没修复吗?

还没修复吗?

如果发版后仍有问题,请艾特我来跟进。github 上 issues 先关闭了,有进展会在这里进行更新

还没修复吗?

还没修复吗?

uni-app 结合 Vue 3 开发微信小程序时,如果遇到 v-if 条件渲染的元素上点击事件触发错误的问题,通常可能是因为 v-if 控制的元素在 DOM 中被销毁和重建导致的。这种情况在条件频繁切换时尤为常见。为了确保事件能够正确绑定,可以考虑以下几种方法:

1. 使用 v-show 替代 v-if

如果元素的显示与隐藏不需要真正地从 DOM 中移除,可以使用 v-show 指令。v-show 只是切换元素的 CSS display 属性,不会销毁和重建元素。

<template>
  <view v-show="isVisible" @click="handleClick">
    点击我
  </view>
</template>

<script>
export default {
  data() {
    return {
      isVisible: true
    };
  },
  methods: {
    handleClick() {
      console.log('元素被点击');
    }
  }
};
</script>

2. 确保事件绑定在稳定的父元素上

如果必须使用 v-if,可以尝试将事件绑定在一个稳定的父元素上,然后通过事件委托来处理点击事件。

<template>
  <view @click="handleParentClick">
    <view v-if="isVisible" class="clickable-child">
      点击我(通过父元素事件委托)
    </view>
  </view>
</template>

<script>
export default {
  data() {
    return {
      isVisible: true
    };
  },
  methods: {
    handleParentClick(event) {
      const target = event.target;
      if (target.classList.contains('clickable-child')) {
        console.log('子元素被点击(通过父元素)');
      }
    }
  }
};
</script>

<style>
.clickable-child {
  /* 确保子元素可点击样式 */
  cursor: pointer;
}
</style>

3. 使用 nextTick 确保 DOM 更新完成

如果条件切换后需要确保 DOM 更新完成再绑定事件,可以使用 VuenextTick 方法。

<template>
  <view v-if="isVisible" ref="clickableElement">
    点击我
  </view>
  <button @click="toggleVisibility">切换可见性</button>
</template>

<script>
import { ref, nextTick } from 'vue';

export default {
  setup() {
    const isVisible = ref(true);
    const clickableElement = ref(null);

    const toggleVisibility = () => {
      isVisible.value = !isVisible.value;
      nextTick(() => {
        if (clickableElement.value) {
          clickableElement.value.addEventListener('click', () => {
            console.log('元素被点击(nextTick后绑定)');
          });
        }
      });
    };

    return {
      isVisible,
      clickableElement,
      toggleVisibility
    };
  }
};
</script>

这些方法可以帮助你解决 v-if 元素上点击事件触发错误的问题。根据具体场景选择合适的方法。

回到顶部