@[toc]

需求: 仿微信聊天

需求很简单, 就是仿微信聊天, 下拉加载历史聊天记录时, 能保持浏览位置, 在网上找了一些例子都不太完美, 要么不能保持浏览位置, 要么会来回跳一下, 反正就是效果不好, 决定还是自己研究研究

处理方法

template 模板部分

使用 uniapp 自带的scroll-view来做滚动容器, 需要配合以下属性:

  1. scroll-y: 允许纵向滚动
  2. scroll-top: 设置竖向滚动条位置
  3. upper-threshold: 距顶部/左边多远时(单位px),触发 scrolltoupper 事件
  4. @scrolltoupper: 滚动到顶部/左边,会触发 scrolltoupper 事件
  5. scroll-anchoring: 开启 scroll anchoring 特性,即控制滚动位置不随内容变化而抖动,仅在 iOS 下生效
<scroll-view
    class="msg-list"
    scroll-y="true"
    :scroll-top="scrollTop"
    @scrolltoupper="getMessageList"
    upper-threshold="0"
    :scroll-anchoring="true"
>
    <view class="content-box-loading">
	<!-- 加载到最后页了 -->
        <text v-if="isCompleted">--- 没有更多了 ---</text>
	<!-- 加载中 -->
        <u-loading v-if="loading" mode="flower"></u-loading>
	<!-- 还可以写些其他状态 -->
    </view>
    <view class="message-list">
        <view class="message-item" v-for="(item, index) in messageList" :key="index">{{ item }}</view>
    </view>
</scroll-view>

script 部分

详细见代码中的注释

export default {
    data() {
        return {
            loading: false,
	    isCompleted: false,	
            // 列表高度
            scrollHeight: 0,
            // 滚动条位置
            scrollTop: 0,
            // 消息列表
            messageList: [],
            page: 1
        }
    },
    computed: {},
    onLoad() {
	this.getMessageList()
    },
    methods: {
        async getMessageList() {
            // 加载中, 或者数据已经加载到最后一页了, 直接 return
            if (this.loading || this.isCompleted) {
                return
            }
            // 将loading设置成true, 防止在重复加载
            this.loading = true
            try {
                const data = await this.$api.getMessageList({
                    // 其他各种入参
                    count: 15,
                    page: this.page
                })
                // 接口返回的数据, 根据实际情况修改
                // 聊天记录列表
                const messageList = data.data.messageList
                // 是否已加载到最后一页
                this.isCompleted = data.data.isCompleted
                // 合并数据
                this.messageList = [...messageList, ...this.messageList]
                // 当新数据渲染完成后
                this.$nextTick(async () => {
                    const query = uni.createSelectorQuery().in(this)
                    query
                        .select('.message-list')
                        .boundingClientRect(data => {
                            // data.height 为已经渲染的聊天列表内容高度
                            // this.scrollHeight 为上一次聊天列表内容高度, 如果当前为第一次, 那么this.scrollHeight应该为0
                            // 设置滚动条的高度
                            this.scrollTop = data.height - this.scrollHeight
                            // (注意: 如果在模板中, upper-threshold设置的值不为0, 为50, 那么可以加上该值), 如:
                            // this.scrollTop = data.height - this.scrollHeight + 50
                            // 将本次列表渲染后的内容高度记录下来, 方便下次加载时使用
                            this.scrollHeight = data.height
                        })
                        .exec()
                    //如果还有数据
                    if (!data.data.isCompleted) {
                        this.page += 1
                        this.loading = false
                    }
                })
            } catch (error) {
                console.log(error)
                this.loading = false
            }
        }
    }
}

其他

该方法只在H5中用浏览器模拟手机测试通过, 其他平台未测试

效果

Kapture 20201218 at 10.11.15.gif