美文网首页vue
vue3 笔记一:在子组件中 props 的参数无法直接被改变

vue3 笔记一:在子组件中 props 的参数无法直接被改变

作者: 暴躁程序员 | 来源:发表于2024-09-27 23:06 被阅读0次

vue 单向数据流特点

一、根据使用场景分为以下解决方案:

场景一:父子状态初始一致,之后不一致,把父组件状态作为子组件初始值,比如:表单提交的数据反显

方案一:将 props 参数作为子组件新状态的初始值

注意:当 props 参数为对象类型时。需要借助深拷贝,否则会影响父组件状态

  1. 在父组件中
<script setup>
  import { ref } from "vue";
  import DialogForm from "@/components/DialogForm.vue";
  const show = ref(false);
  const toggle = () => {
    show.value = !show.value;
  };
  const formData = ref({
    username: "alias",
    password: "123456",
  });
  const test = 100;
  const submit = (v) => {
    console.log("submit:", v);
  };
</script>
<template>
  <h1>{{ show }}</h1>
  <el-button type="primary" @click="toggle">{{ show ? 'hide' : 'show' }} dialog</el-button>
  <dialog-form v-model:show="show" :formData="formData" :test="test" @submit="submit" />
</template>
  1. 在子组件中
<script setup>
  import { ref } from "vue";

  const props = defineProps({
    show: {
      type: Boolean,
      default: false,
    },
    formData: {
      type: Object,
      default: {},
    },
    test: {
      type: Number,
      default: 100,
    },
  });
  const emit = defineEmits(["update:show", "submit"]);

  // 子组件把父组件状态作为新状态的初始值,此处必须深拷贝,否则会同步修改父组件状态
  const form = ref(JSON.parse(JSON.stringify(props.formData)));

  // 不改变的父组件状态,作为重置数据
  const resetForm = () => {
    form.value = JSON.parse(JSON.stringify(props.formData));
  };
  const confirm = () => {
    emit("submit", form.value);
    resetForm();
    emit("update:show", false);
  };
  const cancel = () => {
    resetForm();
    emit("update:show", false);
  };

  // 基础类型直接作为初始值即可
  const newTest = ref(props.test);
  const changeBase = () => {
    newTest.value += 1;
  };
</script>

<template>
  <hr />
  <div v-if="show">
    <div>
      <div style="margin: 20px 0">{{ formData }}</div>
      <el-form :model="form" label-width="auto" style="max-width: 600px">
        <el-form-item label="用户名">
          <el-input v-model="form.username" />
        </el-form-item>
        <el-form-item label="密码">
          <el-input v-model="form.password" />
        </el-form-item>
      </el-form>
    </div>
    <div style="margin: 20px 0">
      <el-button type="primary" @click="changeBase">改变基础类型props</el-button>
      <div>{{ test }}</div>
      <div>{{ newTest }}</div>
    </div>
    <div>
      <el-button type="primary" @click="confirm">confirm</el-button>
      <el-button @click="cancel">cancel</el-button>
    </div>
  </div>
</template>

场景二:父子状态始终一致,比如:dialog 的展示和隐藏

方案一:将 props 属性定义为对象类型,在子组件中改变 props 参数内部属性会改变父组件状态
  1. 在父组件中
<script setup>
  import { ref } from "vue";
  import DialogForm from "@/components/DialogForm.vue";
  const showConfig = ref({
    show: false,
  });
  const toggle = () => {
    showConfig.value.show = !showConfig.value.show;
  };
</script>
<template>
  <h1>{{ showConfig.show }}</h1>
  <el-button type="primary" @click="toggle">{{ showConfig.show ? 'hide' : 'show' }} dialog</el-button>
  <dialog-form :showConfig="showConfig" />
</template>
  1. 在子组件中
<script setup>
  const props = defineProps({
    showConfig: {
      type: Object,
      default() {
        return {};
      },
    },
  });

  const confirm = () => {
    props.showConfig.show = false;
  };
  const cancel = () => {
    props.showConfig.show = false;
  };
</script>

<template>
  <hr />
  <div v-if="showConfig.show">
    <div>
      <el-button type="primary" @click="confirm">confirm</el-button>
      <el-button @click="cancel">cancel</el-button>
    </div>
  </div>
</template>
方案二:在子组件中通过 emit 事件改变父组件状态
  1. 在父组件中
<script setup>
  import { ref } from "vue";
  import DialogForm from "@/components/DialogForm.vue";
  const show = ref(false);
  const toggle = () => {
    show.value = !show.value;
  };
</script>
<template>
  <h1>{{ show }}</h1>
  <el-button type="primary" @click="toggle">{{ show ? 'hide' : 'show' }} dialog</el-button>
  <dialog-form :show="show" @closeDialog="toggle" />
</template>
  1. 在子组件中
<script setup>
  const props = defineProps({
    show: {
      type: Boolean,
      default: false,
    },
  });
  const emit = defineEmits(["closeDialog"]);

  const confirm = () => {
    emit("closeDialog", false);
  };
  const cancel = () => {
    emit("closeDialog", false);
  };
</script>

<template>
  <hr />
  <div v-if="show">
    <div>
      <el-button type="primary" @click="confirm">confirm</el-button>
      <el-button @click="cancel">cancel</el-button>
    </div>
  </div>
</template>
方案三:在子组件中通过 emit 事件改变父组件状态(借助 v-model 语法糖简写)
  1. 在父组件中
<script setup>
  import { ref } from "vue";
  import DialogForm from "@/components/DialogForm.vue";
  const show = ref(false);
  const toggle = () => {
    show.value = !show.value;
  };
</script>
<template>
  <h1>{{ show }}</h1>
  <el-button type="primary" @click="toggle">{{ show ? 'hide' : 'show' }} dialog</el-button>
  <dialog-form v-model:show="show" />
