javascript - vue - $emit 与更新父数据的引用

标签 javascript vue.js

我们需要使用 $emit 来更新 vue 组件中的父数据。这是到处都在说的,甚至是 vue 文档。

v-model.sync 都是使用$emit 来更新的,所以我们把它们算作$emit这里

我涉及的是使用reference 类型传递更新父数据

如果我们将一个对象或数组作为 prop 发送给子组件,并在子组件中更改它,将直接对父数据进行更改。

有些组件我们总是在特定组件中使用,我们不会在其他任何地方使用它们。事实上,这些组件主要用于提高应用程序代码的可读性以及减轻应用程序组件的重量。

将引用类型值作为 prop 传递给子对象以直接从子对象更改它们比传递值然后处理发出的事件要容易得多。尤其是嵌套组件比较多的时候

当我们使用引用类型来更新父级时,代码的可读性会更容易。

例如,假设我们有grand-parentparentchild 组件。在 parent 组件中,我们有一个字段更改祖父数据的 first 属性,在子组件中,我们有另一个字段更改 second 属性祖 parent 数据

如果我们想使用 $emit 来实现它,我们有这样的东西:(我们没有使用 .sync 或 v-model)

// grand-parent

<template>
    <div>
        <parent :fields="fields" @updateFields="fields = $event" >
    </div>
</template>

<script>
import parent from "./parent"
export default {
    components : {parent},
    data(){
        return {
            fields : {
                first : 'first-value',
                second : 'second-value',
            }
        }
    }
}
</script>




// parent

<template>
    <div>
        <input :value="fields.first" @input="updateFirstField" />
        <child :fields="fields" @updateSecondField="updateSecondField" >
    </div>
</template>

<script>
import child from "./child"
export default {
    components : {child},
    props : {
        fields : Object,
    },
    methods : {
        updateFirstField(event){
            this.$emit('updateFields' , {...this.fields , first : event.target.value})
        },
        updateSecondField(value){
            this.$emit('updateFields' , {...this.fields , second : value})
        }
    }
}
</script>




// child

<template>
    <div>
        <input :value="fields.first" @input="updateSecondField" />
    </div>
</template>

<script>
export default {
    props : {
        fields : Object,
    },
    methods : {
        updateFirstField(event){
            this.$emit('updateSecondField' , event.target.value)
        },
    }
}
</script>


是的,我们可以使用 .sync 来简化操作,或者只传递我们需要的字段。但这是基本示例,如果我们有更多字段并且我们在所有组件中使用所有字段,这就是我们这样做的方式。

同样的事情使用引用类型会是这样的:

// grand-parent

<template>
    <div>
        <parent :fields="fields" >
    </div>
</template>

<script>
import parent from "./parent"
export default {
    components : {parent},
    data(){
        return {
            fields : {
                first : 'first-value',
                second : 'second-value',
            }
        }
    }
}
</script>




// parent

<template>
    <div>
        <input v-model="fields.first" />
        <child :fields="fields" >
    </div>
</template>

<script>
import child from "./child"
export default {
    components : {child},
    props : {
        fields : Object,
    }
}
</script>




// child

<template>
    <div>
        <input v-model="fields.second" />
    </div>
</template>

<script>
export default {
    props : {
        fields : Object,
    }
}
</script>

如您所见,使用 reference 类型要容易得多。即使有更多字段。

现在我的问题:

  • 我们应该使用引用类型来更新父数据还是这种方法不好?
  • 即使我们再次使用同一个父组件中的组件,我们也不应该使用这种方法?
  • 我们不应该使用 reference 类型来更新父级的原因是什么?
  • 如果我们不应该使用 reference 类型,为什么 vue 将相同的对象传递给子对象而不是在传递之前克隆它们? (也许是为了更好的表现?)

最佳答案

“始终使用 $emit”规则并非一成不变。两种方法都有利有弊。你应该做任何让你的代码易于维护和推理的事情。

对于您描述的情况,我认为您已经证明直接对数据进行变异是合理的。

当您有一个包含很多属性的对象并且每个属性都可以由子组件修改时,那么让子组件改变每个属性本身就可以了。

替代方案是什么?为每个属性更新发出一个事件?还是发出一个 input 事件,其中包含一个更改了单个属性的对象的副本?这种方法会导致大量内存分配(想想在文本字段中键入,每次按键都会发出一个克隆对象)。话虽如此,但有些库正是为此目的而设计的,并且运行良好(例如 Immutable.js )。

对于只管理小数据的简单组件,例如具有单个字符串值的文本框,您绝对应该使用 $emit。对于具有大量数据的更复杂的组件,有时子组件共享或拥有给定的数据是有意义的。它成为子组件契约的一部分,它会在特定情况下以某种特定方式改变数据。

what is the reason that we should not use reference type to update parent?

  • 父级“拥有”数据并且它知道除了它自己之外没有人会改变它。没有惊喜。
  • parent 可以决定是否接受突变,甚至可以即时修改它。
  • 您不需要观察者就可以知道数据何时更改。
  • 家长知道如何数据被更改以及导致更改的原因。想象一下,有多种方法可以改变数据。父组件可以很容易地知道哪个突变源自子组件。如果外部代码(即子组件内部)可以在任何时间以任何原因改变数据,那么父级就更难知道是什么导致数据发生变化(谁改变了它以及为什么改变了它?)。<

if we should not use reference type why vue pass same object to children and not clone them before passing ? (maybe for better performance ?)

嗯,是的,为了性能,还有许多其他原因,例如:

  • 克隆很重要(浅?深?是否也应该复制原型(prototype)?克隆对象是否有意义?它是单例吗?)。
  • 克隆在内存和 CPU 方面的成本很高。
  • 如果它是克隆的,那么就不可能执行您在此处描述的操作。施加这样的限制性规则是愚蠢的。

关于javascript - vue - $emit 与更新父数据的引用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58704259/

相关文章:

javascript - JSON 的支持程度

javascript - Angular js + 选择插件 + 将值设置为在下拉列表中选择

javascript - iframe.onload 的替代品?

javascript - Vue 切换过滤器

Vue.js 应用程序中的 CSS : Safari & Firefox are not loading Google Font

javascript - 从日志文件中提取正则表达式直到序列

javascript - 如何使用 Regex 和 Javascript 获取字符串的第一个和最后一个字符

javascript - 如何更改 Vue 容器内某个对象的类?

javascript - 无法使用输入更新 v 模型

javascript - 在 Vue 组件内将 webSDK 与 jQuery 集成?