当我滚动uni-app scroll-view时并开启scroll监听后,滚动到底部新增数据后会触发click函数
当我滚动uni-app scroll-view时并开启scroll监听后,滚动到底部新增数据后会触发click函数
示例代码:
<template>
<scroll-view scroll-y class="session-list-container" @scroll="onScroll" @scrolltoupper="onScrolltoupper"
@scrolltolower="getList" v-if="list.length > 0" :throttle="false">
<view class="session-list-item" v-for="(item) in list" [@click](/user/click)="handleClickSession(item)">
{{ item }}
</view>
</scroll-view>
</template>
<script setup>
import { ref } from 'vue';
const list = ref([]);
const loading = ref(false);
const has_more = ref(null);
const queryForm = ref({ page: 1, page_size: 10, is_active: false });
const getList = () => {
if (has_more.value === false || loading.value) {
return;
}
loading.value = true;
getData().then((res) => {
if (has_more.value === null) {
list.value = res;
} else {
list.value = [...list.value, ...res];
}
has_more.value = true;
loading.value = false;
}).catch(() => {
loading.value = false;
});
};
const getData = () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve([{ id: 1 }, { id: 1 }, { id: 1 }, { id: 1 }, { id: 1 }, { id: 1 }, { id: 1 }, { id: 1 }, { id: 1 }, { id: 1 }, { id: 1 }, { id: 1 }, { id: 1 }, { id: 1 }, { id: 1 }, { id: 1 }, { id: 1 }, { id: 1 }, { id: 1 }, { id: 1 }, { id: 1 }]);
}, 100);
});
};
const onScroll = (event) => {};
const onScrolltoupper = () => {};
const handleClickSession = (item) => {
console.log('handleClickSession');
uni.showToast({
title: 'handleClickSession被触发',
icon: 'none',
});
// uni.navigateTo({
// url: '/pages/session/info?id=' + item.session_id,
// });
};
getList();
</script>
<style scoped lang="scss">
.session-list-container {
// padding: 40rpx;
// overflow: auto;
height: 100vh;
width: 100vw;
}
.session-list-item {
border-radius: 28rpx;
background: rgba(255, 255, 255, 0.1);
padding: 30rpx 0;
width: 100%;
position: relative;
.session-list-item-bar-name {
position: absolute;
right: 14rpx;
top: 30rpx;
font-size: 26rpx;
font-weight: 500;
color: rgba(255, 255, 255, 0.5);
width: 300rpx;
text-align: right;
}
.session-list-item-time {
font-size: 26rpx;
font-weight: 500;
color: rgba(255, 255, 255, 0.5);
padding: 0 36rpx;
}
.session-list-item-user {
display: flex;
align-items: center;
margin-top: 28rpx;
>view {
flex: 1;
text-align: center;
font-size: 20rpx;
font-weight: 500;
color: #fff;
&:first-child {
border-right: 1px solid #FFFFFF4C
}
>view:first-child {
font-size: 48rpx;
font-weight: 500;
color: rgba(131, 242, 230, 1);
}
}
}
&:not(:last-child) {
margin-bottom: 20rpx;
}
}
</style>
操作步骤:
滑动滚动到底部,就会触发@click里面的函数
预期结果:
应该不会触发click里面的函数,这个函数应该是点击以后才会触发的
实际结果:
滑动滚动到底部,就会触发@click里面的函数
bug描述:
当我滚动scroll-view时并开启scroll监听后,滑动滚动到底部新增数据后触发click函数,按正常应该是不会触发的我没点击具体view
附件
| 项目信息 | 版本号 |
|---|---|
| PC开发环境操作系统 | Mac |
| PC开发环境操作系统版本号 | 15.3.1 (24D70) |
| 第三方开发者工具版本号 | 4.85 (ARM) |
| 基础库版本号 | 3.11.0 |
| 项目创建方式 | CLI |
| CLI版本号 | 3.0.0-4080420251103001 |
更多关于当我滚动uni-app scroll-view时并开启scroll监听后,滚动到底部新增数据后会触发click函数的实战教程也可以访问 https://www.itying.com/category-93-b0.html
替换 node_modules/@dcloudio/uni-mp-compiler/dist/transforms/vOn.js 为下面的内容试试能不能解决问题
“use strict”;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { “default”: mod };
};
Object.defineProperty(exports, “__esModule”, { value: true });
exports.wrapperVOn = exports.transformOn = void 0;
const uni_cli_shared_1 = require("@dcloudio/uni-cli-shared");
const compiler_core_1 = require("@vue/compiler-core");
const shared_1 = require("@vue/shared");
const crypto_1 = __importDefault(require(“crypto”));
const __1 = require("…");
const runtimeHelpers_1 = require("…/runtimeHelpers");
const transformExpression_1 = require("./transformExpression");
const vFor_1 = require("./vFor");
const utils_1 = require("./utils");
const fnExpRE = /^\s*([\w$_]+|(async\s*)?([^)]?))\s=>|^\s*(async\s+)?function(?:\s+[\w$]+)?\s*(/;
const transformOn = (dir, node, _context, augmentor) => {
const context = _context;
const { loc, modifiers, arg } = dir;
if (!dir.exp && !modifiers.length) {
context.onError((0, compiler_core_1.createCompilerError)(compiler_core_1.ErrorCodes.X_V_ON_NO_EXPRESSION, loc));
}
let eventName;
if (arg.type === compiler_core_1.NodeTypes.SIMPLE_EXPRESSION) {
if (arg.isStatic) {
const rawName = arg.content;
// for all event listeners, auto convert it to camelCase. See issue #2249
eventName = (0, compiler_core_1.createSimpleExpression)((0, shared_1.toHandlerKey)((0, shared_1.camelize)(rawName)), true, arg.loc);
}
else {
// #2388
eventName = (0, compiler_core_1.createCompoundExpression)([
// ${context.helperString(TO_HANDLER_KEY)}(,
arg,
// ),
]);
}
}
else {
// already a compound expression.
eventName = arg;
eventName.children.unshift(${context.helperString(compiler_core_1.TO_HANDLER_KEY)}();
eventName.children.push());
}
// handler processing
let exp = dir.exp;
if (exp && !exp.content.trim()) {
exp = undefined;
}
let shouldCache = context.cacheHandlers && !exp && !context.inVOnce;
if (exp) {
const isMemberExp = (0, compiler_core_1.isMemberExpression)(exp.content, context);
const isInlineStatement = !(isMemberExp || fnExpRE.test(exp.content));
const hasMultipleStatements = exp.content.includes(;);
// process the expression since it’s been skipped
if (context.prefixIdentifiers) {
isInlineStatement && context.addIdentifiers($event);
exp = dir.exp = (0, transformExpression_1.processExpression)(exp, context, false, hasMultipleStatements);
isInlineStatement && context.removeIdentifiers($event);
// with scope analysis, the function is hoistable if it has no reference
// to scope variables.
shouldCache =
context.cacheHandlers &&
// unnecessary to cache inside v-once
!context.inVOnce &&
// runtime constants don’t need to be cached
// (this is analyzed by compileScript in SFC <script setup>)
!(exp.type === compiler_core_1.NodeTypes.SIMPLE_EXPRESSION && exp.constType > 0) &&
// #1541 bail if this is a member exp handler passed to a component -
// we need to use the original function to preserve arity,
// e.g. <transition> relies on checking cb.length to determine
// transition end handling. Inline function is ok since its arity
// is preserved even when cached.
!(isMemberExp && node.tagType === compiler_core_1.ElementTypes.COMPONENT) &&
// bail if the function references closure variables (v-for, v-slot)
// it must be passed fresh to avoid stale values.
!(0, compiler_core_1.hasScopeRef)(exp, context.identifiers) &&
// wxs event
!(0, utils_1.isFilterExpr)(exp, context);
// If the expression is optimizable and is a member expression pointing
// to a function, turn it into invocation (and wrap in an arrow function
// below) so that it always accesses the latest value when called - thus
// avoiding the need to be patched.
if (shouldCache && isMemberExp) {
if (exp.type === compiler_core_1.NodeTypes.SIMPLE_EXPRESSION) {
exp.content = ${exp.content} && ${exp.content}(...args);
}
else {
exp.children = […exp.children, &&, …exp.children, (...args)];
}
}
}
if (isInlineStatement || (shouldCache && isMemberExp)) {
// wrap inline statement in a function expression
exp = (0, compiler_core_1.createCompoundExpression)([
${isInlineStatement ? context.isTS ?($event: any):$event:${context.isTS ? \n//@ts-ignore\n : ``}(…args)} => ${hasMultipleStatements ?{:(},
exp,
hasMultipleStatements ? } : ),
]);
}
}
let ret = {
props: [
(0, compiler_core_1.createObjectProperty)(eventName, exp || (0, compiler_core_1.createSimpleExpression)(() => {}, false, loc)),
],
};
// apply extended compiler augmentor
if (augmentor) {
ret = augmentor(ret);
}
// TODO
if (shouldCache) {
// cache handlers so that it’s always the same handler being passed down.
// this avoids unnecessary re-renders when users use inline handlers on
// components.
// ret.props[0].value = wrapper(
// context.cache(ret.props[0].value) as ExpressionNode,
// context
// )
ret.props[0].value = wrapperVOn(ret.props[0].value, node, context);
}
else {
ret.props[0].value = wrapperVOn(ret.props[0].value, node, context);
}
// mark the key as handler for props normalization check
ret.props.forEach(§ => (p.key.isHandlerKey = true));
return ret;
};
exports.transformOn = transformOn;
function wrapperVOn(value, node, context) {
if ((0, transformExpression_1.isBuiltInIdentifier)(value)) {
return value;
}
// wxs event
if ((0, utils_1.isFilterExpr)(value, context)) {
return value;
}
const keys = [];
if (context.miniProgram.event?.key && context.inVFor) {
let keyProp = (0, compiler_core_1.findProp)(node, ‘key’);
if (!keyProp) {
const vForScope = (0, vFor_1.parseVForScope)(context.currentScope);
if (vForScope) {
keyProp = (0, compiler_core_1.findProp)(vForScope.node, ‘key’);
}
}
// 对 for 中的所有事件增加 key 标记,避免微信小程序不更新事件对象
if (keyProp && (0, uni_cli_shared_1.isDirectiveNode)(keyProp) && keyProp.exp) {
const keyCode = (0, __1.genExpr)(keyProp.exp);
if (keyCode) {
keys.push(’,’);
keys.push((0, __1.genExpr)(keyProp.exp));
}
}
}
// 给事件添加一标识,避免小程序事件覆盖问题 (question/198957)
const eventHashId = crypto_1.default
.createHash(‘sha256’)
.update(JSON.stringify(value), ‘utf8’)
.digest(‘hex’)
.slice(0, 2);
if (keys.length === 0) {
keys.push(, "${eventHashId}");
}
const newExpr = (0, compiler_core_1.createCompoundExpression)([
${context.helperString(runtimeHelpers_1.V_ON)}(,
value,
…keys,
),
]);
// 保存原始事件表达式
// @ts-expect-error 内部 parseVOn 时使用
newExpr.__withoutKeysVOnExpr = (0, compiler_core_1.createCompoundExpression)([
${context.helperString(runtimeHelpers_1.V_ON)}(,
value,
),
]);
return newExpr;
}
exports.wrapperVOn = wrapperVOn;
更多关于当我滚动uni-app scroll-view时并开启scroll监听后,滚动到底部新增数据后会触发click函数的实战教程也可以访问 https://www.itying.com/category-93-b0.html
可以了谢谢大佬,感动哭了!!!!!
回复 9***@qq.com: 记得打个patches,要不然你重新install了代码就丢了
回复 DCloud_UNI_JBB: 好的!谢谢!太感谢了!
用 v-show 替换 v-if 试试能不能解决问题
不行,不用v-if和v-show都不行
回复 9***@qq.com: 你清缓存了吗?
回复 DCloud_UNI_JBB: 清了,直接整个文件删了重新编译的
回复 DCloud_UNI_JBB: 我只要开启scroll监听,并往List里面增加数据就会出现这个问题,我不增加数据没有这个情况,或者我不监听没有这个情况,两个同时出现就会有这个情况
已知问题,后续会修复,关联帖子 https://ask.dcloud.net.cn/question/198957
那我现在是需要怎么来解决这个问题
我那个代码把v-if删掉以后也会出现一样的问题
回复 9***@qq.com: 试一下上面贴的代码能不能解决你的问题
这个问题是由于在滚动到底部触发数据加载时,新增的列表项可能与点击事件产生了冲突。在uni-app中,scroll-view滚动到底部触发数据加载时,可能会误触发click事件。
解决方案:
- 在scroll-view上添加
:scroll-with-animation="false"属性,禁用滚动动画 - 在scroll-view上添加
[@touchstart](/user/touchstart)="onTouchStart"和[@touchend](/user/touchend)="onTouchEnd"来区分滚动和点击 - 使用touch事件替代click事件
推荐修改方案:
<template>
<scroll-view scroll-y class="session-list-container"
[@scroll](/user/scroll)="onScroll"
[@scrolltoupper](/user/scrolltoupper)="onScrolltoupper"
[@scrolltolower](/user/scrolltolower)="getList"
v-if="list.length > 0"
:throttle="false"
:scroll-with-animation="false"
[@touchstart](/user/touchstart)="onTouchStart"
[@touchend](/user/touchend)="onTouchEnd">
<view class="session-list-item" v-for="(item) in list" [@click](/user/click)="handleClickSession(item)">
{{ item }}
</view>
</scroll-view>
</template>
<script setup>
import { ref } from 'vue';
const isScrolling = ref(false);
let touchTimer = null;
const onTouchStart = () => {
isScrolling.value = false;
};
const onTouchEnd = () => {
// 短时间内结束触摸认为是点击,长时间是滚动
clearTimeout(touchTimer);
touchTimer = setTimeout(() => {
isScrolling.value = true;
}, 100);
};
const handleClickSession = (item) => {
if (isScrolling.value) return;
console.log('handleClickSession');
uni.showToast({
title: 'handleClickSession被触发',
icon: 'none',
});
};
</script>

