---
# System prepended metadata

title: Composition API
tags: [vue]

---

---
tags: Vue 直播班 - 2022 冬季班
---

# Composition API

## 與Option API 差異

![](https://hackmd.io/_uploads/B1DEBxXv2.png)
(依功能來分區，但商業邏輯區塊分散)

### OptionAPI
功能依照程式邏輯來拆分
* 優點
    1.易學
* 缺點:商業、功能邏輯拆散易讀性差

![](https://hackmd.io/_uploads/Sy738e7Dh.png)
(顏色為商業邏輯區塊)

### CompositionAPI
整合於setup()內，可以自行依照商業或功能邏輯來區分
* 優點:
    1.高度彈性
    2.可引入外部涵式
    3.熟悉OptionAPI則好上手
* 缺點:需要具備基礎的OptionAPI能力

---

## Proxy (es6原生語法)
> Proxy 是 JavaScript 的內建物件，它允許你創建一個代理（proxy）對象，用於控制對目標物件的訪問和操作。通過使用 Proxy，你可以修改對目標物件的默認行為，並添加自定義的邏輯。



### Proxy資料
* target 是要代理的目標物件，定義的資料會存在這裡。
* handler 是一個處理器（handler）對象，用於定義代理的行為。
![](https://hackmd.io/_uploads/HJ4y9l7wn.png)
(proxy範例圖)

### 一些常見的 Proxy方法 （handler traps）：
* get(target, property, receiver): 當代理對象訪問目標物件的屬性時觸發，可以修改或攔截該屬性的訪問。
* set(target, property, value, receiver): 當代理對象設置目標物件的屬性值時觸發，可以修改或攔截該屬性的賦值操作。
* apply(target, thisArg, argumentsList): 當代理對象作為函式調用時觸發，可以修改或攔截函式的調用操作。
* construct(target, argumentsList, newTarget): 當代理對象作為構造函式（使用 new 關鍵字）創建新實例時觸發，可以修改或攔截該構造函式的行為。


(雙向綁定與邏輯示範)
```
const handler = {
  get(obj, property) {
    console.log(`get:`,obj, property,value);
    // 需回傳才會把值傳出去
    return obj[property]
  },
  set(obj, property, newVal) {
    console.log(`set:` ,obj, property, newVal);
    // 要更改屬性必須在此操作，該物件的屬性 = 新值
    obj[property] = newVal
    
    //會將設置修改的資料觸發渲染行為，進行所謂雙向綁定
    render(obj,obj[property])
    
    // 會跳錯，輸入 return true，告訴他操作完畢就不會跳錯
    return true 
  },
};
// 建立proxy物件，前者必定是物件!!後者為控制器
const newObj = new Proxy({a:1},handler)

newObj.a = 2;
// 因設置更改數值，觸發 set()
// set()中，obj = newObj  property = a  newval = 2
// 會將你寫的值 傳入，並觸發內部涵式更改數值

console.log(newObj.a)
// 因要取得數值，觸發 get()
// get()中，obj = newObj  property = a
// log 中的值 為get()內部的， 外層log會為 undefined ，因內部沒有return回傳出來


const render = (prop , data)=> {
    const target = document.querySelector('#app')
    target.innerHTML = `${prop}:${data}`
}

// 兩秒過後，就會看到資料被更改並渲染出來
setTimeout(()=>{
    newObj.name = '你好棒'
},2000)
```
---

## setup() 起手示

Vue本身就是一個大物件，內部具有許多的方法，因此可以利用解構方法，把相關方法取出使用 console.log(Vue) 可查看

optionAPI 版
![optionAPI](https://hackmd.io/_uploads/rJ4wLZQD3.png)
```
// 透過inport與解構式 取出相關方法 常見於 VueCli ,ESM
// import {} from 'vue' ;
const { creatrApp } = Vue;

// 1. const app = Vue.createApp({}) 寫法之一
// 2. const app = creatrApp({}) 寫法之一
const app = Vue.createApp({
    data(){
        return {
            num: 1,
        }
    },
    methods: {
        add() {(
            this. num++;
        );
    }
});

app.mount('#app') ;
```
CompositionAPI 版
* setup(){} 起手
* ref 為定義資料的方法之一
* ref 取值須於後面加入.value
* function add (){} 不會用到this
* return 需將變數和方法傳出去，才能呈現於畫面與執行
```

const { creatrApp, ref } = Vue;

const app = creatrApp({
    setup () {
        // ref 為定義資料的方法之一
        const num = ref(1) 
        
        // compositionAPI 不會使用到this
        function add (){
            //ref 取用方法須加入.value
            num.value++
        }
        
        // 需將變數和方法傳出去，才能呈現於畫面與執行
        return {
            num,add
        }
    }
});

app.mount('#app') ;
```
---
## ref 與 reactive
###  定義資料方法
兩者方法皆須要使用**const**，使用let，一旦被覆蓋掉就會失去效果，變成一般的變數

直接宣告的變數，並不具有雙向綁定的效果，如果想要具有雙向綁定的效果，Vue3的有兩種寫法分別為 **ref** 和 **reactive**
* **reactive**
    proxy物件，一定是物件!如果傳入純值則會出錯
    
reactive Code :
```
const { creatrApp, reactive ,ref  } = Vue;

const app = creatrApp({
    setup () {
    
        // 直接宣告的變數，並不具有雙向綁定的效果
        const person= {
            name:'王曉明'
        }
        
        // proxy物件
        const person2= reactive({
            name:'王曉明'
        })
        // 使用物件方法來改值
        person.name = '陳小美'

        // 需將變數和方法傳出去，才能呈現於畫面與執行
        return {
            person,person2
        }
    }
});

app.mount('#app') ;
```
* **ref**
    可以定義純值或物件，是RefImpl物件，具雙向綁定
    ![](https://hackmd.io/_uploads/H1cbPO4Pn.png)
    資料存於 value 中 ，因此需使用 .value like: 
    * num.value
    * numObj.value.name (深層結構可，也能替換整個物件)
ref Code :
```
const { creatrApp, reactive ,ref  } = Vue;

const app = creatrApp({
    setup () {
        // ref 可定義純值，並且不是proxy物件
        const num = ref(1) 
        num.value = 5
        
        // ref 物件取值方法
        const numObj = ref ({
            name:'劉德華'
        })
        numObj.value.name = '周杰倫'
        
        // ref 替換整個物件方法
        numObj.value = {
            name:'小小兵'
        }

        // 需將變數和方法傳出去，才能呈現於畫面與執行
        return {
           num,numObj
        }
    }
});

app.mount('#app') ;
```

### 如何選擇 ref 或 reactive


* reactive 特性:
    proxy，須為物件，handler監控控制器(當proxy內部值有變化時，就會觸發)
![](https://hackmd.io/_uploads/HJxFQjVv2.png)

```
const { creatrApp, reactive ,ref  } = Vue;

const app = creatrApp({
    setup () {
        // ref
        const person = ref({
            name:'王曉明'
        })
        // reactive
        const person2 = reactive({
            name:'王曉明'
        })
        return {
           person,person2
        }
    }
});

app.mount('#app') ;
```

* ref 特性:
    proxy，須為物件，handler監控控制器(當proxy內部值有變化時，就會觸發)

```
const { creatrApp, reactive ,ref  } = Vue;

const app = creatrApp({
    setup () {
        // ref
        const person = ref({
            name:'王曉明'
        })
        // reactive
        const person2 = reactive({
            name:'王曉明'
        })
        return {
           person,person2
        }
    }
});

app.mount('#app') ;
```