组建之间通信, 传对象会出现什么问题?(已解决)

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>

<body>
  <div class="container">
    <div class="row">
      <div class="col-md-6">
        <div class="panel panel-default">
          <div class="panel-heading">
            <h3 class="panel-title"><span class="glyphicon glyphicon-th-list"></span> MVVM</h3>
          </div>
          <div class="panel-body">

            <!-- vm -->
            <div id="vm">
              <h3>{{ title }}</h3>
              <ol>
                <li v-for="todo in todos">
                  <dl>
                    <dt contenteditable="true" v-on:blur="update(todo, 'name', $event)">{{ todo.name }}</dt>
                    <dd contenteditable="true" v-on:blur="update(todo, 'description', $event)">{{ todo.description }}</dd>
                    <dd><a href="#0" v-on:click="remove(t)">Delete</a></dd>
                  </dl>
                </li>
              </ol>
            </div>
            <!-- vm -->
          
          </div>
        </div>
      </div>
      <div class="col-md-6">
        <div class="panel panel-default">
          <div class="panel-heading">
            <h3 class="panel-title"><span class="glyphicon glyphicon-plus"></span> Add New Todo</h3>
          </div>
          <div class="panel-body">

            <!-- vmAdd -->
            <form id="vmAdd" action="#0">
              <div class="form-group">
                <label>Name:</label>
                <input type="text" v-model="name" class="form-control" placeholder="Enter name">
              </div>
              <div class="form-group">
                <label>Description:</label>
                <input type="text" v-model="description" class="form-control" placeholder="Enter description">
              </div>
              <button type="submit" class="btn btn-default" v-on:click.prevent="submit">Add</button>
            </form>
            <!-- vmAdd -->

          </div>
        </div>
      </div>
    </div>
  </div>

  <script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.5.13/dist/vue.js"></script>
  <script>
    // 集成API
    //https://www.liaoxuefeng.com/wiki/001434446689867b27157e896e74d51a89c25cc8b43bdb3000/00147576011615487c65971b3bd4acfa8ee94baaab3dbf7000

    var bus = new Vue()

    var vm = new Vue({
      el: '#vm',
      data: {
        title: 'TODO List',
        todos: [],
      },
      mounted: function() {
        // bus.$on('todo', function(todo) {
        //   console.log(todo);
        //   this.todos.push(todo);
        // }.bind(this))
      },
      created: function() {
        this.init();
      },
      methods: {
        init: function() {
          this.todos = [{
              name: "kavan",
              description: "foo"
            },
            {
              name: "lili",
              description: "bar"
            }
          ]
        },
        create: function(todo) {
          this.todos.push(todo)
        },
        update: function(todo, prop, event) {
          todo[prop] = event.target.innerText
        }
      }
    });

    var vmAdd = new Vue({
      el: '#vmAdd',
      data: {
        name: '',
        description: ''
      },
      methods: {
        submit: function() {
          vm.create(this.$data)
          // bus.$emit('todo', this.$data)
          this.name = '';
          this.description = '';
        }
      }
    });
  </script>
</body>

</html>

clipboard.png

这是老的问题题目(两个vue实例间传递数据问题, 怎么解除数据绑定?), 发现根本不是这原因于是更改了问题题目!
传对象时不能指向同一个对象的引用, 否则在另外一个组建中修改这个对象原组建的view也会因为vue实例中model的修改而发生变化, 目前我的解决方法是"克隆一个新对象"即在内存中新生成一个全新对象, 并把引用传给要通信的组件,JSON.parse(JSON.stringify(anObject)).即首先把对象转化成JSON字符串再把字符串转化成对象, 就相当于克隆了一个对象anObject. 这样这个问题就能得到解决!

事情的起因是想写了个mvvm模拟model更新DOM也同步更新的Demo, 碰到一个不可描述的问题冥想许久不得要领,于是来提问!
首先,发现不可以发动图,而这个问题作为新手表述不会全面,所以请复制代码放入html文件中打开网页, 然后在add表单中添加2个todo事件, 发现两个实例之间的数据是动态绑定, 这不是我想要的.
其实有更好的写法, 但是我认为这个问题的解决会加深对vue框架的理解,所以:


我尝试过把vmAdd中submit方法中的初始化name和description用setTimeout包裹没用, 用非父子组件间的数据传递(空实例, 代码注释了没有删除)也没用, 有哪位有耐心的大神打开代码帮看看要怎么解决, 虽然有其他方法比如只用一个vm实例可以解决, 或者不要在vmAdd中的表单上用v-model. 但是, 目前这种情况有没有解决的方法, 话说每个vue实例都是一个组件, vue api不应该一直跟踪数据的? 求解决, 谢谢

clipboard.png

clipboard.png

阅读 2.5k
2 个回答

