computed&v-model 父子组件数据传递(保留单项数据流,简化代码)

1、组件结构
在这里插入图片描述

(1)父组件

<template>
    <div>
    	<SearchBar v-model="searchData" />
    </div>
</template>

<script setup>
import { ref } from 'vue'
import SearchBar from './components/SearchBar.vue'
const searchData = ref({
   keyword:'',
   placeholder:'请输入你要查询的关键字',
   options:[{
	{ label:'视频',value:'video'},
	{ label:'文章', value:'article'},
	{ label:'用户', value:'user'},
	}],
	selectedValue:'video',
})
</script>

(2)子组件 SearchBar.vue

<template>

   <el-input v-model="modelValue.keyword"
	:placeholder="modelValue.placeholder"
	>
	
	<template #prepend>
		<el-select 
		  v-model="modelValue.selectedValue"
		  placeholder="Select"
		  style="width:85px"
		  >
			<el-option
				v-for="item in modelValue.options"
				:key="item.value"
				:label="item.label"
				:value="item.value"
            ></el-option>
		</el-select>
	</template>
	
	<template #append>
		<el-button :icon="Search">
	</template>
	
   </el-input>
</template>

<script setup>
import { Search } from '@element-plus/icon'

const props=defineProps({
   modelValue:{
		type:Object,
		required:true
	}
})
</script>

直接把父组件传递过来的数据绑定进去了,这会有什么问题呢?
现在直接变成了这种结构:
在这里插入图片描述
打破了单项数据流,每次打破单项数据流,你的工程就离屎山更近一步 ~~

方案一:
最笨的办法呢,在子组件里面就不能用v-model了,不然的话文本框一变,父组件的数据不就跟着改了吗,所以得把它拆开,拆成两个,一个是:modelValue,另一个是@update:modelValue,当事件触发的时候我调用这个函数
在这里插入图片描述
handlekeywordChange 函数要做的事情就是去触发子组件的emit:update:modelValue

需要在子组件里定义事件(子组件变化之后触发这个事件):
子组件 SearchBar.vue

<template>

   <el-input 
    :modelValue = "modelValue.keyword"
    @update:modelValue = "handleKeywordChange"
	:placeholder="modelValue.placeholder"
	>
	
	<template #prepend>
		<el-select 
		  v-model="modelValue.selectedValue"
		  placeholder="Select"
		  style="width:85px"
		  >
			<el-option
				v-for="item in modelValue.options"
				:key="item.value"
				:label="item.label"
				:value="item.value"
            ></el-option>
		</el-select>
	</template>
	
	<template #append>
		<el-button :icon="Search">
	</template>
	
   </el-input>
</template>

<script setup>
import { Search } from '@element-plus/icon'

const props=defineProps({
   modelValue:{
		type:Object,
		required:true
	}
})

const emit = defineEmits(['update:modelValue'])

function handleKeywordChange(val){
// val只是modelValue里面其中一个属性,但是要传的是modelValue对象,所以做个简单处理啦~~
	emit('update:modelValue',{
       ...props.modelValue,
       keyword:val
	})
}
</script>

虽然麻烦 但是保证了单项数据流。

方案二:
使用计算属性
在这里插入图片描述
在 子组件 SearchBar.vue 中:
在这里插入图片描述
在el-input里面依然改成v-model:
在这里插入图片描述
但是呢,这个简化并不多,每一项v-model都需要做一个计算属性。继续简写 直接返回这个对象:
在这里插入图片描述
绑定计算属性的具体字段:
在这里插入图片描述
一个计算属性就解决所有问题,但是真的可以吗???
我改变的是model里面keyword属性,计算属性set是收不到通知的,无法触发。
在这里插入图片描述
在这里返回一个代理对象

cnost model = computed({
  get(){
     return new Proxy(props.modelValue,{
		// 代理的属性,属性名和属性值	
		set(obj,name,val){
			// 你想改变对象里面的某个属性,就意味着你是改动了整个对象,要给他生成一个新的对象
			emit('update:modelValue',{
				...obj,
				[name]:val
			});
			return true
		}
	  })
	}
})

每次这样写很麻烦,把它提出去,写成一个辅助函数:

// 属性对象,属性名,emit函数
export function useVModel(props,propName,emit){
return computed({
  get(){
     return new Proxy(props[propName],{
		set(obj,name,val){
			emit('update:'+ propName,{
				...obj,
				[name]:val
			});
			return true
		}
	  })
	},
	set(val){
		emit('update:' + propName,val)
	}
})

}

子组件页面调用:
在这里插入图片描述
既保留了单项数据流,又简化了代码~~

  • 16
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值