【vue2、vue3】v-model在自定义组件的使用

目前大部分前端组件库中的组件都是通过v-model来实现的,作用是创建双向数据绑定,让开发者不需要手动编写事件监听器和数据更新逻辑。

下面以自定义组件多选按钮为例,简单介绍下v-modelvue2vue3自定义组件中的使用。

vue2 中 v-model 的使用

父组件通过v-model实现btnSelect属性的双向数据绑定。

点击按钮,SelectBtn子组件会给父组件emit事件btnChange,通知父组件当前正在点击的按钮。同时btnSelect也会自动更新,表示当前选中了哪些按钮。

父组件

通过v-model绑定btnSelect属性。

js 复制代码
<template>
  <div class="wrap">
    <p>请选择需要奖励的班级:</p>
    <SelectBtn
      :options="options"
      v-model="btnSelect"
      @btnChange="btnChange"
    ></SelectBtn>
    <p>您选择的班级是:{{ btnSelect.length ? btnSelect : "" }}</p>
  </div>
</template>

<script>
import SelectBtn from "./child.vue";
export default {
  components: {
    SelectBtn,
  },
  data() {
    return {
      options: [
        {
          label: "一年级一班",
          value: "1-1",
        },
        {
          label: "一年级二班",
          value: "1-2",
        },
        {
          label: "一年级三班",
          value: "1-3",
        },
        {
          label: "一年级四班",
          value: "1-4",
        },
      ],
      btnSelect: [],
    };
  },
  methods: {
    /**
     * @desc 按钮点击事件
     * @param {*} item 点击的按钮
     * @return {*}
     */
    btnChange(item) {
      console.log("当前点击的按钮:", item);
      console.log("v-model绑定的btnSelect:", this.btnSelect);
    },
  },
};
</script>

<style lang="scss" scoped>
.wrap {
  width: 100%;
  height: 100%;
  box-sizing: border-box;
  padding: 30px;
  p {
    text-align: left;
    margin-bottom: 20px;
  }
}
</style>

子组件

model 作为一个vue2选项,用于自定义组件的 v-model 绑定。model 选项接受一个对象,该对象有两个属性:propeventprop 用于指定 v-model 绑定的属性,event 用于指定触发 v-model 更新的事件。

this.$emit("v-model绑定的事件", y)更新事件触发,父组件中v-model绑定的变量值即更新为y

js 复制代码
// 多选按钮
<template>
  <div class="select-box">
    <div
      :class="
        btnSelect.includes(item.value)
          ? 'btn-active select-btn-item'
          : 'select-btn-item'
      "
      v-for="(item, index) in options"
      :key="index"
      @click="handleBtnSelect(item)"
    >
      {{ item.label }}
    </div>
  </div>
</template>

<script>
export default {
  model: {
    prop: "btnSelect", // v-model绑定的变量
    event: "updateBtnSelect", // v-model绑定的事件
  },
  props: {
    options: {
      type: Array,
      default: () => [],
    },
    btnSelect: {
      type: Array,
      default: () => [],
    },
  },
  methods: {
    /**
     * @desc 按钮点击,更新v-model绑定的变量btnSelect,同时emit事件btnChange,通知父组件当前正在点击的按钮项
     * @param {*} item
     * @return {*}
     */
    handleBtnSelect(item) {
      let selectIndex = this.btnSelect.findIndex((ele) => ele === item.value);
      // 按钮多选逻辑处理
      if (selectIndex > -1) {
        this.$emit("updateBtnSelect", [
          ...this.btnSelect.slice(0, selectIndex),
          ...this.btnSelect.slice(selectIndex + 1),
        ]);
      } else {
        this.$emit("updateBtnSelect", [...this.btnSelect, item.value]);
      }
      // 通知父组件当前正在点击的按钮项
      this.$emit("btnChange", item);
    },
  },
};
</script>

<style lang="scss" scoped>
.select-box {
  display: flex;
  color: #4d4d4d;
  .select-btn-item {
    border-radius: 2px;
    cursor: pointer;
    padding: 10px 20px;
    border: 1px solid #e0e0e0;
    margin-right: 10px;
  }
  .btn-active {
    color: white;
    background: #4d99f9;
    border: 1px solid #4d99f9;
  }
}
</style>

vue3 中 v-model 的使用

点击按钮,SelectBtn子组件会给父组件emit事件btnChange,通知父组件当前正在点击的按钮。同时btnSelect也会自动更新,表示当前选中了哪些按钮。

父组件

通过v-model:xxx="yyy"或者v-model="yyy"实现属性的双向数据绑定。

如果为v-model:btnSelect="btnSelect",父组件绑定的属性是btnSelect,同时子组件接收到的props变量为btnSelect

如果为v-model="btnSelect",父组件绑定的属性是btnSelect,同时子组件接收到的props变量为默认值modelValue

