uni-app 【报Bug】v-for [數據] 在 [數據] 被短時間編輯後不工作, 或v-for 的[數據]watcher 失效

uni-app 【报Bug】v-for [數據] 在 [數據] 被短時間編輯後不工作, 或v-for 的[數據]watcher 失效

开发环境 版本号 项目创建方式
Windows win10 HBuilderX

产品分类:uniapp/App

PC开发环境操作系统:Windows

HBuilderX类型:正式

HBuilderX版本号:3.1.13

手机系统:Android

手机系统版本号:Android 7.0

手机厂商:電視盒

手机机型:電視盒

页面类型:vue

打包方式:云端

项目创建方式:HBuilderX

示例代码:

<template>  
    <view class="content">  
        <button >null</button>  
        <button @click="initData">initData + downloadFile</button>  
        <text>第一次打开app要先下载3段影片,\n可自行更换网络较好的资源,\n看见console.log有3个[save sucess ...]代表下载成功</text>  
        <button @click="setStor">setStor</button>  
        <text>3断影片都下载好后,手动把数据写入Storage</text>  
        <button @click="printFileDict">printFileDict</button>  
        <text>检查是否3断影片都下载好,并保存好</text>  
        <text>以及打印出convertData及bug-convertData2处理后的handledPlayData,其实一样</text>  
        <button @click="convertData">convertData</button>  
        <text>convertData是正常的动作,作用是把原数据转化成播放的数据</text>  
        <button @click="convertData2">bug-convertData2</button>  
        <text>convertData2是出现bug的动作,其实跟1动作一样,\n但就在整理数据时,直接在this.handledPlayData上插入数据,\n1就是先用tmpDict先把数据缓取,待制作好后,\n再用this.handledPlayData = tmpDict更换数据</text>  
        <button @click="cleanData">cleanData</button>  
        <text>convertData转成convertData2时用</text>  
        <view>  
            <view v-for="(value,FrameKey,index) in handledPlayData">  
        <!--        <text>  
                    {{value.win[0]}} , {{value.win[1]}} , {{value.win[2]}} , {{value.win[3]}}    
                    FrameKey:{{FrameKey}} playIndex:{{value.playIndex}} index:{{index}}  
                </text> -->  
                <!-- 以上用于把影片排列可视化 -->  
                <video :src="getMyMedia(FrameKey)" class='myVideo' autoplay controls=false loop show-loading=false  
                    @ended="playEnd(FrameKey)" :style='  
            "top:"+value.win[0]+"rpx;"+  
            "left:"+value.win[1]+"rpx;"+  
            "width:"+value.win[2]+"rpx;"+  
            "height:"+value.win[3]+"rpx;"'&gt;</video>  
            </view>  
        </view>  
    </view>  
</template>  

