Uniapp中父组件向子组件传递prop,新的prop会带有旧的prop属性

简洁

最近使用uniapp开发微信小程序,某一个页面需要做成可配置化,因此会出现直接在父组件修改传递到子组件的prop,但是会出现这种情况,假设新传递的prop对象为newObj,旧传递的prop对象为oldObj,如果Object.keys(newObj).length > Object.keys(oldObj).length,newObj中会带有oldObj的属性,值为null。

例子

father.vue

<template>
	<view>
		<view>
		    {{ curCategory }}
		</view>
		<view>
			<childTry :config="config" 
						@gotoPre="gotoPrePage()"
						@gotoNext="gotoNextPage()">
			</childTry>
		</view>
	</view>
</template>

<script>
	import childTry from './childTry.vue'
	export default {
		components: {
			childTry
		},
		data() {
			return {
				pages: ['tab1', 'tab2', 'tab3'],
				curCategory: 'tab1',
				//**********//
				config: {},
				tabConfig1: {
					'tabConfig1-1': {
					},
				},
				tabConfig2: {
					'tabConfig2-1': {
					},
					'tabConfig2-2': {
					},
					'tabConfig2-3': {
					},
				},
				tabConfig3: {
					'tabConfig3-1': {
					},
					'tabConfig3-2': {
					},
					'tabConfig3-3': {
					}
				}
			}
		},
		watch: {
			curCategory(newVal) {
				if(newVal === 'tab1') {
					this.config = this.tabConfig1;
					console.log('father Config', this.config);
					return;
				}
				if(newVal === 'tab2') {
					this.config = this.tabConfig2;
					console.log('father Config', this.config);
					return;
				}
				if(newVal === 'tab3') {
					this.config = this.tabConfig3;
					console.log('father Config', this.config);
					return;
				}
			}
		},
		computed:{
			curPage:function() {
				const index = this.pages.indexOf(this.curCategory)
				return index + 1
			}
		},
		methods: {
			gotoPrePage() {
				// this.setLocalStorage(category,answers)
				const index = this.pages.indexOf(this.curCategory)
				this.curCategory = this.pages[index-1 < 0 ? index : index-1]
			},
			gotoNextPage() {
				const index = this.pages.indexOf(this.curCategory)
				this.curCategory = this.pages[index+1]
			},
		}
	}
</script>

childVue

<template>
	<view>
		<view>
		    <view class="previous-btn" @click="gotoPre">上一步</view>
		    <view class="next-btn" @click="submit">下一步</view>
		</view>
	</view>
</template>

<script>
	export default {
		name: "try",
	    props:["config"],
	    data() {
	        return {
	           
	        }
	    },
	    watch: {
			async category() {
				// this.initAns = await this.getAnswers()
				// if(this.initAns)
				//     this.initAnswers()
			},
			config: {
				deep: true,
				handler(newVal){
					console.log('childNewVal',newVal)
					console.log('childOldVal',oldVal)
				}
			}
		},
		methods:{
			gotoPre() {
			    // this.formatterSubques()
			    this.$emit('gotoPre')
			},
			submit() {
				this.answers = [];
				// this.formatterQues()
				// if(this.formatterSubques()) {
				// }
				this.$emit('gotoNext')
			}
		}
	}
</script>

步骤
从tab2跳到tab3时
在这里插入图片描述
总结:oldvAL的属性跑到newVal的属性中去了。
当我们把父组件的tabConfig3的对象属性的数量修改成一个

	tabConfig1: {
		'tabConfig1-1': {
		},
	},
	tabConfig2: {
		'tabConfig2-1': {
		},
		'tabConfig2-2': {
		},
		'tabConfig2-3': {
		},
	},
	tabConfig3: {
		'tabConfig3-1': {
		},
	}

测试结果,当从tab3到tab2时
在这里插入图片描述
可以看到,oldVal的属性跑到newVal的属性中去了

原因

当父组件传递子组件的prop为一个对象时,在父组件修改对象,假设新的porp为newObj,旧的prop为oldObj,如果Object.keys(newObj).length > Object.keys(oldObj).length,会将oldObj存在的属性,newObj不存在的属性,赋予newObj。

父组件修改传入子组件的prop,会触发render更新,进而触发pach打补丁,pach代码中有一段是diff函数,会使oldObj的属性附加到newObj的属性中。

导致错误的打包源代码如下
/packages/vue-cli-plugin-uni/packages/mp-vue/dist/mp.runtime.esm.js的文件中

//  pach函数中
// data为newData,mpData为oldData,比较新旧data的差异,进行差异更新
 var diffData = this.$shouldDiffData === false ? data : diff(data, mpData);
// diff函数
function diff(current, pre) {
    var result = {};
    syncKeys(current, pre);
    _diff(current, pre, '', result);
    return result
}
// syncKeys函数,进行递归差异更新
function syncKeys(current, pre) {
    if (current === pre) { return }
    var rootCurrentType = type(current);
    var rootPreType = type(pre);
    if (rootCurrentType == OBJECTTYPE && rootPreType == OBJECTTYPE) {
    	// 错误原因在于此,当新的对象的keys长度大于旧对象的keys长度时,会进行将旧对象属性赋予新对象
        if(Object.keys(current).length >= Object.keys(pre).length){
            for (var key in pre) {
                var currentValue = current[key];
               // 如果newObj的keys数量大于oldObj的keys数量,则会将oldObj的属性赋予newObj,并且值为null
                if (currentValue === undefined) {
                    current[key] = null;
                } else {
                    syncKeys(currentValue, pre[key]);
                }
            }
        }
    } else if (rootCurrentType == ARRAYTYPE && rootPreType == ARRAYTYPE) {
        if (current.length >= pre.length) {
            pre.forEach(function (item, index) {
                syncKeys(current[index], item);
            });
        }
    }
}

上述打包后的代码中,会将新旧data进行比较,但是不知道为什么要把旧对象的key值添加到新对象中,有点坑。

在组件中设置$shouldDiffData字段为false可以规避这个bug,同时不使用其差异算法

this.$shouldDiffData = false;

只找到打包后的代码,有人知道具体源码在哪个部分可以评论下

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章