鍍金池/ 問答/HTML/ 組建之間通信, 傳對象會出現(xiàn)什么問題?(已解決)

組建之間通信, 傳對象會出現(xiàn)什么問題?(已解決)

<!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"  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實例間傳遞數(shù)據(jù)問題, 怎么解除數(shù)據(jù)綁定?), 發(fā)現(xiàn)根本不是這原因于是更改了問題題目!
傳對象時不能指向同一個對象的引用, 否則在另外一個組建中修改這個對象原組建的view也會因為vue實例中model的修改而發(fā)生變化, 目前我的解決方法是"克隆一個新對象"即在內(nèi)存中新生成一個全新對象, 并把引用傳給要通信的組件,JSON.parse(JSON.stringify(anObject)).即首先把對象轉(zhuǎn)化成JSON字符串再把字符串轉(zhuǎn)化成對象, 就相當(dāng)于克隆了一個對象anObject. 這樣這個問題就能得到解決!

事情的起因是想寫了個mvvm模擬model更新DOM也同步更新的Demo, 碰到一個不可描述的問題冥想許久不得要領(lǐng),于是來提問!
首先,發(fā)現(xiàn)不可以發(fā)動圖,而這個問題作為新手表述不會全面,所以請復(fù)制代碼放入html文件中打開網(wǎng)頁, 然后在add表單中添加2個todo事件, 發(fā)現(xiàn)兩個實例之間的數(shù)據(jù)是動態(tài)綁定, 這不是我想要的.
其實有更好的寫法, 但是我認(rèn)為這個問題的解決會加深對vue框架的理解,所以:


我嘗試過把vmAdd中submit方法中的初始化name和description用setTimeout包裹沒用, 用非父子組件間的數(shù)據(jù)傳遞(空實例, 代碼注釋了沒有刪除)也沒用, 有哪位有耐心的大神打開代碼幫看看要怎么解決, 雖然有其他方法比如只用一個vm實例可以解決, 或者不要在vmAdd中的表單上用v-model. 但是, 目前這種情況有沒有解決的方法, 話說每個vue實例都是一個組件, vue api不應(yīng)該一直跟蹤數(shù)據(jù)的? 求解決, 謝謝

clipboard.png

clipboard.png

回答
編輯回答
怣人

先附一段解決后的完整代碼

<!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"  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 } // 復(fù)制值
          console.log(todo_new)
          this.todos.push(todo_new)
        },

這里其實是一個 js 基礎(chǔ)問題,關(guān)于對象的引用,其實是引用的對象的一個指針。例如下面

var obj = { name: 'lw' }
var obj_new = obj
// 這里 obj 和 obj_new 他們都保存的是指向同一個對象的一個指針,而不是對象的值
// 例如下面這樣
obj_new.name = 'xm'
// 你會發(fā)現(xiàn) obj.name 的值也會變?yōu)?xm 而不是原來的 lw  

然后你的問題就明了了,你傳遞給 vm 的對象,把 vmAdd 對值的跟蹤處理(get,set)也一起傳入。這樣的話,其實你傳入的這個值和 vmAdd 中的 data 指向的對象是同一個。
所以解決方法就是把這個對象深度復(fù)制一下,然后傳入對象的值而不是傳入對象的一個引用。

2018年5月26日 02:40
編輯回答
故人嘆

解決方法:

methods: {
    submit: function () {
    // 重點在這里  為什么自己去拼數(shù)據(jù)? 因為你一開始傳過去的是一個對象,傳遞的是引用,就是你后面再多次ADD過去的都是一個對象,東西都是一樣的。
    // 再然后你馬上又清空了,導(dǎo)致沒有產(chǎn)生預(yù)期的效果,
      vm.create({ name: this.name, description: this.description });
      this.name = '';
      this.description = '';
    }
  }

題外話:

  1. 這個明明用一個vue示例就能做的非常簡潔了,為什么還寫那么多個?
  2. js保存復(fù)雜類型的方法需要注意。
  3. 這個東西并不能加深你對vue的理解,只能說明你的js基礎(chǔ)水平不行。
2018年4月26日 09:59