<script>  
    export default {  
        data() {  
            return {  
                fileDict: {},  
                oriPlayData: {},  
                handledPlayData: {}  
            }  
        },  
        onLoad() {  
            this.getStor()  
        },  
        watch: {},  
        methods: {  
            cleanData(){  
                this.handledPlayData = {}  
            },  
            initData() {  
                this.setOriData()  //初始化原数据(播放的config)  
                let that = this  
                let i  
                let sampleVideo = [  
                    "https://assets.mixkit.co/videos/preview/mixkit-huge-trees-in-a-large-green-forest-5040-large.mp4",  //可以更改为网络下载较快的资源,但setOriData()的Content也要作相对更改  
                    "https://assets.mixkit.co/videos/preview/mixkit-forest-stream-in-the-sunlight-529-large.mp4",  
                    "https://assets.mixkit.co/videos/preview/mixkit-stars-in-space-1610-large.mp4"  
                ]  
                for (i in sampleVideo) {  
                    // console.log(sampleVideo[i])  
                    uni.downloadFile({  
                        url: sampleVideo[i],  
                        success: (res) => {  
                            console.log('dl sucess')  
                            let tempFilePath = res.tempFilePath;  
                            console.log(res.tempFilePath)  
                            uni.saveFile({  
                                tempFilePath: tempFilePath,  
                                success: (saveRes) => {  
                                    let savedFilePath = saveRes.savedFilePath  
                                    console.log('save sucess', savedFilePath)  
                                    that.fileDict[res.tempFilePath.split('/')[3]] = savedFilePath  
                                }  
                            })  
                        }  
                    })  
                }  
            },  
            setOriData() { //初始化原数据(播放的config)  
                let oriData = {  
                    "L": "1920",  
                    "W": "1080",  
                    "horizontal": true,  
                    "Frame1": [0, 0, 800, 800],  
                    "Content1": ["mixkit-stars-in-space-1610-large.mp4"],  
                    "Frame2": [0, 800, 800, 800],  
                    "Content2": ["mixkit-stars-in-space-1610-large.mp4", "mixkit-huge-trees-in-a-large-green-forest-5040-large.mp4"],  
                    "Frame3": [800, 0, 800, 800],  
                    "Content3": ["mixkit-stars-in-space-1610-large.mp4", "mixkit-forest-stream-in-the-sunlight-529-large.mp4", "mixkit-huge-trees-in-a-large-green-forest-5040-large.mp4"],  
                    "Frame4": [800, 800, 800, 800],  
                    "Content4": ["mixkit-huge-trees-in-a-large-green-forest-5040-large.mp4"]  
                }  
                uni.setStorageSync('oriPlayData', oriData)  
            },  

            setStor() {  
                uni.setStorageSync('File_UniSavedName', this.fileDict)  
                this.getStor()  
            },  

            getStor() {  
                this.fileDict = uni.getStorageSync('File_UniSavedName')  
                console.log('fileDict:', this.fileDict)  
                this.oriPlayData = uni.getStorageSync('oriPlayData')  
                console.log('oriPlayData:', this.oriPlayData)  
            },  

            printFileDict() {  
                console.log(this.fileDict)  
                console.log('handledPlayData:',this.handledPlayData)  
            },  

            getUniappSaveName(OriMediaName) {  
                return this.MedList[OriMediaName]  
            },  

            convertData() {  
                console.log(this.handledPlayData)  
                let that = this  
                let tmp  
                let tmpPlayingDict = {}  
                for (tmp in this.oriPlayData) {  
                    if (tmp.slice(0, 7) == "Content") {  
                        let tmpNum = tmp.split("Content")[1]  
                        let newDictFrame = 'Frame' + tmpNum  
                        tmpPlayingDict[newDictFrame] = {}  
                        tmpPlayingDict[newDictFrame]['win'] = this.oriPlayData[newDictFrame]  
                        tmpPlayingDict[newDictFrame]['Content'] = []  
                        let oriNameMed  
                        for (oriNameMed in this.oriPlayData['Content' + tmpNum]) {  
                            tmpPlayingDict[newDictFrame]['Content'].push(this.fileDict[this.oriPlayData['Content' + tmpNum][oriNameMed]])  
                        }  
                        tmpPlayingDict[newDictFrame]['playIndex'] = 0  
                    }  
                }  
                this.handledPlayData = tmpPlayingDict  
                console.log(this.handledPlayData)  
            },  

            convertData2() {  
                let that = this  
                let tmp  
                for (tmp in this.oriPlayData) {  
                    if (tmp.slice(0, 7) == "Content") {  
                        let tmpNum = tmp.split("Content")[1]  
                        let newDictFrame = 'Frame' + tmpNum  
                        console.log(newDictFrame)  
                        this.handledPlayData[newDictFrame] = {}  
                        this.handledPlayData[newDictFrame]['win'] = this.oriPlayData[newDictFrame]  
                        this.handledPlayData[newDictFrame]['Content'] = []  
                        let oriNameMed  
                        for (oriNameMed in this.oriPlayData['Content' + tmpNum]) {  
                            console.log(this.fileDict[this.oriPlayData['Content' + tmpNum][oriNameMed]])  
                            this.handledPlayData[newDictFrame]['Content'].push(this.fileDict[this.oriPlayData['Content' + tmpNum][oriNameMed]])  
                        }  
                        this.handledPlayData[newDictFrame]['playIndex'] = 0  
                    }  
                }  
                console.log(this.handledPlayData)  
            },  

            playEnd(FrameKey) {  
                this.handledPlayData[FrameKey].playIndex += 1  
                // console.log('ended', FrameKey, this.handledPlayData[FrameKey].playIndex)  
                this.handledPlayData[FrameKey].playIndex = (this.handledPlayData[FrameKey].playIndex) % (this.handledPlayData[FrameKey].Content.length)  
            },  

            getMyMedia(FrameKey) {  
                // console.log('get')  
                let med = this.handledPlayData[FrameKey].Content[this.handledPlayData[FrameKey].playIndex]  
                return med  
            }  
        }  
    }  
