首頁 > web前端 > js教程 > vue中數組變化檢測問題

vue中數組變化檢測問題

php中世界最好的语言
發布: 2018-04-17 15:36:22
原創
1687 人瀏覽過

這次帶給大家vue中數組變化檢測問題,vue中數組變化檢測的注意事項有哪些,下面就是實戰案例,一起來看一下。

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>vue</title>
 <script src="https://unpkg.com/vue@2.3.3/dist/vue.js"></script>
 <style>
  li:hover {
   cursor: pointer;
  }
 </style>
</head>
<body>
 <p class="wrap">
  <ul>
   <li v-for="item,index in items" v-on:click="handle(index)">
    <span>{{item.name}}</span>
    <span>{{numbers[index]}}</span>
   </li>
  </ul>
 </p>
 <script>
  var vm = new Vue({
   el: ".wrap",
   data: {
    numbers: [],
    items: [
     {name: 'jjj'},
     {name: 'kkk'},
     {name: 'lll'},
    ]
   },
   methods: {
    handle: function (index) {
     // WHY: 更新数据,view层未渲染,但通过console这个数组可以发现数据确实更新了
      if (typeof(this.numbers[index]) === "undefined" ) {
       this.numbers[index] = 1;
      } else {
       this.numbers[index]++;
      }
    }
   }
  });
 </script>
</body>
</html>
登入後複製

這裡的實現目的很明確 --- 我希望在點擊li時先檢測是否存在,當然是不存在的,所以就將值設為1, 如果再次點擊,就讓數字累加。

但出現的問題是: 點擊之後數字並沒有在view層更新,而透過console列印發現資料確實更新了,只是view層沒有及時的偵測到, 而我一直以來的想法就是: 既然vue實現的時資料雙向綁定,那麼在model層發生了變化之後為什麼就沒有在view層更新呢? 

首先,我就考慮了這是不是數組的問題,於是,我測試了下面的例子:

例2

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>vue</title>
 <script src="https://unpkg.com/vue@2.3.3/dist/vue.js"></script>
 <style>
  li:hover {
   cursor: pointer;
  }
 </style>
</head>
<body>
 <p class="wrap">
  <ul>
   <li v-for="item,index in items" v-on:click="handle(index)">
    <span>{{item.name}}</span>
    <span>{{numbers[index]}}</span>
   </li>
  </ul>
 </p>
 <script>
  var vm = new Vue({
   el: ".wrap",
   data: {
    numbers: [],
    items: [
     {name: 'jjj'},
     {name: 'kkk'},
     {name: 'lll'},
    ]
   },
   methods: {
    handle: function (index) {
     // 不是数组,这里更新数据就可以直接在view层渲染
     this.items[index].name += " success";
    }
   }
  });
 </script>
</body>
</html>
登入後複製

這時,我再測試時就發現,這裡的model層發生了變化時,view層就能及時、有效的得到更新。

其中最重要的一句話是--- 如果物件是響應式的,確保屬性被創建後也是響應式的,同時觸發視圖更新,這個方法主要用於避開Vue無法偵測到屬性被加的限制。

那什麼情況下Vue是不能偵測到屬性被加入呢?  根據參考鏈接,我們在文件上看到了很好的說明 --- 深入響應式原則

首先,我們要了解Vue是如何實現資料的雙向綁定的! 

# 把一個普通 JavaScript 物件傳給 Vue 實例的 data 選項,Vue 將遍歷此物件所有的屬性,並使用 Object.defineProperty 把這些屬性全部轉為 getter/setter。 Object.defineProperty 是僅 ES5 支持,且無法 shim 的特性,這就是為什麼 Vue 不支援 IE8 以及更低版本瀏覽器的原因。

知識補充:

存取器屬性不包含資料值,他們包含一對getter函數和setter函數(這兩個函數不是必須的)。在讀取存取器屬性時,會呼叫getter函數,這個函數負責傳回有效的值;在寫入存取器屬性是,會呼叫setter函數並傳入新值,這個函數負責決定如何處理資料。

存取器屬性不能直接定義,必須用Object.defineProperty()來定義。

