头图

组件v-model

父组件内

<NavSearch v-model="searchForm" @onSearch="onSearch" />
const selectParams = ref({
  name: null,
  address: null,
  desc: null
})

我们在子组件上定义v-model用于双向绑值,父组件内需要定义一个绑定的变量searchForm

子组件
然后在子组件内通过defineProps接收父组件绑定的值

const props = defineProps({
  modelValue: {
    type: Function,
    default: () => ({
      name: null,
      address: null,
      desc: null
    }),
  },
});

const { modelValue } = toRefs(props);

通过toRefs解构defineProps内的modelValue,这样子组件的表单就可以直接使用modelValue
子组件内点击搜索按钮,此时通过update:modelValue事件回传改值

const emit = defineEmits(["update:modelValue", "onSearch"]);
const onSearch = () => {
  // 子组件直接修改modelValue的值,update:modelValue表示修改modelValue,修改为第二个参数的值
  emit("update:modelValue", modelValue.value);
  // 搜索时返回一个事件,父组件内执行查询列表
  emit("onSearch");
};

父组件

<NavSearch v-model="searchForm" @onSearch="onSearch" />
const onSearch = () => {
  console.log("查询列表", searchForm.value);
};

通过给组件绑定v-model让父子组件的数据双向互通,父组件可以拿到最新的值。

分页组件的封装

这里举例一个分页组件的封装,通过v-model双向绑定,数据修改后通知父组件更新

// 父组件
<pagination v-model="pageInfo" @change="onPageChange" />

const pageInfo = ref({
  num: 1,
  size: 12,
  total: 50
});

const onPageChange = () => {
  console.log('分页变化', pageInfo.value);
};
// 子组件
<template>
  <a-pagination
    :current="modelValue.num"
    :page-size="modelValue.size"
    :total="modelValue.total"
    :page-size-options="[12, 24, 36, 48, 60]"
    size="medium"
    show-total
    show-jumper
    show-page-size
    @change="onPageChange"
    @page-size-change="onPageSizeChange"
  />
</template>

<script setup lang="ts">
// 子传父事件
const emit = defineEmits(['update:modelValue', 'change']);
// 接收父组件v-model
const props = defineProps({
  modelValue: {
    type: Object,
    default: () => ({
      num: 1,
      size: 12,
      total: 0
    })
  }
});
// 用toRefs变为可修改响应式,用于组件绑值,但无法直接影响父组件
const { modelValue } = toRefs(props);

// 当前页发生变化
const onPageChange = (current: number) => {
  // 先修改modelValue
  modelValue.value.num = current;
  // 通过update:modelValue事件修改父组件绑定的modelValue
  emit('update:modelValue', modelValue);
  // 子组件返回事件,通知父组件更新
  emit('change');
};

// 当前分页大小发生变化
const onPageSizeChange = (size: number) => {
  // 先修改modelValue
  modelValue.value.size = size;
  // 通过update:modelValue事件修改父组件绑定的modelValue
  emit('update:modelValue', modelValue);
  // 子组件返回事件,通知父组件更新
  emit('change');
};
</script>

image.png
另外,你也可以通过withDefaultsdefineProps标注数据类型,结果是一样的

interface IProps {
  modelValue: {
    num: number;
    size: number;
    total: number;
  };
}

const props = withDefaults(defineProps<IProps>(), {
  modelValue: () => ({
    num: 1,
    size: 12,
    total: 0
  })
});
const { modelValue } = toRefs(props);

兔子先森
466 声望556 粉丝

致力于新技术的推广与优秀技术的普及。