1. 程式人生 > 實用技巧 >Vue3.0中的reactive

Vue3.0中的reactive

Vue3.0中的reactive

  • reactive 是 Vue3 中提供的實現響應式資料的方法。
  • 在 Vue2 中響應式資料是通過 defineProperty 來實現的,
  • 在 Vue3 中響應式資料是通過 ES6 的 Proxy來實現的。
  • reactive 引數必須是物件 (json / arr)
  • 如果給 reactive 傳遞了其它物件
    • 預設情況下,修改物件無法實現介面的資料繫結更新。
    • 如果需要更新,需要進行重新賦值。(即不允許直接操作資料,需要放個新的資料來替代原資料)
reactive 使用基本型別引數

基本型別(數字、字串、布林值)在 reactive 中無法被建立成 proxy

物件,也就無法實現監聽。

<template>
<div>
  <p>{{msg}}</p>
  <button @click="c">button</button>
</div>
</template>

<script>
import { reactive } from 'vue'
export default {
  name: 'App',
  setup() {
    let msg = reactive(0)
    function c() {
      console.log(msg);
      msg ++;
    }
    return {
      msg,
      c
    };
  }
}
</script>

點選 button ,我們期望的結果是數字從 0 變成 1,然而實際上介面上的數字並沒有發生任何改變。

檢視控制檯,它的輸出是這樣的(我點了 3 次)

出現提示

value cannot be made reactive: 0

而輸出的值確實發生了變化,只不過這種變化並沒有反饋到介面上,也就是說並沒有實現雙向資料繫結。當然,如果是 ref 的話,就不存在這樣的問題。而如果要使用 reactive ,我們需要將引數從 基本型別 轉化為 物件。

<template>
<div>
  <p>{{msg.num}}</p>
  <button @click="c">button</button>
</div>
</template>

<script>
import { reactive } from 'vue'
export default {
  name: 'App',
  setup() {
    let msg = reactive({
      num: 0
    })
    function c() {
      console.log(msg);
      msg.num ++;
    }
    return {
      msg,
      c
    };
  }
}
</script>

將引數替換成了物件 {num: 0},此時,點選按鈕介面就會產生改變(我點了 3 次)。

在控制檯列印訊息

可以看到,msg 成功被建立成了 proxy 物件,他通過劫持物件的 getset 方法實現了物件的雙向資料繫結。

深層的、物件內部的變化也能被察覺到(注意下面程式碼中的 inner

<template>
<div>
  <p>{{msg.num.inner}}</p>
  <button @click="c">button</button>
</div>
</template>

<script>
import { reactive } from 'vue'
export default {
  name: 'App',
  setup() {
    let msg = reactive({
      num: {
        inner: 0
      }
    })
    function c() {
      console.log(msg);
      msg.num.inner ++;
    }
    return {
      msg,
      c
    };
  }
}
</script>

陣列變化也不在話下。

<template>
<div>
  <p>{{msg}}</p>
  <button @click="c">button</button>
</div>
</template>

<script>
import { reactive } from 'vue'
export default {
  name: 'App',
  setup() {
    let msg = reactive([1, 2, 3])
    function c() {
      console.log(msg);
      msg[0] += 1;
      msg[1] = 5;
    }
    return {
      msg,
      c
    };
  }
}
</script>

在-reactive-使用-date-引數在 reactive 使用 Date 引數

如果引數不是陣列、物件,而是稍微奇怪一點的資料型別,例如說 Date ,那麼麻煩又來了。

<template>
<div>
  <p>{{msg}}</p>
  <button @click="c">button</button>
</div>
</template>

<script>
import { reactive } from 'vue'
export default {
  name: 'App',
  setup() {
    let msg = reactive(new Date())
    function c() {
      console.log(msg);
      msg.setDate(msg.getDate() + 1);
      console.log(msg);
    }
    return {
      msg,
      c
    };
  }
}
</script>!

這裡我先列印了 msg 兩次,可以看到,點選一次 button ,msg 的資料是存在變化的,但介面並未發生變化,同時我們發現在控制檯裡,msg 並未被識別成 proxy

就算我們把 Date 放在物件裡,就像這樣

<template>
<div>
  <p>{{msg.date}}</p>
  <button @click="c">button</button>
</div>
</template>

<script>
import { reactive } from 'vue'
export default {
  name: 'App',
  setup() {
    let msg = reactive({
      date: new Date()
    });
    function c() {
      console.log(msg);
      msg.date.setDate(msg.date.getDate() + 1);
      console.log(msg);
    }
    return {
      msg,
      c
    };
  }
}
</script>

也仍然不起效果。

顯然,對於這種資料型別,我們需要做特殊處理。

這個特殊處理就是重新賦值(,而不是直接修改原來的值)。

<template>
<div>
  <p>{{msg.date}}</p>
  <button @click="c">button</button>
</div>
</template>

<script>
import { reactive } from 'vue'
export default {
  name: 'App',
  setup() {
    let msg = reactive({
      date: new Date()
    });
    function c() {
      console.log(msg);
      msg.date.setDate((msg.date.getDate() + 1));
      msg.date = new Date(msg.date);
      console.log(msg);
    }
    return {
      msg,
      c
    };
  }
}
</script>

這裡我採用了拷貝的方案重新賦值了 msg.date,介面成功發生了變化(日期 + 1)。