</script>  

<style>  
    .content {  
        display: flex;  
        flex-direction: column;  
        align-items: center;  
        justify-content: center;  
        text-align: right;  
    }  

    .myVideo {  
        position: absolute;  
    }  
</style>

操作步骤:

  • 直接按bug-convertData2按钮

预期结果:

  • v-for可用,
  • 影片可以正常播放

实际结果:

  • v-for不可用
  • 影片不播放

bug描述:

  • v-for [数据],
  • [数据]被编辑时,
  • 有可能会导致 v-for 不作用
  • 我猜应该是 v-for 的 watcher 在观察数据是否有变化时,
  • 因为短时间内[数据]被频繁编辑,而出现的bug
  • 由于很难解释,就把代码都贴上了
  • [initData + downloadFile]等3断影片下载完成后再按[setStor]
  • 之后就可以按[convertData2]不出现预期结果

更多关于uni-app 【报Bug】v-for [數據] 在 [數據] 被短時間編輯後不工作, 或v-for 的[數據]watcher 失效的实战教程也可以访问 https://www.itying.com/category-93-b0.html

1 回复

更多关于uni-app 【报Bug】v-for [數據] 在 [數據] 被短時間編輯後不工作, 或v-for 的[數據]watcher 失效的实战教程也可以访问 https://www.itying.com/category-93-b0.html


这个问题是由于Vue响应式系统的限制导致的。在convertData2方法中,你直接对this.handledPlayData进行动态属性赋值:

this.handledPlayData[newDictFrame] = {}

Vue无法检测到对象属性的添加或删除,因此这种赋值方式不会触发视图更新。虽然数据确实被修改了,但v-for的watcher没有收到变化通知,导致界面不重新渲染。

相比之下,convertData方法工作正常是因为它先构建完整的tmpPlayingDict对象,然后一次性赋值给this.handledPlayData

this.handledPlayData = tmpPlayingDict

这种整体替换能够被Vue检测到,从而触发视图更新。

解决方案: 使用$set方法确保属性添加是响应式的:

convertData2() {
    let that = this
    let tmp
    for (tmp in this.oriPlayData) {
        if (tmp.slice(0, 7) == "Content") {
            let tmpNum = tmp.split("Content")[1]
            let newDictFrame = 'Frame' + tmpNum
            console.log(newDictFrame)
            
            // 使用$set确保响应式
            this.$set(this.handledPlayData, newDictFrame, {
                win: this.oriPlayData[newDictFrame],
                Content: [],
                playIndex: 0
            })
            
            let oriNameMed
            for (oriNameMed in this.oriPlayData['Content' + tmpNum]) {
                console.log(this.fileDict[this.oriPlayData['Content' + tmpNum][oriNameMed]])
                this.handledPlayData[newDictFrame].Content.push(this.fileDict[this.oriPlayData['Content' + tmpNum][oriNameMed]])
            }
        }
    }
    console.log(this.handledPlayData)
}
回到顶部