Vue.js 如何实现父孙之间的双向数据绑定

How to Two-way Data Binding Between Parents and grandchildren in Vue.js

我遇到了一个问题,我通过 cookie 解决了它,但我想在没有 cookie 的情况下解决问题。我有一个名为 app-header 的组件,它还有另一个名为 outmodal 的组件。 现在,我的第一个 Vue 实例需要组件 app-header。

var vue = new Vue({
    el : "html",
    data : {
        title       : "Site Title",
        description : "description of page",
        keywords    : "my keywords",
        view        : "home",
        login       : "login"
    },
    components:{
        "app-header" :require("../../components/header"),
        "app-footer" :require("../../components/footer"),
        "home"       :require("../../views/home")
    },
});

app-header 代码

var Vue     = require("vue");

Vue.partial("login",require("../../partials/login.html"));
Vue.partial("logged",require("../../partials/logged.html"));

module.exports = {
    template    : require("./template.html"),
    replace     : true,
    components  : {
        outmodal : require("../outmodal")
    },
    props : ['login']
}

外峰代码

var Vue = require("vue");
Vue.partial("loginModal",require("../../partials/loginModal.html"));

module.exports = {
    template    : require("./template.html"),
    replace     : true,
    props       : ['name'],
    data        : function () {
            return  {
                userLogin : { mail  :   "", password    :   "", remember    :   ""}
            }

    },
    methods : {
        formSubmit : function(e){
                e.preventDefault();
                this.$http.post("http://example.com/auth/login",{ "email": this.userLogin.mail , "password": this.userLogin.password },function(data,status,request){
                    $.cookie("site_token",data.token,{expires : 1})
                }).error(function(data,status,request){

                });

        }
    }, ready  : function(){
        console.log("it works")
    }
}

在 outmodal 组件中,我连接 API 并检查登录,如果登录成功,我想在我的 Vue 实例中更改登录变量的值。我使用 web pack 来构建所有需求。所以我不知道如何在这些文件之间进行数据绑定。

我该如何解决?我

我找到的最佳解决方案

为 0.12

http://012.vuejs.org/guide/components.html#Inheriting_Parent_Scope

1.0

http://v1.vuejs.org/guide/components.html#Parent-Child-Communication

2.0

https://vuejs.org/v2/guide/components.html#Composing-Components(使用 props 单向绑定父子数据)

我发现这个更准确。 https://vuejs.org/v2/guide/components.html#sync-Modifier 仅在 2.3.0+ 中。 老实说,它仍然不够好。应该只是 'two-way' 数据绑定的一个简单选项。所以 none 这些选项是好的。

尝试改用 vuex。为此,他们有更多选择。 https://vuex.vuejs.org/en/state.html

有几种方法,其他答案中提到了一些:

  1. 使用props on components
  2. 使用v-model attribute
  3. 使用sync modifier
  4. 使用Vuex

请记住,对于双向绑定,它可能会导致一系列难以维护的突变, 引用自文档:

Unfortunately, true two-way binding can create maintenance issues, because child components can mutate the parent without the source of that mutation being obvious in both the parent and the child.

以下是可用方法的一些详细信息:

1.) 在组件上使用道具

道具易于使用,是解决大多数常见问题的理想方式。
由于 how Vue observes changes 所有属性都需要在一个对象上可用,否则它们将不会是反应性的。 如果在 Vue 完成使它们可观察后添加任何属性,则必须使用 'set'

 //Normal usage
 Vue.set(aVariable, 'aNewProp', 42);
 //This is how to use it in Nuxt
 this.$set(this.historyEntry, 'date', new Date());

该对象对组件和父组件都是反应式的:

I you pass an object/array as a prop, it's two-way syncing automatically - change data in the child, it is changed in the parent.

If you pass simple values (strings, numbers) via props, you have to explicitly use the .sync modifier

引自 -->

2.) 使用v-model属性

v-model 属性是一种语法糖,可以轻松实现父子之间的双向绑定。它与 sync 修饰符做同样的事情,只是它使用特定的 prop 和特定的事件来绑定

这个:

 <input v-model="searchText">

与此相同:

 <input
   v-bind:value="searchText"
   v-on:input="searchText = $event.target.value"
 >

道具必须是,事件必须是输入

3.) 使用同步修饰符

sync 修饰符也是语法糖,与 v-model 的作用相同,只是 prop 和事件名称由正在使用的任何内容设置。

在父类中可以这样使用:

 <text-document v-bind:title.sync="doc.title"></text-document>

可以从子级发出一个事件以通知父级任何更改:

 this.$emit('update:title', newTitle)

4.) 使用 Vuex

Vuex 是一个可以从每个组件访问的数据存储。 可以订阅更改。

通过使用 Vuex 存储,可以更轻松地查看数据突变流,并且它们是明确定义的。通过使用 vue developer tools 可以轻松调试和回滚所做的更改。

这种方法需要更多的样板文件,但如果在整个项目中使用,它会成为一种更清晰的方式来定义如何进行更改以及从何处进行更改。

getting started guide

我更喜欢 event-driven 文档中推荐的更新。但是,我受到已经使用 props$emit 的现有 ("third-party") 组件的限制。这个组件是我的大child。以下是我的解决方案(使用 propssync 通过 child 传递值,使用 $emit.

计算值

欢迎评论。

Value可以在parent和grandchild中修改不报错:

Grandchild(简化的 third-party 组件):

<template>
  <div v-show="value">{{ value}}</div>
  <button @click="closeBox">Close</button>
</template>

<script>
export default {
  props: {
    value: null
  },
  methods: {
    closeBox() {
      this.$emit('update:value', null);
    }
  }
}
</script>

Child:

<template>
  <grandchild-component :value.sync="passedValue" />
</template>

<script>
export default {
  props: {
    value: null
  },
  computed: {
    passedValue: {
      get() {
        return this.value;
      },
      set(newVal) {
        this.$emit('update:value', newVal);
      }
    }
  }
}
</script>

Parent:

<template>
  <child-component :value.sync="value" />
</template>
<script>
export default {
  data() {
    return {
      value: null,
    }
  },
  // ... e.g. method setting/modifying the value
}
</script>