当前位置: 首页 > news >正文

日常开发记录-radio组件

日常开发记录-radio组件

  • 1.radio组件
  • 2.解除疑惑
  • 3.input标签又绑定了v-model,不是重复了吗?还是父组件的v-model绑定到那个元素上的呢
  • 4.拓展

1.radio组件

闲来无事,写写vue2的单选radio组件。

<template><label><inputref="radio"type="radio"class="q-radio__original":value="label":name="name":disabled="disabled"v-model="currentValue"@change="handleChange"/><span class="q-radio__label"><slot>{{ label }}</slot></span></label>
</template><script>export default {name: 'MyRadio',props: {value: {},name: String,label: {},disabled: Boolean,},computed:{currentValue:{get() {return this.value},set(value) {this.$emit('input',value)}}},methods: {handleChange(event) {this.$emit('change', event.target.value)},},}
</script>//父组件<input type="radio" id="contactChoice1" name="contact" value="email" /><label for="contactChoice1">电子邮件</label><input type="radio" id="contactChoice2" name="contact" value="phone" /><label for="contactChoice2">电话</label><input type="radio" id="contactChoice3" name="contact2" value="mail" /><label for="contactChoice3">邮件</label><MyRadio v-model="currentValue" name="aaa" @change="handleChange" label="男"></MyRadio><MyRadio v-model="currentValue" name="aaa" @change="handleChange" label="女"></MyRadio><MyRadio v-model="currentValue" name="bbb" @change="handleChange" label="未知">未知</MyRadio>

实际效果:
在这里插入图片描述

问题就来了,我给了不同的name,但是却和原生的input标签效果不同。按理说男女和未知,应该互不影响的呢。

2.解除疑惑

  1. v-model 的本质
    v-model 本质上是一个语法糖,它由两个操作组成:
    将数据绑定到元素的 value 属性 (:value=“value”)
    监听元素的 input/change 事件 (@input=“value = $event”)
  2. 之前的代码分析
    在之前的代码中:
