---
# System prepended metadata

title: Vue 3 composition API

---

[TOC]

# Vue 3 composition API


## Vue2 vs Vue3
### 一、以 Vue source code 為切入點 談兩個版本的差異
:::danger
此區段提到的內容博大精深，喜歡燒腦的勇士歡迎至vue mastery學習或是google其他高手寫的文章，因為資源很多，就不附上特定連結惹，這邊先預祝大家越挫越勇 :fire:
:::
>Evan You 本人表示：
>> (1) ==**因應瀏覽器開始支援最新JS語法(ES6)**==：[color=#907bf7]
>> ex vue2用defineProperty，vue3用proxy來撰寫reative objects( = IE掰餔) 
>> (2) ==**在渲染DOM的效能上仍有很多改善空間**==
>> vue3在渲染虛擬DOM節點的function上進行了重構，讓佔用CPU的效能不到vue2的十分之一 
>> (3) ==**跟typescript真的太不合**==
>> vue2的寫法完全沒有考慮要兼容ts檔案，但typescript已經大流行，所以必須跟進
>> (4) ==**打包後體積超大**==
>>ex vue2會直接把透過npm安裝的所有dependencies打包，導致沒用到的也會一起打包進去，改善後，vue3打包體積小於vue2的一半



### 二、以 使用Vue的開發者為切入點 談vue3提供的新設計模式
:::    info
即使將專案建置在vue3底下，我們仍然可以使用熟悉的Vue2 API撰寫程式碼，只是透過Vue3新增的API，讓開發者可以根據情境選擇不同的設計模式，以下會先介紹Vue3新增了哪些API，再介紹Vue3 API帶來的新設計模式-composition
:::
#### 1. Vue3 新增的API
```javascript
//跟reative有關的api
import {ref, reactive, computed ,watchEffect,watch} from "vue";
```
```javascript
//跟life cycle有關的api
import {
  onMounted,
  onBeforeMount,
  onBeforeUpdate,
  onUpdated,
  onUnmounted,
  onBeforeUnmount,
} from "vue";
```

>以上新增的API都需要在 ==**vue3提供的全新生命週期setup(等於原先的beforeCreate和created)內使用**== ，在setup以外的地方，都還是使用原來的vue2 API[color=#907bf7]

> **computed和watch** 是原本vue2就有的用法，只是在vue3可以透過引入的方式，在setup內使用[color=#907bf7]

> **ref和reactive** 是vue3在setup內創建reactive object的api[color=#907bf7]

> **watchEffect** 和watch一樣具有監聽效果，不過watchEffect可以監聽整個funciton，只要function內任一個reactive object更動，watchEffect就會自己重跑一次！[color=#907bf7]

>生命週期的部分 **多了setup**(等於beforeCreate和created)， **如果要在setup內調用生命週期，要記得加上'on'** [color=#907bf7]


#### 2. vue3 新API帶來了什麼樣的設計模式？ 如何善用vue3 新API讓程式碼更好維護？
>上面介紹新API時有提到，==**新API都必須放在setup裡面使用**==，也就是不同於Vue2主打 **「拆成data/methods/computed/watch等區段」** 的設計模式，Vue3讓我們可以 **「把所有的東西一次組好塞進setup」** 裡面，請見下面這張圖[color=#907bf7]

![vue3_Composition_api](https://i.imgur.com/89zcQaE.png)

>看到這張圖大家可能會有個疑惑[color=#907bf7]

:::    danger
修但幾勒！啊把一大坨程式碼塞進去setup裡面？？這個新設計模式也太可怕了ㄅ ？？！！
:::

```!
    我只能說，這根據情況及工程師喜好有所不同，不過有個絕對的好處是，當程式碼拖到像下圖這～麼～長～的時候，光是想到在code review和開發時，明明都是同一個功能，卻要上上下下跳來跳去的講/寫code，setup就有足夠的使用價值惹(拭淚)
```
![option_api](https://i.imgur.com/Rv0QFWB.png)

)

>上圖中，大家可以看到剛剛提到的vue的兩個設計模式名稱：「options」和「composition」[color=#907bf7]

>vue2希望開發者將元件切成適當的大小，然後「自行選用」需要用到的官方api， **「以官方api分區塊」**，方便程式碼閱讀[color=#907bf7]



>vue3希望開發者可以將同一功能的東西「組合」在一起， **「以功能分區塊」**，結合vue2的設計模式，達到 **方便共用及閱讀** 的效果
[color=#907bf7]

```!
*官方api = data / methods / computed / life cycle ...
```

:::success
了解vue3帶來的 **以功能分區塊的「組合」設計模式** 後，接下來告訴大家在什麼情境下使用這個設計模式可能會好於vue2的options api設計模式
:::

:::spoiler 1. 單一component過度肥胖時
```!
    基本上好的component切分是vue設計中很重要的一環，所以這種情況應該是幾乎不會發生才對，但還是會有遇到的時候，比如說，麻醉系統的remark表格(再度拭淚) 
```
![ORMS_remark](https://i.imgur.com/2jKTTIH.png)
```!
    大家可以看到它其實就是一個三列的表格，只以表格的渲染來看，它的確該被放在一個component裡面，但偏偏這三列各自有不同的複雜功能，因此造成remark元件非常非常非常的臃腫，為了提高易讀性，開發時在這個元件裡寫了很多註解，不過有了setup以後！就可以使用composition api，讓開發者可以非常清楚的看出：
```
###### ==「什麼是註記要用的」、「什麼是手術擺位要用的」、「什麼是兩者共用的」==
:::

:::spoiler 2. 有多個元件共用function、data時
```!
    因為可以在setup引入任意一個我們寫好的composition function，所以共用方便性及易讀性大大大大大大幅提升惹！！！
```
:::

vue2 to vue3
[https://v3.vuejs.org/guide/migration/introduction.html#overview]

## 實作 Vue3 Composition API

:::info
再來要透過一個簡單的小範例，讓大家知道composition api究竟怎麼用，順便比較與vue2寫法的差異！
:::

>這邊要提醒的是，此次的範例完全可以只用vue2的設計模式設計，但為了讓大家簡易入門，所以才使用了vue3 composition api，正常情況下，這個簡單範例應該是使用vue2設計模式，程式碼就夠好懂了喔～
[color=#907bf7]

>先來個作品預覽圖 希望各位等等都會有水噹噹的todolist可以用[color=#907bf7]

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

:::success
耶嘿！ 那麻煩大家幫我git clone專案下來！我們要來開始實作囉！！！！！！
:::

>git clone https://github.com/TingYuWong/vue2-to-vue3.git [color=#907bf7]
```javascript
// 專案剛下載下來時 要記得用 npm install 把 dependencies 都載下來
npm install
```
```javascript
// 用@vue/cli建立的專案 運行vue專案的指令
npm run serve
```
>成功run起來以後，vue會在terminal給網址，請ctrl+click點擊url，網頁就會咻地跳出來了喔[color=#907bf7]

### 第一步：製作composition function
:::info
以下教學為個人研究過後覺得最好維護的寫法，有興趣的人們可以自己去查詢其他種設計方法～
:::

>請大家在src路徑點右鍵，新增use資料夾[color=#907bf7]
>在use資料夾裡面新增todo.js
![path](https://i.imgur.com/BQKQDvt.png)

>點兩下todo.js，我們要在這裡製作composition function[color=#907bf7]

>首先import我們需要用到的api: **reactive 和 toRefs** [color=#907bf7]
```javascript
import { reactive, toRefs } from 'vue'
```

>再來命名我們要製作的function，慣例為依照 ==**use<功能名稱>**== 取名，所以我們要寫一個叫作 ==**useTodos**== 的function [color=#907bf7]
```javascript
// 等等要輸出使用 所以前面幫我多加個export default喔
export default function useTodos(){
    // 這兩行代表 我們會從瀏覽器的儲存空間抓資料 有抓到就用瀏覽器的 
    // 沒抓到就用預設的defaultData
    const defaultData = [{
              done: false,
              content: 'Write a blog post'
          }]
    const todosData = JSON.parse(localStorage.getItem('todos')) || defaultData
}
```
>使用reactive，把所有我們需要的東西都丟進去！[color=#907bf7]

>使用reactive時，其實就是在創建一個響應式object，因此reactive要這樣用: ==reactive **({** 我們要放的東西 **})**==[color=#907bf7]

```javascript
// 建立reative object 存到變數todolist裡
const todolist = reactive({
})
```

>首先讓我們把需要的 ==data== 放進去[color=#907bf7]

```javascript
const todolist = reactive({
    //data
    newTodo: '',
    todos: todosData,
})
```

>再來把需要的 ==function== 放進去[color=#907bf7]

>這邊要注意的是，以往在vue2我們都是用 ==this.<data/methods名稱>==，但在這邊因為我們自行創建了響應式物件，所以要變成 ==**<reactive obj variable name>** .<data/methods名稱>== [color=#907bf7]

>大家可以看到addTodo func裡面，我們是寫 ==**todolist**== .newTodo和 ==**todolist**== .saveTodo() [color=#907bf7]

```javascript
// 建立reative object 存到變數todolist裡
const todolist = reactive({
        //data
        newTodo: '',
        todos: todosData,
        //methods
        saveTodo: ()=>{
            localStorage.setItem('todos', JSON.stringify(todolist.todos))
        },
        addTodo: ()=>{
          if(todolist.newTodo){
              todolist.todos.push({ done: false, content: todolist.newTodo})
              todolist.newTodo = ''
              todolist.saveTodo()
          }
        },
        doneTodo: (todo)=>{
            todo.done = !todo.done
            todolist.saveTodo()
        },
        removeTodo: (index)=>{
            todolist.todos.splice(index, 1)
            todolist.saveTodo()
        },
})
```

>寫完reactive object以後，==**我們要用toRefs「解構」它**==，解構是什麼意思呢，大家可以先想成把一個大包裹裡面包的幾個小箱子，拿出來一個一個放在桌上，方便之後一個一個交貨給我們的元件[color=#907bf7]

>toRefs的寫法為： ==**return { ...toRefs(reative obj變數名稱) }**== [color=#907bf7]

```javascript
// todo.js的檔案結構如下：
// 一個function裡面包著以下三個變數 並return解構後的reacitve object
export default function useTodos(){
    const defaultData = ...
    const todosData = ...
    const todolist = reactive({...})
    
    return { ...toRefs(todolist) }
}
```

### 第二步：在元件中引入
>寫好composition funciton以後，我們就可以在任意一個component引入使用惹！[color=#907bf7]

>請大家幫我點進 ==**src/components/todolist.vue**==[color=#907bf7]

>可以看到script這個區塊的東西，其實我們剛剛在composition function裡面都寫過了，所以這邊我們先註解起來，然後放入剛剛寫好的composition function！[color=#907bf7]

```javascript
<script>

import useTodos from '../use/todo'

export default {
    name: 'todolist',
}

</script>
```

>import function以後，我們要把剛剛解構出來的小箱子放進setup裡面！[color=#907bf7]

```javascript
<script>

import useTodos from '../use/todo'

export default {
    name: 'todolist',
    setup(){
    
        // 把小箱子一個個放在桌上
        const { newTodo, todos, saveTodo, addTodo, removeTodo, doneTodo } = useTodos();
        
        // 把小箱子們交給元件
        return {
            newTodo,
            todos,
            saveTodo,
            addTodo,
            removeTodo,
            doneTodo
        }
    },
}


</script>
```
## 就是這樣！ 下課！

:::    success
登愣！！！！這樣就大功告成惹！！！！
:::

>點進去vue開發者工具，可以看到顯示的不再是data，而是setup！[color=#907bf7]

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

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


>小總結：[color=#907bf7]

>vue2 -> vue3
（1）源碼上的改善讓效能大幅提昇，同時宣佈不支援IE
（2）除了vue2的options設計模式，vue3多提供了composition設計模式，讓開發者自行選擇
[color=#907bf7]

>vue3 composition api best practice
（1）建立use資料夾，依照功能分出不同的js檔案，裡面各自撰寫該功能需要的data/function等，功能命名慣例為use<功能名稱>
（2）用reactive包入所有data/function等，並用toRefs解構後，輸出至要用的元件
（3）在元件內的setup返回解構後的物件，就完成惹！
[color=#907bf7]

:::   info
那今天的課程就到這邊結束了，謝謝大家的參與 :100: 
:::

- [資料來源一 Evan You談rewrite vue的原因](https://increment.com/frontend/making-vue-3/)
- [資料來源二 vue3 vs react hook](https://zhuanlan.zhihu.com/p/133819602)
- [資料來源三 vue3 composition api官方文件](https://v3.vuejs.org/guide/composition-api-introduction.html)
