---
# System prepended metadata

title: 【Vue】響應式基礎 (Reactivity Fundamentals)
tags: [Vue]

---

###### tags: `Vue`

# 【Vue】響應式基礎 (Reactivity Fundamentals)
>Document: https://vuejs.org/guide/essentials/reactivity-fundamentals.html
## 響應式物件 (Reactive objects)
>能夠追蹤物件的存取權與狀態改變

![](https://i.imgur.com/rpHDyPO.png)


### SFC (Single-File Component) vs Codepen
[option api](https://codepen.io/intHuang/pen/rNqOzgr)
[composition api](https://codepen.io/intHuang/pen/PoyPKMz)
1. 創建
2. 使用
3. 方法宣告

### DOM 的更新時機
DOM 的更新是非同步的(不是你按下 Click Me 按鈕 DOM 就會馬上更新)，使用 nextTick() 來確保 DOM 已經完全更新

### 深層響應
預設都是深層響應  (有淺層響應 [shallowReactive](https://vuejs.org/api/reactivity-advanced.html#shallowreactive))

### 【響應式 proxy】  與 【原始對象】
```js=
const raw = {}
const proxy = reactive(raw)

// proxy is NOT equal to the original.
console.log(proxy === raw) // false
console.log(proxy === reactive(raw)) // true
```
-> 要更改 proxy 才會具有響應式

### reactive() 的限制
* 只能作用在 object type (string、number、boolean 都不能作用)
* 響應式系統是通過屬性追蹤
```js=
let state = reactive({ count: 0 })

state = reactive({ count: 1 })
```
:::warning
更改區域變數、解構物件或是函數參數的值都不會影響原始的響應式物件
:::

```js=
const state = reactive({ count: 0 })

let n = state.count
// does not affect original state
n++

let { count } = state
// does not affect original state
count++

callSomeFunction(state.count)
```
### 使用 ref()
```js=
import { ref } from 'vue'

const count = ref(0)
```
ref() 會回傳一個 reactive 的物件( ref )，包含一個 value 屬性

#### ref 物件在模板中的展開( unwrapping )
>你不用寫 `.value`
```js=
const count = ref(0);

console.log(count);
console.log(count.value);
```
* **只會作用在最上層的物件**

```js=
const object = { foo: ref(1) }
```
* 不會動:
```html=
{{ object.foo + 1 }}
```
#### ref 物件在響應式物件中的展開
* ref 物件如果被當成 reactive 物件的屬性，也會自己展開
```js=
const count = ref(0)
const state = reactive({
  count
})

console.log(state.count) // 0

state.count = 1
console.log(count.value) // 1
```
* ref 會取代 ref
```js=
const otherCount = ref(2)

state.count = otherCount
console.log(state.count) // 2
// original ref is now disconnected from state.count
console.log(count.value) // 1
```
* 只會作用在深層響應式物件

#### Arrays 與 [Collections](https://zh.wikipedia.org/zh-tw/%E9%9B%86%E5%90%88_(%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6)) 的 ref 物件展開
* array 與 collection 中不會自動展開(你要自己加上 `.value`)
```js=
const books = reactive([ref('Vue 3 Guide')])
// need .value here
console.log(books[0].value)

const map = reactive(new Map([['count', ref(0)]]))
// need .value here
console.log(map.get('count').value)
```
## 小結
1. option api 中的 data 在 composition api 中 要使用 reactive() 或 ref() 來宣告
2. reactive() 只能用在物件上
3. ref 物件除了陣列與集合，其他都不需要用 `.value`

## 其他

### option api 中的 debounce function
因為 Vue 的方法都是有狀態的 (stateful)，在 Vue 中如果同時調用 debounce function component 之間就可能會互相影響
>* Stateful: 一個方法的計算結果會影響到其他程式碼就稱為 Stateful，反之稱為 Unstateful
>* debounce 是一種控制事件觸發頻率的方法，當一個事件被觸發時，它會延遲一段時間，如果在這段時間內有其他事件被觸發，則這個事件會被忽略掉。透過 debounce，可以控制事件的觸發頻率，防止事件被過度觸發。


```js=
import { debounce } from 'lodash-es'

export default {
  methods: {
    // Debouncing with Lodash
    click: debounce(function () {
      // ... respond to click ...
    }, 500)
  }
}
```
解決方法: 把 debounce 的創建移到 created 週期
```js=
export default {
  created() {
    // each instance now has its own copy of debounced handler
    this.debouncedClick = _.debounce(this.click, 500)
  },
  unmounted() {
    // also a good idea to cancel the timer
    // when the component is removed
    this.debouncedClick.cancel()
  },
  methods: {
    click() {
      // ... respond to click ...
    }
  }
}
```

## 提問
1. 什麼時候要用 ref()，什麼時候用 reactive()
2. debounce ?

## 補充
[Reactivity](https://vuejs.org/api/reactivity-core.html#ref)

[Collection](https://zh.wikipedia.org/zh-tw/%E9%9B%86%E5%90%88_(%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6))

pinia cheat sheet
![](https://i.imgur.com/Qk3qos1.jpg)