下面是一個例子:

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>vue</title>
</head>
<body>
 <script>
  var book={
    _year:2004,
    edition:1
  };
  Object.defineProperty(book,"year",{
    get:function(){
      return this._year;
    },
    set:function(newValue){
      if(newValue>2004){
        this._year=newValue;
        this.edition+=newValue-2004;
      }
    }
  });
  console.log(book.year); // 2004 在读取访问器属性时会调用get函数
  book.year=2005; // 在给访问器属性赋值时会调用set函数
  console.log(book.edition); // 2
 </script>
</body>
</html>
登入後複製

這個例子應該可以很好的理解訪問器屬性了。

所以,當物件下的存取器屬性值發生了改變之後(vue會將屬性都轉換為存取器屬性,之前提到了), 那麼就會呼叫set函數,這時vue就可以透過這個set函數來追蹤變化,呼叫相關函數來實作view視圖的更新。

每個元件實例都有對應的watcher 實例對象,它會在元件渲染的過程中把屬性記錄為依賴,之後當依賴項的setter 被呼叫時,會通知watcher 重新計算,從而致使它關聯的元件得以更新。也就是在渲染的過程中就會呼叫物件屬性的getter函數,然後getter函數通知wather物件將之宣告為依賴,依賴之後,如果物件屬性發生了變化,那麼就會呼叫settter函數來通知watcher,watcher就會在重新渲染元件,以此來完成更新。

OK!既然知道了原理,我們就可以進一步了解為什麼出現了之前數組的問題了!

變化偵測問題

# 收到現代JavaScript瀏覽器的限制,其實主要是 Object.observe() 方法支援的不好,Vue不能偵測到物件的新增或刪除。然而Vue在初始化實例時就對屬性執行了setter/getter轉換過程,所以屬性必須開始就在物件上,這樣才能讓Vue轉換它。

所以对于前面的例子就不能理解了 --- 数组中index都可以看做是属性,当我们添加属性并赋值时,Vue并不能检测到对象中属性的添加或者删除,但是其的确是添加或删除了,故我们可以通过console看到变化,所以就没有办法做到响应式; 而在第二个例子中,我们是在已有的属性的基础上进行修改的,这些属性是在最开始就被Vue初始化实例时执行了setter/getter的转化过程,所以说他们的修改是有效的,model的数据可以实时的在view层中得到相应。

补充知识: 什么是 Object.observe() ?

在介绍之前,不得不残忍的说,尽管这个方法可以在某些浏览器上运行,但事实是这个方法已经废弃!

概述: 此方法用于异步地监视一个对象的修改。当对象的属性被修改时,方法的回调函数会提供一个有序的修改流,然而这个接口已经从各大浏览器移除,可以使用通用的proxy 对象。      

方法:

Object.observe(obj, callback[, acceptList])
登入後複製

其中obj就是被监控的对象, callback是一个回调函数,其中的参数包括changes和acceptList,

changes一个数组,其中包含的每一个对象代表一个修改行为。每个修改行为的对象包含:

  • name: 被修改的属性名称。

  • object: 修改后该对象的值。

  • type: 表示对该对象做了何种类型的修改,可能的值为"add", "update", or "delete"。

  • oldValue: 对象修改前的值。该值只在"update"与"delete"有效。

acceptList在给定对象上给定回调中要监视的变化类型列表。如果省略, ["add", "update", "delete", "reconfigure", "setPrototype", "preventExtensions"] 将会被使用。

var obj = {
 foo: 0,
 bar: 1
};
Object.observe(obj, function(changes) {
 console.log(changes);
});
obj.baz = 2;
// [{name: 'baz', object: <obj>, type: 'add'}]
obj.foo = 'hello';
// [{name: 'foo', object: <obj>, type: 'update', oldValue: 0}]
delete obj.baz;
// [{name: 'baz', object: <obj>, type: 'delete', oldValue: 2}]
登入後複製

相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!

推荐阅读:

layer前端组件图片显示功能

web前端必看4本开发书籍

以上是vue中數組變化檢測問題的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
最新問題
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板