解决方法:

methods: {
    submit: function () {
    // 重点在这里  为什么自己去拼数据? 因为你一开始传过去的是一个对象,传递的是引用,就是你后面再多次ADD过去的都是一个对象,东西都是一样的。
    // 再然后你马上又清空了,导致没有产生预期的效果,
      vm.create({ name: this.name, description: this.description });
      this.name = '';
      this.description = '';
    }
  }

题外话:

  1. 这个明明用一个vue示例就能做的非常简洁了,为什么还写那么多个?
  2. js保存复杂类型的方法需要注意。
  3. 这个东西并不能加深你对vue的理解,只能说明你的js基础水平不行。

先附一段解决后的完整代码

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>

<body>
  <div class="container">
    <div class="row">
      <div class="col-md-6">
        <div class="panel panel-default">
          <div class="panel-heading">
            <h3 class="panel-title"><span class="glyphicon glyphicon-th-list"></span> MVVM</h3>
          </div>
          <div class="panel-body">

            <!-- vm -->
            <div id="vm">
              <h3>{{ title }}</h3>
              <ol>
                <li v-for="todo in todos">
                  <dl>
                    <dt contenteditable="true" v-on:blur="update(todo, 'name', $event)">{{ todo.name }}</dt>
                    <dd contenteditable="true" v-on:blur="update(todo, 'description', $event)">{{ todo.description }}</dd>
                    <dd><a href="#0" v-on:click="remove(t)">Delete</a></dd>
                  </dl>
                </li>
              </ol>
            </div>
            <!-- vm -->
          
          </div>
        </div>
      </div>
      <div class="col-md-6">
        <div class="panel panel-default">
          <div class="panel-heading">
            <h3 class="panel-title"><span class="glyphicon glyphicon-plus"></span> Add New Todo</h3>
          </div>
          <div class="panel-body">

            <!-- vmAdd -->
            <form id="vmAdd" action="#0">
              <div class="form-group">
                <label>Name:</label>
                <input type="text" v-model="name" class="form-control" placeholder="Enter name">
              </div>
              <div class="form-group">
                <label>Description:</label>
                <input type="text" v-model="description" class="form-control" placeholder="Enter description">
              </div>
              <button type="submit" class="btn btn-default" v-on:click.prevent="submit">Add</button>
            </form>
            <!-- vmAdd -->

          </div>
        </div>
      </div>
    </div>
  </div>

  <script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.5.13/dist/vue.js"></script>
  <script>
    // 集成API
    //https://www.liaoxuefeng.com/wiki/001434446689867b27157e896e74d51a89c25cc8b43bdb3000/00147576011615487c65971b3bd4acfa8ee94baaab3dbf7000

    var bus = new Vue()

    var vm = new Vue({
      el: '#vm',
      data: {
        title: 'TODO List',
        todos: [],
      },
      mounted: function() {
        // bus.$on('todo', function(todo) {
        //   console.log(todo);
        //   this.todos.push(todo);
        // }.bind(this))
      },
      created: function() {
        this.init();
      },
      methods: {
        init: function() {
          this.todos = [{
              name: "kavan",
              description: "foo"
            },
            {
              name: "lili",
              description: "bar"
            }
          ]
        },
        create: function(todo) {
          const todo_new = { ...todo }
          console.log(todo_new)
          this.todos.push(todo_new)
        },
        update: function(todo, prop, event) {
          todo[prop] = event.target.innerText
        }
      }
    });

    var vmAdd = new Vue({
      el: '#vmAdd',
      data: {
        name: '',
        description: ''
      },
      methods: {
        submit: function() {
          console.log(vm)
          vm.create(this.$data)
          this.name = '';
          this.description = '';
        }
      }
    });
  </script>
</body>

</html>

先吐槽一下你的代码,为什么要用两个 vue 实例……,其实用一个更简洁一点。
然后是你上面的问题

我的改动是这个地方

create: function(todo) {
          const todo_new = { ...todo } // 复制值
          console.log(todo_new)
          this.todos.push(todo_new)
        },

这里其实是一个 js 基础问题,关于对象的引用,其实是引用的对象的一个指针。例如下面

var obj = { name: 'lw' }
var obj_new = obj
// 这里 obj 和 obj_new 他们都保存的是指向同一个对象的一个指针,而不是对象的值
// 例如下面这样
obj_new.name = 'xm'
// 你会发现 obj.name 的值也会变为 xm 而不是原来的 lw  

然后你的问题就明了了,你传递给 vm 的对象,把 vmAdd 对值的跟踪处理(get,set)也一起传入。这样的话,其实你传入的这个值和 vmAdd 中的 data 指向的对象是同一个。
所以解决方法就是把这个对象深度复制一下,然后传入对象的值而不是传入对象的一个引用。

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