隱式修改組件屬性會導致功能異常
實際操作中發現,即便是父組件把簡單數據通過prop傳給了子組件,子組件再使用v-model綁定,也不行,響應式還是對異常
原vue2業務中存在組件定義某個類型為Object的屬性,然后將該屬性對象的屬性值綁定給??v-model??的用法,在此種場景會導致隱式修改組件屬性,vue3中是禁止的,此種方式目前不報錯,但是響應式不正確生效,功能會不正常。請修改掉。
例子一:
針對常規將props屬性賦值給組件內如 ??input???的??v-model???,因??v-model??會雙向修改值,形成修改屬性,此場景推薦用如下方式,兩者均可:
方式1修改:
// vue2 ,vue2中會警告但是能運行正常,vue3中無法正常運行
<el-input v-model="searchValue"/>
props:{searchValue:{type:String,default:""}
}
// vue3可用如下替代方案,
// compoentA.vue
<el-input v-model="searchValueLocal"/>
props:{searchValue:{type:String,default:""}
},
computed:{searchValueLocal:{get(){return this.searchValue;},set(val){this.$emit("update:searchValue",val) // 更新searchValue值}}
}
// compoentB.vue使用 compoentA.vue
<compoent-b v-model:search-value="text"/>
data(){return{text:''}
}
方式2修改
將??v-model??語法糖拆開
??v-model??語法糖本質是綁定??modelValue??,并綁定??update:modelValue=val=>this.modelValue=val
??,導致形成屬性修改,只要將語法糖拆開即可避免直接修改屬性
// compoentA.vue
<el-input :model-value="modelValue" @update:model-value="handleSelectChange"/>
props:{modelValue:{type:String,default:""}
},
methods:{handleSelectChange(val){this.$emit('update:modelValue', val); // 值更新拋給父組件,由父組件來更新修改modelValue的值}
}
例子二:
如下代碼:
1) 問題:定義了??data???組件屬性,但是將屬性綁定給??el-form???的??model???屬性,和各表單組件的??v-model???;此時形成了隱式操作組件屬性的問題,對于el-form
的model
和各表單組件的??v-model??,無法形成響應式,會產生實際填寫內容但是表單校驗提示未輸入內容,表單輸入更新不及時的問題。
2) 解決:在組件內部直接修改屬性的方式改掉,改成單向數據流或者在data中定義變量中轉;同時注意,對于computed的計算屬性(理論上是只讀),也不能做值修改,會不觸發響應式原理
<template><div class="label-form"><el-form :model="data" label-position="top" :rules="rules" ref="labelForm" label-width="10rem" @submit.prevent><el-form-item :label="$t('name')" prop="Name"><el-input @click.stop="focusTag" ref="tag" v-model="data.Name" v-input-permit.name :placeholder="$t('tag_name')"></el-input></el-form-item><el-form-item :label="$t('description')" prop="Describe"><el-inputtype="textarea"@click.stop="focusDesc"ref="desc":count="128":autosize="{ minRows: 2, maxRows: 4, maxWidth: 300 }"v-model="data.Describe"v-input-permit.name:placeholder="$t('description')"></el-input></el-form-item><el-form-item class="form-buttons"><el-button type="primary" @click="onSubmit">{{ $t('btn_save') }}</el-button></el-form-item></el-form></div>
</template><script>
import rules from '@/Common/service/rule/validate-rules';
export default {name: 'LabelForm',props: {data: {type: Object,default() {return {Name: '',Describe: ''};}}},emits: ['onSubmit'],data() {return {rules: {Name: [rules.required, rules.name],Describe: [rules.textareaDescription]}};},methods: {onSubmit() {this.$refs['labelForm'].validate(async (valid) => {if (valid) {this.emit('onSubmit', this.data);} else {this.$refs['labelForm'].focusFirstField();return false;}});},reset() {this.$refs['labelForm'].resetFields();},focusTag() {const inputDom = this.$refs.tag.$el.querySelector('input');inputDom.focus();},focusDesc() {const inputDom = this.$refs.desc.$el.querySelector('textarea');inputDom.focus();}}
};
</script>// ai幫改
<template><div class="label-form"><el-form :model="formData" label-position="top" :rules="rules" ref="labelForm" label-width="10rem" @submit.prevent><el-form-item :label="$t('name')" prop="Name"><el-input @click.stop="focusTag" ref="tagRef" v-model="formData.Name" v-input-permit.name :placeholder="$t('tag_name')"></el-input></el-form-item><el-form-item :label="$t('description')" prop="Describe"><el-inputtype="textarea"@click.stop="focusDesc"ref="descRef":count="128":autosize="{ minRows: 2, maxRows: 4 }"v-model="formData.Describe"v-input-permit.name:placeholder="$t('description')"></el-input></el-form-item><el-form-item class="form-buttons"><el-button type="primary" @click="onSubmit">{{ $t('btn_save') }}</el-button></el-form-item></el-form></div>
</template><script>
import { ref, reactive, onMounted } from 'vue';
import rules from '@/Common/service/rule/validate-rules';export default {name: 'LabelForm',props: {data: {type: Object,default() {return {Name: '',Describe: ''};}}},emits: ['onSubmit'],setup(props, { emit }) {const labelForm = ref(null);const tagRef = ref(null);const descRef = ref(null);const formData = reactive({ ...props.data });const rules = {Name: [rules.required, rules.name],Describe: [rules.textareaDescription]};const onSubmit = () => {labelForm.value.validate((valid) => {if (valid) {emit('onSubmit', formData);} else {labelForm.value.focusFirstField();return false;}});};const reset = () => {labelForm.value.resetFields();};const focusTag = () => {const inputDom = tagRef.value?.$el.querySelector('input');inputDom?.focus();};const focusDesc = () => {const inputDom = descRef.value?.$el.querySelector('textarea');inputDom?.focus();};return {labelForm,tagRef,descRef,formData,rules,onSubmit,reset,focusTag,focusDesc};}
};
</script>