js 复制代码
<template>
  <div class="wrap">
    <p>请选择多选按钮:</p>
    <SelectBtn
      :options="options"
      v-model:btnSelect="btnSelect"
      @btnChange="btnChange"
    ></SelectBtn>
    <p>v-model绑定的btnSelect:{{ btnSelect.length ? btnSelect : "" }}</p>
  </div>
</template>

<script setup>
import SelectBtn from "./child.vue";
import { ref } from "vue";
const options = ref([
  {
    label: "一年级一班",
    value: "1-1"
  },
  {
    label: "一年级二班",
    value: "1-2"
  },
  {
    label: "一年级三班",
    value: "1-3"
  },
  {
    label: "一年级四班",
    value: "1-4"
  }
]);
const btnSelect = ref([]); // v-model绑定的变量
/**
 * @desc 按钮点击事件
 * @param {*} item 当前点击的按钮
 * @return {*}
 */
const btnChange = (item) => {
  console.log("当前点击的按钮:", item);
  console.log("v-model绑定的btnSelect:", btnSelect.value);
};
</script>

<style lang="scss" scoped>
.wrap {
  width: 100%;
  height: 100%;
  box-sizing: border-box;
  padding: 30px;
  p {
    text-align: left;
    margin-bottom: 20px;
  }
}
</style>

子组件

子组件接收到的v-model绑定的props变量由父组件决定。

子组件定义v-model绑定的事件,格式为update:xxx, xxxv-model绑定的props变量。emit("update:xxx", 更新值y)事件触发,父组件中v-model绑定变量的变量值就会更新为y

  • 如果父组件中定义v-model="btnSelect",那么子组件中绑定的props变量就是modelValuev-model绑定的事件就是update:modelValue
  • 如果父组件中定义v-model:btnSelect="btnSelect",那么子组件中绑定的props变量就是btnSelectv-model绑定的事件就是update:btnSelect
js 复制代码
<template>
  <div class="select-box">
    <div
      :class="
        btnSelect.includes(item.value)
          ? 'btn-active select-btn-item'
          : 'select-btn-item'
      "
      v-for="(item, index) in options"
      :key="index"
      @click="handleBtnSelect(item)"
    >
      {{ item.label }}
    </div>
  </div>
</template>

<script setup>
const props = defineProps({
  options: {
    type: Array,
    default: () => []
  },
  btnSelect: {
    type: Array,
    default: () => []
  }
});

// 定义v-model绑定的事件,格式为"update:v-model绑定的变量"
const emit = defineEmits(["update:btnSelect", "btnChange"]);

/**
 * @desc 按钮点击事件,更新v-model绑定的变量btnSelect,同时emit事件btnChange,通知父组件当前正在点击的按钮项
 * @param {*} item
 * @return {*}
 */
const handleBtnSelect = (item) => {
  let selectIndex = props.btnSelect.findIndex((ele) => ele === item.value);
  if (selectIndex > -1) {
    emit("update:btnSelect", [
      ...props.btnSelect.slice(0, selectIndex),
      ...props.btnSelect.slice(selectIndex + 1)
    ]);
  } else {
    emit("update:btnSelect", [...props.btnSelect, item.value]);
  }
  emit("btnChange", item);
};
</script>

<style lang="scss" scoped>
.select-box {
  display: flex;
  color: #4d4d4d;
  .select-btn-item {
    border-radius: 2px;
    cursor: pointer;
    padding: 10px 20px;
    border: 1px solid #e0e0e0;
    margin-right: 10px;
  }
  .btn-active {
    color: white;
    background: #4d99f9;
    border: 1px solid #4d99f9;
  }
}
</style>
相关推荐
该怎么办呢10 分钟前
Source/Core/Matrix4.js
前端·javascript
小江的记录本11 分钟前
【MyBatis-Plus】Spring Boot + MyBatis-Plus 进行各种数据库操作(附完整 CRUD 项目代码示例)
java·前端·数据库·spring boot·后端·sql·mybatis
console.log('npc')39 分钟前
响应式布局的 Element UI、Ant Design 24栅格布局
vue.js·ui
于慨1 小时前
Capacitor
前端
IT凝冬1 小时前
liunx 的 centos7 安装ngin
前端
赵锦川1 小时前
大屏比例缩放
前端·javascript·html
于慨2 小时前
tauri
java·服务器·前端
riyue6662 小时前
封装 WebSocket 工具类
网络·vue.js·websocket·网络协议·v
贼爱学习的小黄2 小时前
NC BIP参照开发
java·前端·nc
小江的记录本3 小时前
【MyBatis-Plus】MyBatis-Plus的核心特性、条件构造器、分页插件、乐观锁插件
java·前端·spring boot·后端·sql·tomcat·mybatis