关于vue的单向数据流问题?

新手上路,请多包涵

当一个组件接收一个“引用类型”的状态, 如果只是修改这个状态的属性(不触发这个对象的setter), 这算不算违反了单向数据流?

<template>
        <input v-model="user.name">
</template>
<script>
{
    props:['user']
}
</script>

个人偏向于认为这违反了“单向数据流”. 因为本质上,它还是直接更改了共享状态,而不是通过emit或commit.
不过vue又没有去显示的阻止这种行为,这让我很疑惑,官方到底赞不赞同这种做法?

我知道官方文档,举了例子建议使用data或computed,但是那个例子的共享状态是值类型.
而状态是引用类型的情况,即使 用一个data来接收也只是引用的复制, 还是会更改相同的数据(对象所在的堆区).

我能想到的一种比较较真且能够减轻本人的强迫症(钻牛角尖)的办法是:

<template>
    <input v-model="mutableUser.name" />
</template>

<script>
import { cloneDeep } from "lodash-es";
export default {
  props: ["user"],
  data() {
    return {
      //1.深拷贝 共享状态
      mutableUser: cloneDeep(this.user)
    };
  },
  created() {
    //2.递归的监听mutableUser的所有属性,
    this.$watch(
      "mutableUser",
      function() { this.$emit("update:user", this.mutableUser) },
      { deep: true }
    );
  }
};
</script>

虽然按照死理(必须使用emit或commit修改共享状态),这样应该是符合的.但是,总感觉哪里怪怪的??

阅读 2.7k
1 个回答

我觉得你后面那种写法相对这个情景下完全没必要,除非父组件每次update都执行一次cloneDeep 这明显有效率问题并不是个好主意,否则emit触发一次之后usermutableUser马上又指向同一个对象。

你可以试下不用v-model语法糖,而用原生方法来处理

<input :value="user.name" @input="$emit('update-name', $event.target.value)">

你若不想用难看的$event.target.value也可以多写几行代码借助计算属性

<template>
  <input v-model="userName">
</template>
<script>
  export default {
    props: ['user'],
    computed: {
      userName:{
        get(){
          return this.user.name
        },
        set(name){
          this.$emit('update-name', name)
        }
      }
    },

当然父组件必须监听这个事件来触发更新

<ChildInput
    :user="user"
    @update-name="name => user.name = name"
>
</ChildInput>

这样是否可以适度减轻你对单向数据流的执念?

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题