20_Vue如何監測陣列型別資料發生改變的?

2022-11-03 09:00:17

通過上一節,我們知道了vue檢測物件資料發生改變的原理

但是還有個api我們沒有講解,Vue.set();

這個API比較適合在理解了物件檢測的原理後進行講解

案例準備

html

<!-- 建立一個容器 -->
    <div class="app">
        <h1>姓名:{{student.name}}</h1>
        <h1>年齡:{{student.age}}</h1><br>
        <h2>朋友們</h2>
        <ul>
            <!-- 列表渲染 == friends -->
            <li v-for="(item,index) in friends">
                {{item.name}}-{{item.rage}}-{{item.mage}}
            </li>
        </ul>
    </div>

data設定項

<script>
    const vm = new Vue({
        el: '.app',
        data: {
            student: {
                name: 'wavesbright',
                age: 21,
            },
            friends: [ // 真實年齡,內心年齡
                {name: "Jack",rage: 32,mage: 40},
                {name: "Jony",rage: 24,mage: 45},
                {name: "Jone",rage: 28,mage: 50},
            ]
        },
        methods: {

        },
    });
</script>

頁面效果

需求

  • 我的資料都是寫在data當中的,通過vue的資料代理
  • 在頁面當中實現了響應式開發
  • 那麼現在有一個問題
  • 我想給,student 新增一個屬性,這個屬性是後來新增的,不是之前就新增的
  • 想讓 這個屬效能夠實現響應式,應該如何實現?

錯誤示範

  1. 直接在vm後面加個屬性不就完了?
  2. 修改 _data,然後給它新增一個屬性?
  • 上述兩個操作的問題,新增的屬性沒有進行資料代理
  • 無法完成響應式
  • 仔細看的話,在student這個物件當中,sex這個屬性是沒有get和set函數的

新增測試

我們在這裡新增一個渲染項,然後我們新增屬性試試,現在我data當中是沒有這個屬性的

並不能被vue所識別到

那麼我們後面想要自己新增屬性就沒有辦法完成響應式了嗎,誒,這就是我們接下來要引入的API

Vue.set()

  • 該API需要三個引數
    1. target: 目標
    2. key: 新增的屬性名
    3. val: 新增的屬性值

我現在算是明白了,小程式的 this.setData() 就是從這裡變來的

這裡其實應該是 .student的;因為操作的都是同一個物件的地址

set的侷限性

我現在要在data當中,新增一個屬性,這個屬性是leader == 校長

我們使用.set新增試試

  • 這裡報錯的意思就是,不允許直接在vue範例身上新增一個屬性
  • 解讀一下這段話哈
    • 我現在是在_data當中新增屬性
    • 但是這個新增的屬性,最後會掛載在vue範例身上
    • 所以,這裡使用.set是不允許的,不能直接新增
  • .set()不能在data當中直接新增屬性
  • 只能給data當中的某個物件(student)新增屬性

vm是不能作為target的,vm當中的data,也不能作為target

監測陣列

準備工作

  • 新建一個demo,重新設定了資料項data
  • 在data當中有兩個屬性,一個numbers的陣列,一個student的物件
  • 我們檢視vue範例物件,在檢視之前可以很明確的說,numbers 和 student 都掛載在了vue範例身上,並且,有專門為他倆服務的get和set

資料代理

現在我們點選去看看二者有什麼不同,或者說,vue當中對陣列和物件型別的資料是如何代理的

陣列和物件的不同

陣列

物件

區別,目前而言

  1. 陣列當中的資料,是沒有進行資料代理的,沒有專門為 元素 服務 的 get和set
  2. 而物件當中,每個屬性都是有get和set的,哪怕這個屬性是物件也有
  3. 也就是說,如果我們直接在vue當中直接修改numbers對應索引的值,vue是觀測不到的

直接修改numbers

我們寫一串DOM元素進行測試

<div class="app">
    <ul>
    	<li v-for="item in numbers">{{item}}</li>	
    </ul>
</div>

現在,我直接在控制檯中對陣列當中的元素進行修改

我們將最後一項 從5改為6

資料的確修改成功了,但是vue檢測不到,頁面無法響應

之前的錯誤解釋

現在,我們來回顧一下之前遇到的bug,我們新增一個persons物件陣列

從控制檯我們來觀察一下這個persons

  1. 這是一個物件陣列,這個陣列當中的每一項資料,都沒有被進行資料代理
  2. 但是因為每一項 資料 都是物件型別,所以 在物件型別當中 資料是進行了代理(get和set)的
  3. 這裡很重要,請仔細看

所以為什麼下面的修改不起作用,因為根本沒代理,沒有代理無法完成響應式資料

這個問題解決了順勢丟擲下一個問題,vue怎麼就知道陣列內部的屬性發生改變了呢,它是如何監測到的?

vue如何監測?

  • 藍色框當中的都是可以對陣列進行修改的,會改變原有陣列結構
  • 但是filter不會,他會返回一個新陣列,不修改原陣列
  • arr 呼叫了藍色框框當中的陣列API,自身才會發生改變
  • vue當中規定,你只有使用了上述的7個方法,我才承認你修改陣列了

那它咋知道我呼叫了上面的7個API呢?

包裝技術

原形

使用 Array這個原形物件身上的 push 舉例子

這個push,是給陣列呼叫的

我們在控制檯上來個陣列

這個push是哪裡來的?==> 其實是一層一層巢狀的,從原形物件身上來的

二者身上的push是相等的

vue

vue身上的陣列,使用的並不是 原形陣列Array身上的 API函數

如何測試?

很簡單,回到我們剛剛的案例

這下您能明白了嗎

流程

當你對一個被vue所管理的陣列進行了api的呼叫(push,shift,unshift.....)

你呼叫的這個API,就不是原型物件Array身上的API了;而是vue的api

在這個api當中,會做兩個步驟

  1. 呼叫原形身上的API(push.....)
  2. 重新解析模板,生成虛擬dom.......那一套流程

是這麼一回事嗎,我們看下官網是如何解答的

官網尋找答案

點我跳轉

  1. 點選 列表渲染
  2. 點選 陣列更新檢測

我們來看這句話

enmmmm,後面沒講了,基本其實到這裡就差不多了,後面的都需要在實際開發當中去慢慢琢磨了

尚矽谷yyds,黑馬也是

你們都是我的天使