// App.vue
data() {return {currentValue: ''  // 单个字符串值}
}// 模板中
<MyRadio v-model="currentValue" name="aaa" label="男"></MyRadio>
<MyRadio v-model="currentValue" name="aaa" label="女"></MyRadio>
<MyRadio v-model="currentValue" name="bbb" label="未知">未知</MyRadio>

问题在于:
所有的 MyRadio 组件都绑定到同一个 currentValue 变量
当任何一个 radio 被选中时,都会触发 v-model 的更新
由于共享同一个数据源,当一个 radio 更新了 currentValue,其他所有绑定到这个值的 radio 也会随之更新
3. 数据流动过程
让我们看看当点击一个 radio 时发生了什么:
用户点击 “男” 选项:

// MyRadio 组件内部
computed: {currentValue: {get() {return this.value  // 从父组件获取值},set(value) {this.$emit('input', value)  // 向父组件发送新值}}
}

触发事件链:

点击 radio → change 事件 → currentValue setter → $emit(‘input’) → 父组件 currentValue 更新 → 所有使用这个 currentValue 的组件都更新

由于所有 MyRadio 组件都监听着同一个 currentValue,当它变成 “男” 时:
name=“aaa” 的两个 radio (“男"和"女”) 会更新
name=“bbb” 的 radio (“未知”) 也会更新
它们都共享同一个值,所以只能有一个被选中
4. 为什么使用对象解决了问题
新的实现中:

data() {return {currentValue: {aaa: '',  // 专门存储 name="aaa" 的值bbb: ''   // 专门存储 name="bbb" 的值}}
}

现在:
每组 radio 都有自己独立的数据存储空间
当 name=“aaa” 的 radio 改变时,只会更新 currentValue.aaa
当 name=“bbb” 的 radio 改变时,只会更新 currentValue.bbb
不同 name 的 radio 组之间完全独立,互不影响
5. 原生 radio 的对比
原生 HTML radio 的行为:

<input type="radio" name="contact" value="email">  <!--1 -->
<input type="radio" name="contact" value="phone">  <!--1 -->
<input type="radio" name="contact2" value="mail">  <!--2 -->

原生 radio 通过 name 属性天然分组,同一个 name 的 radio 互斥,不同 name 的 radio 独立。但在 Vue 组件中,我们需要通过数据结构(对象)来模拟这种分组行为。
这就是为什么之前使用单个值会导致所有 radio 互相影响,而使用对象可以实现独立分组的原因。每个分组都有自己的数据空间,就像原生 radio 的 name 属性一样。

3.input标签又绑定了v-model,不是重复了吗?还是父组件的v-model绑定到那个元素上的呢

这是一个很好的问题!让我详细解析一下组件中的 v-model 工作原理。

  1. 组件的双重 v-model
    让我们看看当前的代码:
<!-- App.vue 父组件 -->
<MyRadio v-model="currentValue.aaa" name="aaa" label="男"></MyRadio><!-- MyRadio.vue 子组件 -->
<template><label><inputtype="radio":value="label":name="name"v-model="currentValue"  <!-- 这里确实看起来像重复了 -->@change="handleChange"/><span class="q-radio__label"><slot>{{ label }}</slot></span></label>
</template><script>
export default {computed: {currentValue: {get() {return this.value},set(value) {this.$emit('input', value)}}}
}
</script>
  1. v-model 的传递过程
    实际上这里涉及到两个层级的数据绑定:
    父组件的 v-model
   <MyRadio v-model="currentValue.aaa">

这等同于:

   <MyRadio :value="currentValue.aaa"@input="val => currentValue.aaa = val">

子组件的实现
子组件接收 value prop
通过计算属性 currentValue 代理这个值
当 input 元素变化时,触发 currentValue 的 setter
setter 向上触发 input 事件
3. 数据流动过程
让我们看看当用户点击 radio 时的完整流程:

1. 用户点击 radio input↓
2. 同时触发两个事件流:流程A (v-model):                    流程B (@change):2A. 原生 input 的 v-model 触发      2B. 原生 input 的 change 事件触发↓                                   ↓3A. 更新子组件的 currentValue       3B. 执行 handleChange 方法↓                                   ↓4A. currentValue 的 setter 被调用   4B. 触发 $emit('input', event.target.value)↓                                   5A. 触发 $emit('input', value)6. 父组件接收到 input 事件(可能收到两次!)↓
7. 更新父组件的 currentValue.aaa

确实还会触发两次
4. 优化
实际上,当前的实现确实有些冗余。我们可以简化这个过程,移除子组件中的 v-model:

// ... existing code ...<inputref="radio"type="radio"class="q-radio__original":value="label":name="name":checked="value === label":disabled="disabled"@change="handleChange"/>
// ... existing code ...props: {value: {required: true},name: {type: String,required: true},label: {required: true},disabled: Boolean,},methods: {handleChange(event) {this.$emit('input', this.label)},}
// ... existing code ...

主要改动:
移除了 currentValue 计算属性
使用 :checked=“value === label” 来控制选中状态
直接在 change 事件中发出 input 事件
这样的实现有以下优点:
更清晰的数据流:
父组件的值直接决定 radio 的选中状态
用户操作直接触发父组件的更新
没有中间的计算属性转换
更符合单向数据流:
Props 向下传递(value)
事件向上传递(input)
性能更好:
减少了计算属性的开销
减少了不必要的数据绑定
更容易理解:
直接使用原生 radio 的 checked 属性
清晰的事件处理流程
这样修改后,组件的行为保持不变,但实现更加简洁和高效。父组件的 v-model 仍然可以正常工作,因为我们保持了 Vue 组件 v-model 所需的 value prop 和 input 事件。

4.拓展

在现有代码上加一个

<template><label><inputref="radio"type="radio"class="q-radio__original":value="label":name="name":checked="value === label":disabled="disabled"@change="handleChange"/><span class="q-radio__label"><slot></slot>    <!===新加的><template v-if="!$slots.default">{{label}}</template></span></label>
</template><script>export default {name: 'MyRadio',props: {value: {},name: String,label: {},disabled: Boolean,},methods: {handleChange(event) {this.$emit('input', this.label)this.$emit('change', this.label)},},}
</script>

这是一个很巧妙的插槽(slot)实现,让我们逐部分分析:

  1. 结构解析
    插槽内容
   <slot></slot>

默认插槽,允许用户自定义标签内容
后备内容

   <template v-if="!$slots.default">{{label}}</template>

当没有提供插槽内容时显示的后备内容
使用 prop 中的 label 值
2. 使用场景
场景一:使用插槽自定义内容

<q-radio v-model="radio" label="1"><span style="color: red">自定义文本</span>
</q-radio>

渲染结果:显示红色的"自定义文本"
场景二:直接使用 label

<el-radio v-model="radio" label="选项1"></el-radio>

渲染结果:显示"选项1"
3. 工作原理
插槽检测
s l o t s . d e f a u l t : V u e 提供的插槽检测机制如果有内容传入默认插槽, slots.default: Vue 提供的插槽检测机制 如果有内容传入默认插槽, slots.default:Vue提供的插槽检测机制如果有内容传入默认插槽,slots.default 就不为空
条件渲染
先尝试渲染插槽内容
如果插槽为空(!$slots.default 为 true),则渲染 label 值


http://www.mrgr.cn/news/93116.html

相关文章:

  • Python----数据分析(Matplotlib二:绘图一:折线图,条形图,直方图)
  • DeepSeek 系列模型:论文精读《A Survey of DeepSeek Models》
  • v-code-diff 配置
  • 【第13节】C++设计模式(行为模式)-Template(模板)模式
  • 【每日学点HarmonyOS Next知识】web滚动、事件回调、selectable属性、监听H5内部router、Grid嵌套时高度设置
  • 双目系统求坐标系A到坐标系B的旋转矩阵和平移向量——C++代码实现
  • 测试工程师的DeepSeek提效3:质保中的应用
  • io标准函数和时间函数day2
  • 认识线程
  • SpaCy处理NLP的详细工作原理及工作原理框图
  • 《AJAX:前端异步交互的魔法指南》
  • Qt之QGraphicsView图像操作
  • Python----数据分析(Matplotlib三:绘图二:箱图,散点图,饼图,热力图,3D图)
  • day3作业
  • 【pta】1031 查验身份证
  • windows下使用Hyper+wsl实现ubuntu下git的平替
  • 多视图几何--2单应矩阵-2.0从0-1理解并计算单应矩阵
  • 测试工程师的DeepSeek提效4:测试效能提升应用
  • Keepalived 入门详解:高可用集群部署最佳实践!
  • ASP.NET Core JWT认证与授权