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;

只找到打包後的代碼,有人知道具體源碼在哪個部分可以評論下

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