</template>
  1. 在子组件中
<script setup>
  const props = defineProps({
    show: {
      type: Boolean,
      default: false,
    },
  });
  const emit = defineEmits(["update:show"]);

  const confirm = () => {
    emit("update:show", false);
  };
  const cancel = () => {
    emit("update:show", false);
  };
</script>

<template>
  <hr />
  <div v-if="show">
    <div>
      <el-button type="primary" @click="confirm">confirm</el-button>
      <el-button @click="cancel">cancel</el-button>
    </div>
  </div>
</template>

二、案例: 实现 dialog form 组件

  1. 在 父组件 中
<script setup>
  import MyDialog from "@/components/MyDialog";
  import { ref } from "vue";

  // 1、dialog 是否展示状态
  const isShow = ref(false);
  const confirm = (v) => {
    console.log(v);
  };
  const cancel = (v) => {
    console.log(v);
  };

  // 2、定义form表单数据和配置
  const formConfig = ref([
    {
      field: "name",
      span: 20,
      label: "名字",
      type: "input",
    },
    {
      field: "gender",
      span: 20,
      label: "性别",
      type: "radio",
      options: [
        {
          label: "男",
          value: 1,
        },
        {
          label: "女",
          value: 2,
        },
      ],
    },
  ]);
  const formData = ref({
    name: "123",
    gender: 1,
  });
</script>

<template>
  <h1>修改子组件 props</h1>
  <div>{{ formData }}</div>
  <el-button type="primary" @click="isShow = !isShow"> dialog {{ isShow }}</el-button>
  <MyDialog v-model:isShow="isShow" :formConfig="formConfig" :formData="formData" @emitConfirm="confirm" @emitCancel="cancel" />
</template>
  1. 在 子组件 中
<template>
  <div class="dialog-wrapper">
    <el-dialog v-model="isDialog" title="提示" width="500" :before-close="handleClose">
      <el-form :model="form" label-width="auto" style="max-width: 600px">
        <el-col v-for="item in formConfig" :key="item.field" :span="item.span">
          <el-form-item v-if="item.type === 'input'" :label="item.label">
            <el-input v-model="form[item.field]" />
          </el-form-item>
          <el-form-item v-if="item.type === 'radio'" :label="item.label">
            <el-radio-group v-model="form[item.field]">
              <el-radio v-for="ra in item.options" :key="ra.label" :value="ra.value">{{ ra.label }}</el-radio>
            </el-radio-group>
          </el-form-item>
        </el-col>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button @click="cancel"> 取消 </el-button>
          <el-button type="primary" @click="confirm"> 确认 </el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>

<script setup>
  import { ref, watch, toRaw } from "vue";
  const props = defineProps({
    isShow: {
      type: Boolean,
      default: false,
    },
    formConfig: {
      type: Array,
      default() {
        return [];
      },
    },
    formData: {
      type: Object,
      default() {
        return {};
      },
    },
  });

  // 1、定义 form 表单数据,深拷贝不直接改变 props 属性
  const form = ref(JSON.parse(JSON.stringify(props.formData)));

  // 2、定义控制 dialog 状态,此处需要借助watch、emit,不直接改变 props 属性
  const isDialog = ref(false);
  watch(
    () => props.isShow,
    (newValue) => {
      isDialog.value = newValue;
    }
  );
  const emit = defineEmits(["emitConfirm", "emitCancel", "update:isShow"]);

  // 3、表单提交:提交表单数据、从父组件改变控制展示 dialog 状态、重置表单数据
  const handleClose = () => {
    emit("update:isShow", false);
    form.value = JSON.parse(JSON.stringify(props.formData));
  };
  const confirm = () => {
    emit("emitConfirm", form);
    handleClose();
  };
  const cancel = () => {
    emit("emitCancel", "");
    handleClose();
  };
</script>

相关文章

  • Vue3---父子组件间互相传值

    Vue3中,子组件通过setup函数中的第一个参数值 props 拿到定义的组件参数进行使用。如果要向父组件传参,...

  • Vue - 父子组件传值

    父组件向子组件传值 在子组件的props中声明想要接受的参数,父组件在使用子组件时传入参数。 使用props传值时...

  • 前端VUE3,JQ,uniapp,综合

    vue3 + ts 子组件更新props 子组件可以直接修改父组件传进来的值子组件定义事件名称update:事件名...

  • Vue .sync修饰符

    案例 子组件: 父组件: 子组件props的数据是从父组件得到的,因此子组件无法直接改变数据。子组件会触发一个事件...

  • Vue3 踩坑记,持续更新

    props的.sync在vue3中被移除 vue2中写法:组件内部: 定义 props:{name: Strin...

  • React传递参数的多种方式

    最常见的就是父子组件之间传递参数 父组件往子组件传值,直接用this.props就可以实现 在父组件中,给需要传递...

  • vue组件中父组件向子组件传递值

    父组件向子组件传递值主要通过在子组建中定义props数组,父组件模板中使用子组件中props内定义的名称直接赋值,...

  • 8.父子组件的数据传递

    1.父组件通过属性向子组件传递参数,子组件用props接收,但是在vue中只可以用,不可以直接修改,这就是单向数据...

  • react第4天

    props 子组件 需要传入两个参数person size父组件使用到子组件在组件使用中传入 person siz...

  • 19.ReactNative组件间的通信

    父组件向子组件通信: 父组件向子组件传值 通过props传递 在父组件中name='我是父组件向子组件传递的参数'...

网友评论

    本文标题:vue3 笔记一:在子组件中 props 的参数无法直接被改变

    本文链接:https://www.haomeiwen.com/subject/cbdlrjtx.html