Vue.js:改变对象作为道具传递是一种糟糕的技术吗?

Vue.js: Is changing objects passed as props a bad technique?

我注意到在 Vue 中你可以更改作为 Props 传递的对象字段,它会更改父级中的这些字段。

这是一种糟糕的技术吗?应该避免,还是可以使用?这种方法有什么注意事项?

示例:

Parent.vue:

<script setup>
import { reactive } from 'vue';
import ChildInput from "./Child.vue";
</script>

<script>
const myObj = reactive({'val':'Default text'});
</script>

<template>
  <ChildInput :passedObj="myObj" />
  <div>
    It's a parent input: 
    <input v-model="myObj.val">
  </div>
</template>

Child.vue:

<script setup>
  const props = defineProps({
    passedObj: {
      type: Object,
      required: true,
    },
  })
  
  const dropValue = function() {
        props.passedObj.val = "Changed text";
  }
</script>

<template>
  <div>
    <label>
      It's a child input: 
      <input v-model="passedObj.val">
      <button @click="dropValue">Change text</button>
    </label>
  </div>
</template>

你可以check this example here.

禁止浅 prop 突变,因为 props 对象是 read-only。

深层道具突变是 a bad practice that should be avoided。一个原因是这使数据流变得更加复杂且难以遵循,在这种情况下这是无意识地发生的,这解释了为什么这是一个问题。另一个可能的原因是性能可能会受到影响,因为这种情况不在常用范围内,尽管我目前还没有意识到这样的优化问题。

官方推荐在需要对props进行变异时使用v-model two-way binding,这样变异发生在父组件中,需要调试时可以通过Vue事件追踪。当一个 prop 发生深度变异时,它会被克隆到一个子节点中并发送给父节点。

所以它 似乎是 一种不好的做法。如果有人需要用 Composition API.

编写的,我会留下一个我自己使用的替代方案

如果你只想修改 prop 的某些字段,你可以使用 setter 和 getter 的计算变量。像这样。

在parent中:

<script setup>
import { reactive } from "vue";
import InteractiveTable from "./Child.vue";
// Reactive object, which contains our data
const myObj = reactive({
  data: [{name: "Sasha", address: "Middle of nowhere"}]
});
</script>

<InteractiveTable v-model="myObj" />

请记住,您可以传递多个 v-model 自定义标题:v-model:title。您也可以自己监听事件。像那样:

<InteractiveTable :passedObj="myObj" @update:passedObj="(data) => <something>" />

在child中:

import { computed } from "vue";
// Defining our props
// For v-model default prop is 'modelValue'
// Don't forget to assign it to a variable
const props = defineProps({
  modelValue: {
    type: Object,
    required: true,
  },
});
// Emits must be assigned to a variable too
const emit = defineEmits([
  "update:modelValue",
]);

// our computed variable, which we will use in JS instead of prop
const localData = computed({
  get() {
    // note that we can use object itself or any of its properties, 'data' in this case
    return props.modelValue.data;
  },
  set(val) {
    emit("update:modelValue.data", val);
  },
});

现在你可以做:

const addRow = function (name, address) {
  // You have to call this localData with .value
  // In all other regards you can treat it as a normal variable
  localData.value.push({
    'name': name,
    'address': address,
  });
};

您可以阅读有关 computed 个变量的更多信息 here and about emit here

我是 Vue.js 的新手,所以如果您发现我的代码有问题,请在评论中通知我。