首页 > 解决方案 > 将vue2.x的代码迁移到vue3.x,遇到v-model模块中“task”prop的Unexpected mutation

问题描述

我在将项目从vue2.x迁移到vue3.0时使用了Composition API,但是页面不能正常工作,在vue3.0环境提示我出现“Unexpected mutation of "task" prop”错误,我要知道如何编写正确的 Compos API 这是 Vue2.x 代码

<template>
  <transition name="fade">
    <div class="task" v-if="!task.deleted">
      <input :id="id" type="checkbox" v-model="task.done" />
      <label :for="id">{{ task.title }}</label>
      <transition name="fade">
        <span
          class="task_delete"
          v-show="task.done"
          @click="deleteTask({ task })"
        >
          <i class="fa fa-trash"></i>
        </span>
      </transition>
    </div>
  </transition>
</template>

<script>
import { mapMutations } from "vuex";
let GID = 1;
export default {
  props: {
    task: {
      type: Object,
      required: true,
    },
  },
  data() {
    return {
      id: `task-${GID++}`,
    };
  },
  methods: {
    ...mapMutations(["deleteTask"]),
  },
};
</script>

<style lang="scss">
.task {
  display: flex;
  padding: 12px 0;
  border-bottom: 1px solid #eee;
  font-size: 14px;
}
.task input {
  display: none;
}
.task label {
  flex: 1;
  line-height: 20px;
}
.task label:before,
.task label:after {
  content: "";
  display: inline-block;
  margin-right: 20px;
  margin-top: 1px;
  width: 14px;
  height: 14px;
  vertical-align: top;
}
.task label:before {
  border: 1px solid #ccc;
  border-radius: 2px;
  background-color: white;
}
.task label:after {
  content: "\f00c";
  position: relative;
  display: none;
  z-index: 10;
  margin-right: -16px;
  width: 10px;
  height: 10px;
  padding: 3px;
  border-radius: 2px;
  font: normal normal normal 10px/1 FontAwesome;
  color: white;
  background-color: #ccc;
  float: left;
}
.task input:checked + label:after {
  display: inline-block;
}
.task_delete {
  padding: 0 10px;
  color: #ccc;
  font-size: 16px;
}
.fade-leave-to,
.fade-enter {
  opacity: 0;
}
.fade-enter-to,
.fade-leave {
  opacity: 1;
}
.fade-enter-active,
.fade-leave-active {
  transition: all 0.3s ease;
}
</style>

这是 Vue3.0 代码使用 Composition API,但不起作用

<template>
  <transition name="fade">
    <div class="task" v-if="!data.task.deleted">
      <input :id="id" type="checkbox" v-model="data.task.done" />
      <label :for="id">{{ data.task.title }}</label>
      <transition name="fade">
        <span
          class="task_delete"
          v-show="data.task.done"
          @click="deleteTask({ task })"
        >
          <i class="fa fa-trash"></i>
        </span>
      </transition>
    </div>
  </transition>
</template>

<script>
import { reactive } from "vue";
import { mapMutations } from "vuex";

let GID = 1;
export default {
  name: "Task",
  props: {
    task: {
      type: Object,
      required: true,
    },
  },
  setup(props) {
    const data = reactive({
      task: props.task,
      id: `task-${GID++}`,
    });
    return { data };
  },
  methods: {
    ...mapMutations(["deleteTask"]),
  },
};
</script>

<style lang="scss">
.task {
  display: flex;
  padding: 12px 0;
  border-bottom: 1px solid #eee;
  font-size: 14px;
}
.task input {
  display: none;
}
.task label {
  flex: 1;
  line-height: 20px;
}
.task label:before,
.task label:after {
  content: "";
  display: inline-block;
  margin-right: 20px;
  margin-top: 1px;
  width: 14px;
  height: 14px;
  vertical-align: top;
}
.task label:before {
  border: 1px solid #ccc;
  border-radius: 2px;
  background-color: white;
}
.task label:after {
  content: "\f00c";
  position: relative;
  display: none;
  z-index: 10;
  margin-right: -16px;
  width: 10px;
  height: 10px;
  padding: 3px;
  border-radius: 2px;
  font: normal normal normal 10px/1 FontAwesome;
  color: white;
  background-color: #ccc;
  float: left;
}
.task input:checked + label:after {
  display: inline-block;
}
.task_delete {
  padding: 0 10px;
  color: #ccc;
  font-size: 16px;
}
.fade-leave-to,
.fade-enter {
  opacity: 0;
}
.fade-enter-to,
.fade-leave {
  opacity: 1;
}
.fade-enter-active,
.fade-leave-active {
  transition: all 0.3s ease;
}
</style>

标签: vue.js

解决方案


我认为问题在于您在复选框上使用 v-model 进行道具,它会直接更改道具值而 vue 不允许。尝试通过发出事件手动更新道具值。而且您不需要在子组件中使用具有反应性的道具,但需要在父组件中反应性道具

<input :id="id" type="checkbox" v-value="task.done" @change="updateCheckbox($event)")/>  

在脚本中:

export default {
  name: "Task",
  props: {
    task: {
      type: Object,
      required: true,
    },
  },
  emits: ['updateCheckbox'],
  setup(props) {
    const data = reactive({
      id: `task-${GID++}`,
    });
    return { data };
  },
  methods: {
    updateCheckbox(e) {
       this.$emit('updateCheckbox', e.target.value)
    }
  },
};

推荐阅读